Skip to content

Commit 82b91a0

Browse files
Add ordering to Index implementation
1 parent 9b67604 commit 82b91a0

File tree

11 files changed

+296
-175
lines changed

11 files changed

+296
-175
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/core/OrderBy.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,25 @@
2424
public class OrderBy {
2525
/** The direction of the ordering */
2626
public enum Direction {
27-
ASCENDING(1),
28-
DESCENDING(-1);
27+
ASCENDING(1, "asc"),
28+
DESCENDING(-1, "desc");
2929

3030
private final int comparisonModifier;
31+
private final String shorthand;
3132

32-
Direction(int comparisonModifier) {
33+
Direction(int comparisonModifier, String canonicalString) {
3334
this.comparisonModifier = comparisonModifier;
35+
this.shorthand = canonicalString;
3436
}
3537

3638
int getComparisonModifier() {
3739
return comparisonModifier;
3840
}
41+
42+
/** Returns "asc" for ascending or "desc" for descending. */
43+
public String canonicalString() {
44+
return shorthand;
45+
}
3946
}
4047

4148
public static OrderBy getInstance(Direction direction, FieldPath path) {

firebase-firestore/src/main/java/com/google/firebase/firestore/core/Target.java

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@
1616

1717
import static com.google.firebase.firestore.model.Values.max;
1818
import static com.google.firebase.firestore.model.Values.min;
19+
import static com.google.firebase.firestore.util.Assert.fail;
1920

2021
import androidx.annotation.Nullable;
21-
import com.google.firebase.firestore.core.OrderBy.Direction;
2222
import com.google.firebase.firestore.model.DocumentKey;
2323
import com.google.firebase.firestore.model.FieldIndex;
2424
import com.google.firebase.firestore.model.ResourcePath;
2525
import com.google.firebase.firestore.model.Values;
26-
import com.google.firestore.v1.ArrayValue;
2726
import com.google.firestore.v1.Value;
2827
import java.util.ArrayList;
28+
import java.util.Collections;
2929
import java.util.List;
3030

3131
/**
@@ -115,6 +115,27 @@ public boolean hasLimit() {
115115
return endAt;
116116
}
117117

118+
/** Returns the list of values that are used in ARRAY_CONTAINS and ARRAY_CONTAINS_ANY filter. */
119+
public List<Value> getArrayValues(FieldIndex fieldIndex) {
120+
for (FieldIndex.Segment segment : fieldIndex.getArraySegments()) {
121+
for (Filter filter : filters) {
122+
if (filter.getField().equals(segment.getFieldPath())) {
123+
FieldFilter fieldFilter = (FieldFilter) filter;
124+
switch (fieldFilter.getOperator()) {
125+
case ARRAY_CONTAINS_ANY:
126+
return fieldFilter.getValue().getArrayValue().getValuesList();
127+
case ARRAY_CONTAINS:
128+
return Collections.singletonList(fieldFilter.getValue());
129+
default:
130+
// Remaining filters cannot be used as array filters.
131+
}
132+
}
133+
}
134+
}
135+
136+
return Collections.emptyList();
137+
}
138+
118139
/**
119140
* Returns a lower bound of field values that can be used as a starting point to scan the index
120141
* defined by {@code fieldIndex}.
@@ -127,7 +148,7 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
127148
boolean inclusive = true;
128149

129150
// Go through all filters to find a value for the current field segment
130-
for (FieldIndex.Segment segment : fieldIndex) {
151+
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
131152
Value segmentValue = Values.NULL_VALUE;
132153
boolean segmentInclusive = true;
133154

@@ -142,26 +163,17 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
142163
case LESS_THAN_OR_EQUAL:
143164
filterValue = Values.getLowerBound(fieldFilter.getValue().getValueTypeCase());
144165
break;
145-
case NOT_EQUAL:
146-
filterValue = Values.NULL_VALUE;
147-
break;
148-
case NOT_IN:
149-
filterValue =
150-
Value.newBuilder()
151-
.setArrayValue(ArrayValue.newBuilder().addValues(Values.NULL_VALUE))
152-
.build();
153-
break;
154166
case EQUAL:
155167
case IN:
156-
case ARRAY_CONTAINS_ANY:
157-
case ARRAY_CONTAINS:
158168
case GREATER_THAN_OR_EQUAL:
159169
filterValue = fieldFilter.getValue();
160170
break;
161171
case GREATER_THAN:
162172
filterValue = fieldFilter.getValue();
163173
filterInclusive = false;
164174
break;
175+
default:
176+
// Remaining filters cannot be used as upper bounds.
165177
}
166178

167179
if (max(segmentValue, filterValue) == filterValue) {
@@ -206,7 +218,7 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
206218
List<Value> values = new ArrayList<>();
207219
boolean inclusive = true;
208220

209-
for (FieldIndex.Segment segment : fieldIndex) {
221+
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
210222
@Nullable Value segmentValue = null;
211223
boolean segmentInclusive = true;
212224

@@ -218,26 +230,22 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
218230
boolean filterInclusive = true;
219231

220232
switch (fieldFilter.getOperator()) {
221-
case NOT_IN:
222-
case NOT_EQUAL:
223-
// These filters cannot be used as an upper bound. Skip.
224-
break;
225233
case GREATER_THAN_OR_EQUAL:
226234
case GREATER_THAN:
227235
filterValue = Values.getUpperBound(fieldFilter.getValue().getValueTypeCase());
228236
filterInclusive = false;
229237
break;
230238
case EQUAL:
231239
case IN:
232-
case ARRAY_CONTAINS_ANY:
233-
case ARRAY_CONTAINS:
234240
case LESS_THAN_OR_EQUAL:
235241
filterValue = fieldFilter.getValue();
236242
break;
237243
case LESS_THAN:
238244
filterValue = fieldFilter.getValue();
239245
filterInclusive = false;
240246
break;
247+
default:
248+
// Remaining filters cannot be used as upper bounds.
241249
}
242250

243251
if (min(segmentValue, filterValue) == filterValue) {
@@ -283,6 +291,11 @@ public List<OrderBy> getOrderBy() {
283291
return this.orderBys;
284292
}
285293

294+
/** Returns the first order by (which always exists). */
295+
public OrderBy getFirstOrderBy() {
296+
return this.orderBys.get(0);
297+
}
298+
286299
/** Returns a canonical string representing this target. */
287300
public String getCanonicalId() {
288301
if (memoizedCannonicalId != null) {
@@ -307,7 +320,7 @@ public String getCanonicalId() {
307320
builder.append("|ob:");
308321
for (OrderBy orderBy : getOrderBy()) {
309322
builder.append(orderBy.getField().canonicalString());
310-
builder.append(orderBy.getDirection().equals(Direction.ASCENDING) ? "asc" : "desc");
323+
builder.append(orderBy.getDirection().canonicalString());
311324
}
312325

313326
// Add limit.

firebase-firestore/src/main/java/com/google/firebase/firestore/index/IndexEntry.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@
2121
// TODO(indexing)
2222
public class IndexEntry {
2323
private final int indexId;
24-
private final byte[] indexValue;
24+
private final byte[] arrayValue;
25+
private final byte[] directionalValue;
2526
private final String uid;
2627
private final String documentName;
2728

28-
public IndexEntry(int indexId, byte[] indexValue, String uid, String documentName) {
29+
public IndexEntry(
30+
int indexId, byte[] arrayValue, byte[] directionalValue, String uid, String documentName) {
2931
this.indexId = indexId;
30-
this.indexValue = indexValue;
32+
this.arrayValue = arrayValue;
33+
this.directionalValue = directionalValue;
3134
this.uid = uid;
3235
this.documentName = documentName;
3336
}
@@ -36,8 +39,12 @@ public int getIndexId() {
3639
return indexId;
3740
}
3841

39-
public byte[] getIndexValue() {
40-
return indexValue;
42+
public byte[] getArrayValue() {
43+
return arrayValue;
44+
}
45+
46+
public byte[] getDirectionalValue() {
47+
return directionalValue;
4148
}
4249

4350
public String getUid() {

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ void addIndexEntry(IndexEntry entry) {
115115
persistence.execute(
116116
"INSERT OR IGNORE INTO index_entries ("
117117
+ "index_id, "
118-
+ "index_value, "
118+
+ "array_value, "
119+
+ "directional_value, "
119120
+ "uid, "
120-
+ "document_name) VALUES(?, ?, ?, ?)",
121+
+ "document_name) VALUES(?, ?, ?, ?, ?)",
121122
entry.getIndexId(),
122-
entry.getIndexValue(),
123+
entry.getArrayValue(),
124+
entry.getDirectionalValue(),
123125
entry.getUid(),
124126
entry.getDocumentName());
125127
}
@@ -141,12 +143,18 @@ void removeIndexEntry(int indexId, String uid, String documentName) {
141143
@VisibleForTesting
142144
IndexEntry getIndexEntry(int indexId) {
143145
return persistence
144-
.query("SELECT index_value, uid, document_name FROM index_entries WHERE index_id = ?")
146+
.query(
147+
"SELECT array_value, directional_value, uid, document_name FROM index_entries WHERE index_id = ?")
145148
.binding(indexId)
146149
.firstValue(
147150
row ->
148151
row == null
149152
? null
150-
: new IndexEntry(indexId, row.getBlob(0), row.getString(1), row.getString(2)));
153+
: new IndexEntry(
154+
indexId,
155+
row.getBlob(0),
156+
row.getBlob(1),
157+
row.getString(2),
158+
row.getString(3)));
151159
}
152160
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,8 @@ public Index encodeFieldIndex(FieldIndex fieldIndex) {
297297
// queries against each collection separately.
298298
index.setQueryScope(Index.QueryScope.COLLECTION_GROUP);
299299

300-
for (FieldIndex.Segment segment : fieldIndex) {
300+
for (int i = 0; i < fieldIndex.segmentCount(); ++i) {
301+
FieldIndex.Segment segment = fieldIndex.getSegment(i);
301302
Index.IndexField.Builder indexField = Index.IndexField.newBuilder();
302303
indexField.setFieldPath(segment.getFieldPath().canonicalString());
303304
if (segment.getKind() == FieldIndex.Segment.Kind.CONTAINS) {

0 commit comments

Comments
 (0)