• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / github_pull_request_285284

21 Nov 2023 01:56PM UTC coverage: 91.664% (-0.03%) from 91.689%
github_pull_request_285284

Pull #7123

Evergreen

jedelbo
Merge branch 'master' into jf/mql
Pull Request #7123: PoC: Add MQL translation skeleton

92364 of 169228 branches covered (0.0%)

264 of 308 new or added lines in 5 files covered. (85.71%)

102 existing lines in 21 files now uncovered.

231504 of 252558 relevant lines covered (91.66%)

5988933.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/src/realm/parser/query_bson.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2023 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include <realm/util/bson/bson.hpp>
20
#include <realm/util/bson/indexed_map.hpp>
21
#include <realm/object-store/results.hpp>
22
#include "query_ast.hpp"
23

24
namespace realm {
25

26
using namespace query_parser;
27

28
namespace {
29

30
std::map<std::string, CompareType> equal_operators = {
31
    {"$eq", CompareType::EQUAL},
32
    {"$in", CompareType::IN},
33
    {"$ne", CompareType::NOT_EQUAL},
34
};
35

36
std::map<std::string, CompareType> relational_operators = {
37
    {"$gt", CompareType::GREATER},
38
    {"$gte", CompareType::GREATER_EQUAL},
39
    {"$lt", CompareType::LESS},
40
    {"$lte", CompareType::LESS_EQUAL},
41
};
42

43
enum class QueryLogicalOperators { $and, $not, $nor, $or };
44

45
std::map<std::string, QueryLogicalOperators> logical_operators = {
46
    {"$and", QueryLogicalOperators::$and},
47
    {"$or", QueryLogicalOperators::$or},
48
    {"$nor", QueryLogicalOperators::$nor},
49
    {"$not", QueryLogicalOperators::$not},
50
};
51

52
class BsonConstant : public ConstantNode {
53
public:
54
    BsonConstant(bson::Bson b)
55
        : value(b)
56
    {
36✔
57
    }
36✔
58

59
    std::unique_ptr<Subexpr> visit(ParserDriver*, DataType) override
60
    {
36✔
61
        switch (value.type()) {
36✔
NEW
62
            case bson::Bson::Type::Int32:
✔
NEW
63
                return std::make_unique<Value<int64_t>>(int32_t(value));
×
64
            case bson::Bson::Type::Int64:
24✔
65
                return std::make_unique<Value<int64_t>>(int64_t(value));
24✔
NEW
66
            case bson::Bson::Type::Bool:
✔
NEW
67
                return std::make_unique<Value<Bool>>(bool(value));
×
NEW
68
            case bson::Bson::Type::Double:
✔
NEW
69
                return std::make_unique<Value<double>>(double(value));
×
70
            case bson::Bson::Type::String:
10✔
71
                return std::make_unique<ConstantStringValue>(std::string(value));
10✔
NEW
72
            case bson::Bson::Type::Binary: {
✔
NEW
73
                auto data = static_cast<std::vector<char>>(value);
×
NEW
74
                return std::make_unique<ConstantBinaryValue>(BinaryData(data.data(), data.size()));
×
NEW
75
            }
×
NEW
76
            case bson::Bson::Type::Timestamp:
✔
NEW
77
            case bson::Bson::Type::Datetime:
✔
NEW
78
                return std::make_unique<Value<Timestamp>>(Timestamp(value));
×
NEW
79
            case bson::Bson::Type::ObjectId:
✔
NEW
80
                return std::make_unique<Value<ObjectId>>(ObjectId(value));
×
NEW
81
            case bson::Bson::Type::Decimal128:
✔
NEW
82
                return std::make_unique<Value<Decimal128>>(Decimal128(value));
×
NEW
83
            case bson::Bson::Type::Uuid:
✔
NEW
84
                return std::make_unique<Value<UUID>>(UUID(value));
×
NEW
85
            case bson::Bson::Type::Null:
✔
NEW
86
                return std::make_unique<Value<null>>(realm::null());
×
87
            default:
2✔
88
                throw query_parser::InvalidQueryError("Unsupported Bson Type");
2✔
NEW
89
        }
×
NEW
90
        return {};
×
NEW
91
    }
×
92

93
private:
94
    bson::Bson value;
95
};
96

97
static inline const char* find_chr(const char* p, char c)
98
{
36✔
99
    while (*p && *p != c) {
312✔
100
        ++p;
276✔
101
    }
276✔
102
    return p;
36✔
103
}
36✔
104

105
static std::vector<std::string> split(const char* path)
106
{
34✔
107
    std::vector<std::string> ret;
34✔
108
    do {
36✔
109
        auto p = find_chr(path, '.');
36✔
110
        ret.emplace_back(path, p);
36✔
111
        path = p;
36✔
112
    } while (*path++ == '.');
36✔
113
    return ret;
34✔
114
}
34✔
115

116
} // namespace
117

118
std::vector<QueryNode*> ParserDriver::get_query_nodes(const bson::BsonArray& bson_array)
119
{
4✔
120
    std::vector<QueryNode*> ret;
4✔
121
    for (const auto& document : bson_array) {
8✔
122
        ret.emplace_back(get_query_node(static_cast<bson::BsonDocument>(document)));
8✔
123
    }
8✔
124
    return ret;
4✔
125
}
4✔
126

127
void ParserDriver::parse(const bson::BsonDocument& document)
128
{
32✔
129
    result = get_query_node(document);
32✔
130
}
32✔
131

132
QueryNode* ParserDriver::get_query_node(const bson::BsonDocument& document)
133
{
40✔
134
    QueryNode* ret = nullptr;
40✔
135
    for (const auto& [key, value] : document) {
40✔
136
        QueryNode* node = nullptr;
40✔
137
        // top level document will contain either keys to compare values against,
20✔
138
        // or logical operators like $and or $or that will contain an array of query ops
20✔
139
        if (logical_operators.count(key)) {
40✔
140
            switch (logical_operators[key]) {
6✔
141
                case QueryLogicalOperators::$and: {
6✔
142
                    if (value.type() != bson::Bson::Type::Array)
6✔
143
                        throw InvalidQueryError("Array expected");
2✔
144
                    node = m_parse_nodes.create<AndNode>(get_query_nodes(static_cast<bson::BsonArray>(value)));
4✔
145
                    break;
4✔
146
                }
4✔
147
                case QueryLogicalOperators::$not:
2✔
NEW
148
                    if (value.type() != bson::Bson::Type::Document)
×
NEW
149
                        throw InvalidQueryError("Array expected");
×
NEW
150
                    node = m_parse_nodes.create<NotNode>(get_query_node(static_cast<bson::BsonDocument>(value)));
×
NEW
151
                    break;
×
NEW
152
                case QueryLogicalOperators::$nor:
✔
NEW
153
                    throw InvalidQueryError("$nor not supported");
×
NEW
154
                    break;
×
NEW
155
                case QueryLogicalOperators::$or: {
✔
NEW
156
                    if (value.type() != bson::Bson::Type::Array)
×
NEW
157
                        throw InvalidQueryError("Array expected");
×
NEW
158
                    node = m_parse_nodes.create<OrNode>(get_query_nodes(static_cast<bson::BsonArray>(value)));
×
NEW
159
                    break;
×
NEW
160
                }
×
161
            }
34✔
162
        }
34✔
163
        else {
34✔
164
            auto path = m_parse_nodes.create<PathNode>();
34✔
165
            auto path_elements = split(key.c_str());
34✔
166
            for (auto& elem : path_elements) {
36✔
167
                path->add_element(elem);
36✔
168
            }
36✔
169
            auto prop = m_parse_nodes.create<PropertyNode>(path);
34✔
170
            // if the value type is a document, we expect that it is a
17✔
171
            // "query document". if it's not, we assume they are doing a value comparison
17✔
172
            if (value.type() == bson::Bson::Type::Document) {
34✔
173
                const auto& doc = static_cast<bson::BsonDocument>(value);
22✔
174
                auto [key, val] = doc[0];
22✔
175
                ValueNode* right;
22✔
176
                if (val.type() == bson::Bson::Type::Array) {
22✔
177
                    auto list = m_parse_nodes.create<ListNode>();
2✔
178
                    for (const auto& document : static_cast<bson::BsonArray>(val)) {
4✔
179
                        list->add_element(m_parse_nodes.create<BsonConstant>(document));
4✔
180
                    }
4✔
181
                    right = list;
2✔
182
                }
2✔
183
                else {
20✔
184
                    right = m_parse_nodes.create<BsonConstant>(val);
20✔
185
                }
20✔
186
                if (equal_operators.count(key)) {
22✔
187
                    node = m_parse_nodes.create<EqualityNode>(prop, equal_operators[key], right);
8✔
188
                }
8✔
189
                else {
14✔
190
                    node = m_parse_nodes.create<RelationalNode>(prop, relational_operators[key], right);
14✔
191
                }
14✔
192
            }
22✔
193
            else {
12✔
194
                node = m_parse_nodes.create<EqualityNode>(prop, CompareType::EQUAL,
12✔
195
                                                          m_parse_nodes.create<BsonConstant>(value));
12✔
196
            }
12✔
197
        }
34✔
198
        if (ret) {
39✔
NEW
199
            ret = m_parse_nodes.create<AndNode>(ret, node);
×
NEW
200
        }
×
201
        else {
38✔
202
            ret = node;
38✔
203
        }
38✔
204
    }
38✔
205
    return ret;
39✔
206
}
40✔
207

208
Query Table::query(const bson::BsonDocument& document) const
209
{
32✔
210
    NoArguments args;
32✔
211
    ParserDriver driver(m_own_ref, args, {});
32✔
212
    driver.parse(document);
32✔
213
    driver.result->canonicalize();
32✔
214
    return driver.result->visit(&driver);
32✔
215
}
32✔
216

217
Results Results::find(const bson::BsonDocument& document) const
218
{
32✔
219
    return filter(m_table->query(document));
32✔
220
}
32✔
221

222
Results Results::find(const std::string& document) const
223
{
36✔
224
    const char* p = document.c_str();
36✔
225
    while (isspace(*p))
374✔
226
        ++p;
338✔
227
    if (*p == '{') {
36✔
228
        // This seems to be MQL
17✔
229
        return find(static_cast<bson::BsonDocument>(bson::parse(document)));
34✔
230
    }
34✔
231
    else {
2✔
232
        // Try good old RQL
1✔
233
        return filter(m_table->query(document));
2✔
234
    }
2✔
235
}
36✔
236

237
} // namespace realm
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc