Skip to content

Commit b454a52

Browse files
committed
Run in-expansion as part of DNF computation and add tests.
1 parent 8c020fb commit b454a52

File tree

3 files changed

+169
-1
lines changed

3 files changed

+169
-1
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/util/LogicUtils.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,9 @@ public static List<Filter> getDnfTerms(CompositeFilter filter) {
337337
return Collections.emptyList();
338338
}
339339

340-
Filter result = computeDistributedNormalForm(filter);
340+
// The `in` operator is a syntactic sugar over a disjunction of equalities. We should first
341+
// replace such filters with equality filters before running the DNF transform.
342+
Filter result = computeDistributedNormalForm(computeInExpansion(filter));
341343

342344
hardAssert(
343345
isDisjunctiveNormalForm(result),

firebase-firestore/src/test/java/com/google/firebase/firestore/local/QueryEngineTestCase.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,4 +640,80 @@ public void orQueryWithArrayMembership() throws Exception {
640640
expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
641641
assertEquals(docSet(query2.comparator(), doc1, doc4, doc6), result2);
642642
}
643+
644+
@Test
645+
public void queryWithMultipleInsOnTheSameField() throws Exception {
646+
MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0));
647+
MutableDocument doc2 = doc("coll/2", 1, map("b", 1));
648+
MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2));
649+
MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3));
650+
MutableDocument doc5 = doc("coll/5", 1, map("a", 1));
651+
MutableDocument doc6 = doc("coll/6", 1, map("a", 2));
652+
addDocument(doc1, doc2, doc3, doc4, doc5, doc6);
653+
654+
// a IN [1,2,3] && a IN [0,1,4] should result in "a==1".
655+
Query query1 =
656+
query("coll")
657+
.filter(
658+
andFilters(
659+
filter("a", "in", Arrays.asList(1, 2, 3)),
660+
filter("a", "in", Arrays.asList(0, 1, 4))));
661+
DocumentSet result1 =
662+
expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
663+
assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1);
664+
665+
// a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set.
666+
Query query2 =
667+
query("coll")
668+
.filter(
669+
andFilters(
670+
filter("a", "in", Arrays.asList(2, 3)),
671+
filter("a", "in", Arrays.asList(0, 1, 4))));
672+
DocumentSet result2 =
673+
expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
674+
assertEquals(docSet(query2.comparator()), result2);
675+
676+
// a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
677+
Query query3 =
678+
query("coll")
679+
.filter(
680+
orFilters(
681+
filter("a", "in", Arrays.asList(0, 3)),
682+
filter("a", "in", Arrays.asList(0, 2))));
683+
684+
DocumentSet result3 =
685+
expectFullCollectionScan(() -> runQuery(query3, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
686+
assertEquals(docSet(query3.comparator(), doc3, doc6), result3);
687+
}
688+
689+
@Test
690+
public void queryWithMultipleInsOnDifferentFields() throws Exception {
691+
MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0));
692+
MutableDocument doc2 = doc("coll/2", 1, map("b", 1));
693+
MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2));
694+
MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3));
695+
MutableDocument doc5 = doc("coll/5", 1, map("a", 1));
696+
MutableDocument doc6 = doc("coll/6", 1, map("a", 2));
697+
addDocument(doc1, doc2, doc3, doc4, doc5, doc6);
698+
699+
Query query1 =
700+
query("coll")
701+
.filter(
702+
orFilters(
703+
filter("a", "in", Arrays.asList(2, 3)),
704+
filter("b", "in", Arrays.asList(0, 2))));
705+
DocumentSet result1 =
706+
expectFullCollectionScan(() -> runQuery(query1, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
707+
assertEquals(docSet(query1.comparator(), doc1, doc3, doc6), result1);
708+
709+
Query query2 =
710+
query("coll")
711+
.filter(
712+
andFilters(
713+
filter("a", "in", Arrays.asList(2, 3)),
714+
filter("b", "in", Arrays.asList(0, 2))));
715+
DocumentSet result2 =
716+
expectFullCollectionScan(() -> runQuery(query2, MISSING_LAST_LIMBO_FREE_SNAPSHOT));
717+
assertEquals(docSet(query2.comparator(), doc3), result2);
718+
}
643719
}

firebase-firestore/src/test/java/com/google/firebase/firestore/local/SQLiteQueryEngineTest.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,94 @@ public void orQueryWithArrayMembershipUsingIndexes() throws Exception {
277277
expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE));
278278
assertEquals(docSet(query2.comparator(), doc1, doc4, doc6), result2);
279279
}
280+
281+
@Test
282+
public void queryWithMultipleInsOnTheSameField() throws Exception {
283+
MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0));
284+
MutableDocument doc2 = doc("coll/2", 1, map("b", 1));
285+
MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2));
286+
MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3));
287+
MutableDocument doc5 = doc("coll/5", 1, map("a", 1));
288+
MutableDocument doc6 = doc("coll/6", 1, map("a", 2));
289+
addDocument(doc1, doc2, doc3, doc4, doc5, doc6);
290+
indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING));
291+
indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING));
292+
indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.ASCENDING));
293+
indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.DESCENDING));
294+
indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6));
295+
indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6));
296+
297+
// a IN [1,2,3] && a IN [0,1,4] should result in "a==1".
298+
Query query1 =
299+
query("coll")
300+
.filter(
301+
andFilters(
302+
filter("a", "in", Arrays.asList(1, 2, 3)),
303+
filter("a", "in", Arrays.asList(0, 1, 4))));
304+
DocumentSet result1 =
305+
expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE));
306+
assertEquals(docSet(query1.comparator(), doc1, doc4, doc5), result1);
307+
308+
// a IN [2,3] && a IN [0,1,4] is never true and so the result should be an empty set.
309+
Query query2 =
310+
query("coll")
311+
.filter(
312+
andFilters(
313+
filter("a", "in", Arrays.asList(2, 3)),
314+
filter("a", "in", Arrays.asList(0, 1, 4))));
315+
316+
DocumentSet result2 =
317+
expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE));
318+
assertEquals(docSet(query2.comparator()), result2);
319+
320+
// a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
321+
Query query3 =
322+
query("coll")
323+
.filter(
324+
orFilters(
325+
filter("a", "in", Arrays.asList(0, 3)),
326+
filter("a", "in", Arrays.asList(0, 2))));
327+
328+
DocumentSet result3 =
329+
expectOptimizedCollectionScan(() -> runQuery(query3, SnapshotVersion.NONE));
330+
assertEquals(docSet(query2.comparator(), doc3, doc6), result3);
331+
}
332+
333+
@Test
334+
public void queryWithMultipleInsOnDifferentFields() throws Exception {
335+
MutableDocument doc1 = doc("coll/1", 1, map("a", 1, "b", 0));
336+
MutableDocument doc2 = doc("coll/2", 1, map("b", 1));
337+
MutableDocument doc3 = doc("coll/3", 1, map("a", 3, "b", 2));
338+
MutableDocument doc4 = doc("coll/4", 1, map("a", 1, "b", 3));
339+
MutableDocument doc5 = doc("coll/5", 1, map("a", 1));
340+
MutableDocument doc6 = doc("coll/6", 1, map("a", 2));
341+
addDocument(doc1, doc2, doc3, doc4, doc5, doc6);
342+
indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.ASCENDING));
343+
indexManager.addFieldIndex(fieldIndex("coll", "a", Kind.DESCENDING));
344+
indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.ASCENDING));
345+
indexManager.addFieldIndex(fieldIndex("coll", "b", Kind.DESCENDING));
346+
indexManager.updateIndexEntries(docMap(doc1, doc2, doc3, doc4, doc5, doc6));
347+
indexManager.updateCollectionGroup("coll", IndexOffset.fromDocument(doc6));
348+
349+
Query query1 =
350+
query("coll")
351+
.filter(
352+
orFilters(
353+
filter("a", "in", Arrays.asList(2, 3)),
354+
filter("b", "in", Arrays.asList(0, 2))));
355+
DocumentSet result1 =
356+
expectOptimizedCollectionScan(() -> runQuery(query1, SnapshotVersion.NONE));
357+
assertEquals(docSet(query1.comparator(), doc1, doc3, doc6), result1);
358+
359+
Query query2 =
360+
query("coll")
361+
.filter(
362+
andFilters(
363+
filter("a", "in", Arrays.asList(2, 3)),
364+
filter("b", "in", Arrays.asList(0, 2))));
365+
366+
DocumentSet result2 =
367+
expectOptimizedCollectionScan(() -> runQuery(query2, SnapshotVersion.NONE));
368+
assertEquals(docSet(query2.comparator(), doc3), result2);
369+
}
280370
}

0 commit comments

Comments
 (0)