Skip to content

Commit d542173

Browse files
committed
Auto Indexing heuristic experiment
1 parent c9990de commit d542173

File tree

5 files changed

+402
-0
lines changed

5 files changed

+402
-0
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.google.firebase.firestore;
2+
3+
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.testCollection;
4+
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.toDataMap;
5+
import static com.google.firebase.firestore.testutil.IntegrationTestUtil.waitFor;
6+
import static com.google.firebase.firestore.testutil.TestUtil.map;
7+
import static org.junit.Assert.assertEquals;
8+
9+
import androidx.test.ext.junit.runners.AndroidJUnit4;
10+
import com.google.android.gms.tasks.Task;
11+
import java.util.ArrayList;
12+
import java.util.Arrays;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.Map;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
19+
@RunWith(AndroidJUnit4.class)
20+
public class AutoIndexingExperiment {
21+
static List<Object> values =
22+
Arrays.asList(
23+
"Hello world",
24+
46239847,
25+
-1984092375,
26+
Arrays.asList(1, "foo", 3, 5, 8, 10, 11),
27+
Arrays.asList(1, "foo", 9, 5, 8),
28+
Double.NaN,
29+
map("nested", "random"));
30+
31+
private void createTestingDocument(
32+
CollectionReference colRef, int documentID, boolean isMatched, int numOfFields) {
33+
Map<String, Object> fields = map("match", isMatched);
34+
35+
// Randomly generate the rest of fields
36+
for (int i = 2; i <= numOfFields; i++) {
37+
int valueIndex = (int) (Math.random() * values.size()) % values.size();
38+
fields.put("field" + i, values.get(valueIndex));
39+
}
40+
41+
DocumentReference docRef = colRef.document(String.valueOf(documentID));
42+
System.out.println("Document id " + documentID + String.valueOf(isMatched));
43+
waitFor(docRef.set(fields));
44+
}
45+
46+
private void createTestingCollection(
47+
CollectionReference colRef,
48+
int totalSetCount,
49+
int portion /*0 - 10*/,
50+
int numOfFields /* 1 - 30*/) {
51+
int documentCounter = 0;
52+
for (int i = 1; i <= totalSetCount; i++) {
53+
// Generate a random order list of 0 ... 9
54+
ArrayList<Integer> indexes = new ArrayList<>();
55+
for (int index = 0; index < 10; index++) {
56+
indexes.add(index);
57+
}
58+
Collections.shuffle(indexes);
59+
60+
for (int match = 0; match < portion; match++) {
61+
int currentID = documentCounter + indexes.get(match);
62+
createTestingDocument(colRef, currentID, true, numOfFields);
63+
}
64+
for (int unmatch = portion; unmatch < 10; unmatch++) {
65+
int currentID = documentCounter + indexes.get(unmatch);
66+
createTestingDocument(colRef, currentID, false, numOfFields);
67+
}
68+
documentCounter += 10;
69+
}
70+
}
71+
72+
@Test
73+
public void getDocumentWhileOfflineWithSourceEqualToCache() {
74+
String basePath = "parent";
75+
CollectionReference colRef = testCollection(basePath);
76+
createTestingCollection(colRef, 10, 8, 11);
77+
78+
// Pull the whole collection data into cache
79+
Task<QuerySnapshot> onlineResult = colRef.get();
80+
waitFor(onlineResult);
81+
assertEquals(100, toDataMap(onlineResult.getResult()).size());
82+
83+
waitFor(colRef.getFirestore().disableNetwork());
84+
85+
Task<QuerySnapshot> qrySnapTask = colRef.whereEqualTo("match", true).get(Source.CACHE);
86+
waitFor(qrySnapTask);
87+
QuerySnapshot qrySnap = qrySnapTask.getResult();
88+
System.out.println("result is " + toDataMap(qrySnap));
89+
assertEquals(80, toDataMap(qrySnap).size());
90+
}
91+
92+
@Test
93+
public void testIndexedWithNonIndexedTime() {
94+
// Every set contains 10 documents
95+
final int numOfSet = 9;
96+
// could overflow. Currently it is safe when numOfSet set to 1000 and running on macbook M1
97+
long totalBeforeIndex = 0;
98+
long totalAfterIndex = 0;
99+
long totalDocumentCount = 0;
100+
long totalResultCount = 0;
101+
102+
// Temperate heuristic
103+
double without = 3.7;
104+
double with = 4.9;
105+
106+
for (int totalSetCount = 1; totalSetCount <= numOfSet; totalSetCount *= 10) {
107+
// portion stands for the percentage of documents matching query
108+
for (int portion = 0; portion <= 10; portion++) {
109+
for (int numOfFields = 1; numOfFields <= 31; numOfFields += 10) {
110+
String basePath = "documentCount" + totalSetCount;
111+
CollectionReference colRef = testCollection(basePath);
112+
createTestingCollection(colRef, totalSetCount, portion, numOfFields);
113+
114+
// Pull the whole collection data into cache
115+
Task<QuerySnapshot> onlineResult = colRef.get();
116+
waitFor(onlineResult);
117+
assertEquals(totalSetCount * 10, toDataMap(onlineResult.getResult()).size());
118+
119+
waitFor(colRef.getFirestore().disableNetwork());
120+
121+
// Achieve data through full collection scan
122+
Task<QuerySnapshot> qrySnapTask = colRef.whereEqualTo("match", true).get(Source.CACHE);
123+
waitFor(qrySnapTask);
124+
QuerySnapshot qrySnap = qrySnapTask.getResult();
125+
assertEquals(totalSetCount * portion, toDataMap(qrySnap).size());
126+
}
127+
}
128+
}
129+
}
130+
}

firebase-firestore/src/main/java/com/google/firebase/firestore/local/QueryEngine.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.google.firebase.firestore.local;
1616

17+
import static com.google.firebase.firestore.util.Assert.fail;
1718
import static com.google.firebase.firestore.util.Assert.hardAssert;
1819

1920
import com.google.firebase.database.collection.ImmutableSortedMap;
@@ -103,6 +104,22 @@ public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQuery(
103104
return result;
104105
}
105106

107+
public ImmutableSortedMap<DocumentKey, Document> getDocumentsMatchingQueryTest(
108+
Query query, boolean autoIndexing, QueryContext counter) {
109+
hardAssert(initialized, "initialize() not called");
110+
111+
ImmutableSortedMap<DocumentKey, Document> result;
112+
if (autoIndexing) {
113+
result = performQueryUsingIndex(query);
114+
if (result == null) {
115+
fail("createTargetIndices fails");
116+
}
117+
} else {
118+
result = executeFullCollectionScan(query, counter);
119+
}
120+
return result;
121+
}
122+
106123
private void CreateCacheIndices(Query query, QueryContext counter, int resultSize) {
107124
if (counter.fullScanCount > 2 * resultSize) {
108125
indexManager.createTargetIndices(query.toTarget());

firebase-firestore/src/main/java/com/google/firebase/firestore/model/TargetIndexMatcher.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ public FieldIndex BuildTargetIndex() {
213213
}
214214

215215
for (OrderBy orderBy : orderBys) {
216+
if (orderBy.getField().isKeyField()) {
217+
continue;
218+
}
219+
216220
if (uniqueFields.contains(orderBy.getField())) {
217221
continue;
218222
}

0 commit comments

Comments
 (0)