Skip to content

Commit 04ce260

Browse files
authored
Fix an issue where TargetIndexMatcher rejects valid queries. (#11353)
* Fix an issue where TargetIndexMatcher rejects valid queries. * Revert certtificates * fix comment * style
1 parent 23836ff commit 04ce260

File tree

3 files changed

+114
-12
lines changed

3 files changed

+114
-12
lines changed

Firestore/core/src/model/target_index_matcher.cc

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#include "Firestore/core/src/model/target_index_matcher.h"
1818

19+
#include <unordered_set>
20+
1921
#include "Firestore/core/src/util/hard_assert.h"
2022

2123
namespace firebase {
@@ -59,16 +61,16 @@ bool TargetIndexMatcher::ServedByIndex(const model::FieldIndex& index) {
5961
}
6062

6163
std::vector<Segment> segments = index.GetDirectionalSegments();
64+
std::unordered_set<std::string> equality_segments;
6265
size_t segment_index = 0;
6366
// Process all equalities first. Equalities can appear out of order.
6467
for (; segment_index < segments.size(); ++segment_index) {
6568
// We attempt to greedily match all segments to equality filters. If a
66-
// filter matches an index segment, we can mark the segment as used. Since
67-
// it is not possible to use the same field path in both an equality and
68-
// inequality/oderBy clause, we do not have to consider the possibility that
69-
// a matching equality segment should instead be used to map to an
70-
// inequality filter or orderBy clause.
71-
if (!HasMatchingEqualityFilter(segments[segment_index])) {
69+
// filter matches an index segment, we can mark the segment as used.
70+
if (HasMatchingEqualityFilter(segments[segment_index])) {
71+
equality_segments.emplace(
72+
segments[segment_index].field_path().CanonicalString());
73+
} else {
7274
// If we cannot find a matching filter, we need to verify whether the
7375
// remaining segments map to the target's inequality and its orderBy
7476
// clauses.
@@ -86,14 +88,18 @@ bool TargetIndexMatcher::ServedByIndex(const model::FieldIndex& index) {
8688
// `order_bys_` has at least one element.
8789
auto order_by_iter = order_bys_.begin();
8890

89-
// If there is an inequality filter, the next segment must match both the
90-
// filter and the first OrderBy clause.
9191
if (inequality_filter_.has_value()) {
92-
if (!MatchesFilter(inequality_filter_, segments[segment_index]) ||
93-
!MatchesOrderBy(*order_by_iter, segments[segment_index])) {
94-
return false;
92+
// If there is an inequality filter and the field was not in one of the
93+
// equality filters above, the next segment must match both the filter
94+
// and the first orderBy clause.
95+
if (equality_segments.count(
96+
inequality_filter_.value().field().CanonicalString()) == 0) {
97+
if (!MatchesFilter(inequality_filter_, segments[segment_index]) ||
98+
!MatchesOrderBy(*(order_by_iter++), segments[segment_index])) {
99+
return false;
100+
}
95101
}
96-
++order_by_iter;
102+
97103
++segment_index;
98104
}
99105

Firestore/core/test/unit/local/leveldb_index_manager_test.cc

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,58 @@ TEST_F(LevelDbIndexManagerTest, CursorCannotExpandResult) {
361361
});
362362
}
363363

364+
TEST_F(LevelDbIndexManagerTest, FiltersOnTheSameField) {
365+
persistence_->Run("TestFiltersOnTheSameField", [&]() {
366+
index_manager_->Start();
367+
368+
index_manager_->AddFieldIndex(
369+
MakeFieldIndex("coll", "a", model::Segment::kAscending));
370+
index_manager_->AddFieldIndex(
371+
MakeFieldIndex("coll", "a", model::Segment::kAscending, "b",
372+
model::Segment::kAscending));
373+
AddDoc("coll/val1", Map("a", 1, "b", 1));
374+
AddDoc("coll/val2", Map("a", 2, "b", 2));
375+
AddDoc("coll/val3", Map("a", 3, "b", 3));
376+
AddDoc("coll/val4", Map("a", 4, "b", 4));
377+
378+
{
379+
auto query = Query("coll")
380+
.AddingFilter(Filter("a", ">", 1))
381+
.AddingFilter(Filter("a", "==", 2));
382+
VerifyResults(query, {"coll/val2"});
383+
}
384+
{
385+
auto query = Query("coll")
386+
.AddingFilter(Filter("a", "<=", 1))
387+
.AddingFilter(Filter("a", "==", 2));
388+
VerifyResults(query, {});
389+
}
390+
{
391+
auto query = Query("coll")
392+
.AddingFilter(Filter("a", ">", 1))
393+
.AddingFilter(Filter("a", "==", 2))
394+
.AddingOrderBy(OrderBy("a"));
395+
VerifyResults(query, {"coll/val2"});
396+
}
397+
{
398+
auto query = Query("coll")
399+
.AddingFilter(Filter("a", ">", 1))
400+
.AddingFilter(Filter("a", "==", 2))
401+
.AddingOrderBy(OrderBy("a"))
402+
.AddingOrderBy(OrderBy("__name__", "desc"));
403+
VerifyResults(query, {"coll/val2"});
404+
}
405+
{
406+
auto query = Query("coll")
407+
.AddingFilter(Filter("a", ">", 1))
408+
.AddingFilter(Filter("a", "==", 3))
409+
.AddingOrderBy(OrderBy("a"))
410+
.AddingOrderBy(OrderBy("b", "desc"));
411+
VerifyResults(query, {"coll/val3"});
412+
}
413+
});
414+
}
415+
364416
TEST_F(LevelDbIndexManagerTest, EqualityFilter) {
365417
persistence_->Run("TestEqualityFilter", [&]() {
366418
index_manager_->Start();

Firestore/core/test/unit/model/target_index_matcher_test.cc

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <vector>
2121

2222
#include "Firestore/core/src/core/query.h"
23+
#include "Firestore/core/src/model/document_key.h"
2324
#include "Firestore/core/src/model/field_index.h"
2425
#include "Firestore/core/test/unit/testutil/testutil.h"
2526
#include "gtest/gtest.h"
@@ -513,6 +514,49 @@ TEST(TargetIndexMatcher, WithInAndOrderBySameField) {
513514
ValidateServesTarget(q, "a", Segment::Kind::kAscending);
514515
}
515516

517+
TEST(TargetIndexMatcher, WithEqualityAndInequalityOnTheSameField) {
518+
ValidateServesTarget(testutil::Query("collId")
519+
.AddingFilter(Filter("a", ">=", 5))
520+
.AddingFilter(Filter("a", "==", 0)),
521+
"a", Segment::Kind::kAscending);
522+
523+
ValidateServesTarget(testutil::Query("collId")
524+
.AddingFilter(Filter("a", ">=", 5))
525+
.AddingFilter(Filter("a", "==", 0))
526+
.AddingOrderBy(OrderBy("a")),
527+
"a", Segment::Kind::kAscending);
528+
529+
ValidateServesTarget(testutil::Query("collId")
530+
.AddingFilter(Filter("a", ">=", 5))
531+
.AddingFilter(Filter("a", "==", 0))
532+
.AddingOrderBy(OrderBy("a"))
533+
.AddingOrderBy(OrderBy("__name__")),
534+
"a", Segment::Kind::kAscending);
535+
536+
ValidateServesTarget(testutil::Query("collId")
537+
.AddingFilter(Filter("a", ">=", 5))
538+
.AddingFilter(Filter("a", "==", 0))
539+
.AddingOrderBy(OrderBy("a"))
540+
.AddingOrderBy(OrderBy("__name__", "desc")),
541+
"a", Segment::Kind::kAscending);
542+
543+
ValidateServesTarget(testutil::Query("collId")
544+
.AddingFilter(Filter("a", ">=", 5))
545+
.AddingFilter(Filter("a", "==", 0))
546+
.AddingOrderBy(OrderBy("a"))
547+
.AddingOrderBy(OrderBy("b"))
548+
.AddingOrderBy(OrderBy("__name__", "desc")),
549+
"a", Segment::Kind::kAscending, "b",
550+
Segment::Kind::kAscending);
551+
552+
ValidateServesTarget(testutil::Query("collId")
553+
.AddingFilter(Filter("a", ">=", 5))
554+
.AddingFilter(Filter("a", "==", 0))
555+
.AddingOrderBy(OrderBy("a", "desc"))
556+
.AddingOrderBy(OrderBy("__name__", "desc")),
557+
"a", Segment::Kind::kDescending);
558+
}
559+
516560
} // namespace
517561
} // namespace model
518562
} // namespace firestore

0 commit comments

Comments
 (0)