Skip to content

Commit d318021

Browse files
authored
Optimize Query::between for integers and timestamps (#7785)
* Add benchmark test for QueryRange<Timestamp>
1 parent b9b3d89 commit d318021

File tree

16 files changed

+188
-41
lines changed

16 files changed

+188
-41
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### Enhancements
44
* <New feature description> (PR [#????](https://github.com/realm/realm-core/pull/????))
5+
* Performance has been improved for range queries on integers and timestamps. Requires that you use the "BETWEEN" operation in MQL or the Query::between() method when you build the query. (PR [#7785](https://github.com/realm/realm-core/pull/7785))
56
* None.
67

78
### Fixed

src/realm/array_integer.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@ Mixed ArrayInteger::get_any(size_t ndx) const
2929
return Mixed(get(ndx));
3030
}
3131

32+
size_t ArrayInteger::find_first_in_range(int64_t from, int64_t to, size_t start, size_t end) const
33+
{
34+
if (m_ubound >= from && m_lbound <= to) {
35+
while (start < end) {
36+
auto val = get(start);
37+
if (from <= val && val <= to)
38+
return start;
39+
start++;
40+
}
41+
}
42+
return realm::not_found;
43+
}
44+
3245
Mixed ArrayIntNull::get_any(size_t ndx) const
3346
{
3447
return Mixed(get(ndx));
@@ -177,6 +190,18 @@ size_t ArrayIntNull::find_first(value_type value, size_t begin, size_t end) cons
177190
return find_first<Equal>(value, begin, end);
178191
}
179192

193+
size_t ArrayIntNull::find_first_in_range(int64_t from, int64_t to, size_t start, size_t end) const
194+
{
195+
if (m_ubound >= from && m_lbound <= to) {
196+
for (size_t i = start; i < end; i++) {
197+
auto val = get(i);
198+
if (val && *val >= from && *val <= to)
199+
return i;
200+
}
201+
}
202+
return realm::not_found;
203+
}
204+
180205
void ArrayIntNull::get_chunk(size_t ndx, value_type res[8]) const noexcept
181206
{
182207
// FIXME: Optimize this

src/realm/array_integer.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class ArrayInteger : public Array, public ArrayPayload {
7070
}
7171
template <class cond>
7272
bool find(value_type value, size_t start, size_t end, QueryStateBase* state) const;
73+
74+
size_t find_first_in_range(int64_t from, int64_t to, size_t start, size_t end) const;
7375
};
7476

7577
class ArrayIntNull : public Array, public ArrayPayload {
@@ -138,6 +140,7 @@ class ArrayIntNull : public Array, public ArrayPayload {
138140

139141

140142
size_t find_first(value_type value, size_t begin = 0, size_t end = npos) const;
143+
size_t find_first_in_range(int64_t from, int64_t to, size_t start, size_t end) const;
141144

142145
protected:
143146
void avoid_null_collision(int64_t value);

src/realm/array_timestamp.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,23 @@ size_t ArrayTimestamp::find_first<NotEqual>(Timestamp value, size_t begin, size_
236236
return not_found;
237237
}
238238

239+
size_t ArrayTimestamp::find_first_in_range(Timestamp from, Timestamp to, size_t start, size_t end) const
240+
{
241+
while (start < end) {
242+
start = m_seconds.find_first_in_range(from.get_seconds(), to.get_seconds(), start, end);
243+
if (start != realm::not_found) {
244+
util::Optional<int64_t> seconds = m_seconds.get(start);
245+
int32_t nanos = int32_t(m_nanoseconds.get(start));
246+
if ((from.get_seconds() < *seconds || from.get_nanoseconds() <= nanos) &&
247+
(to.get_seconds() > *seconds || nanos <= to.get_nanoseconds()))
248+
return start;
249+
start++;
250+
}
251+
}
252+
return not_found;
253+
}
254+
255+
239256
void ArrayTimestamp::verify() const
240257
{
241258
#ifdef REALM_DEBUG

src/realm/array_timestamp.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class ArrayTimestamp : public ArrayPayload, private Array {
106106
size_t find_first(Timestamp value, size_t begin, size_t end) const noexcept;
107107

108108
size_t find_first(Timestamp value, size_t begin, size_t end) const noexcept;
109+
size_t find_first_in_range(Timestamp from, Timestamp to, size_t start, size_t end) const;
109110

110111
void verify() const;
111112

src/realm/exec/importer.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,11 @@ void print_row(Table& table, size_t r)
118118
std::cout << "\n";
119119
}
120120

121+
} // anonymous namespace
121122

122-
bool is_null(const char* v)
123+
namespace realm {
124+
template <>
125+
bool is_null(const char* const& v)
123126
{
124127
if (v[0] == 0)
125128
return true;
@@ -132,9 +135,7 @@ bool is_null(const char* v)
132135

133136
return false;
134137
}
135-
136-
} // anonymous namespace
137-
138+
} // namespace realm
138139

139140
Importer::Importer()
140141
: Quiet(false)

src/realm/null.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ struct null {
117117
}
118118
};
119119

120+
template <typename T>
121+
inline bool is_null(const T&)
122+
{
123+
return false;
124+
}
125+
120126
template <class OS>
121127
OS& operator<<(OS& os, const null&)
122128
{

src/realm/parser/driver.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -640,10 +640,29 @@ Query BetweenNode::visit(ParserDriver* drv)
640640

641641
auto& min(limits->elements.at(0));
642642
auto& max(limits->elements.at(1));
643+
Query q(drv->m_base_table);
644+
645+
auto tmp = prop->visit(drv);
646+
const ObjPropertyBase* obj_prop = dynamic_cast<const ObjPropertyBase*>(tmp.get());
647+
if (obj_prop) {
648+
if (tmp->get_type() == type_Int) {
649+
auto min_val = min->visit(drv, type_Int);
650+
auto max_val = max->visit(drv, type_Int);
651+
q.between(obj_prop->column_key(), min_val->get_mixed().get_int(), max_val->get_mixed().get_int());
652+
return q;
653+
}
654+
if (tmp->get_type() == type_Timestamp) {
655+
auto min_val = min->visit(drv, type_Timestamp);
656+
auto max_val = max->visit(drv, type_Timestamp);
657+
q.between(obj_prop->column_key(), min_val->get_mixed().get_timestamp(),
658+
max_val->get_mixed().get_timestamp());
659+
return q;
660+
}
661+
}
662+
643663
RelationalNode cmp1(prop, CompareType::GREATER_EQUAL, min);
644664
RelationalNode cmp2(prop, CompareType::LESS_EQUAL, max);
645665

646-
Query q(drv->m_base_table);
647666
q.and_query(cmp1.visit(drv));
648667
q.and_query(cmp2.visit(drv));
649668

src/realm/query.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,12 @@ Query& Query::less(ColKey column_key, int64_t value)
624624
}
625625
Query& Query::between(ColKey column_key, int64_t from, int64_t to)
626626
{
627-
group();
628-
greater_equal(column_key, from);
629-
less_equal(column_key, to);
630-
end_group();
627+
if (column_key.is_nullable()) {
628+
add_node(std::unique_ptr<realm::ParentNode>(new BetweenNode<ArrayIntNull>(from, to, column_key)));
629+
}
630+
else {
631+
add_node(std::unique_ptr<realm::ParentNode>(new BetweenNode<ArrayInteger>(from, to, column_key)));
632+
}
631633
return *this;
632634
}
633635
Query& Query::equal(ColKey column_key, bool value)
@@ -737,6 +739,11 @@ Query& Query::less(ColKey column_key, Timestamp value)
737739
return add_condition<Less>(column_key, value);
738740
}
739741

742+
Query& Query::between(ColKey column_key, Timestamp from, Timestamp to)
743+
{
744+
add_node(std::unique_ptr<realm::ParentNode>(new BetweenNode<ArrayTimestamp>(from, to, column_key)));
745+
return *this;
746+
}
740747
// ------------- ObjectId
741748
Query& Query::greater(ColKey column_key, ObjectId value)
742749
{

src/realm/query.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class Query final {
151151
Query& greater_equal(ColKey column_key, Timestamp value);
152152
Query& less_equal(ColKey column_key, Timestamp value);
153153
Query& less(ColKey column_key, Timestamp value);
154+
Query& between(ColKey column_key, Timestamp from, Timestamp to);
154155

155156
// Conditions: ObjectId
156157
Query& equal(ColKey column_key, ObjectId value);

0 commit comments

Comments
 (0)