Skip to content

Commit 26e680d

Browse files
Add ordering to Index implementation
1 parent 9b67604 commit 26e680d

File tree

11 files changed

+295
-175
lines changed

11 files changed

+295
-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: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@
1818
import static com.google.firebase.firestore.model.Values.min;
1919

2020
import androidx.annotation.Nullable;
21-
import com.google.firebase.firestore.core.OrderBy.Direction;
2221
import com.google.firebase.firestore.model.DocumentKey;
2322
import com.google.firebase.firestore.model.FieldIndex;
2423
import com.google.firebase.firestore.model.ResourcePath;
2524
import com.google.firebase.firestore.model.Values;
26-
import com.google.firestore.v1.ArrayValue;
2725
import com.google.firestore.v1.Value;
2826
import java.util.ArrayList;
27+
import java.util.Collections;
2928
import java.util.List;
3029

3130
/**
@@ -115,6 +114,27 @@ public boolean hasLimit() {
115114
return endAt;
116115
}
117116

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

129149
// Go through all filters to find a value for the current field segment
130-
for (FieldIndex.Segment segment : fieldIndex) {
150+
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
131151
Value segmentValue = Values.NULL_VALUE;
132152
boolean segmentInclusive = true;
133153

@@ -142,26 +162,17 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
142162
case LESS_THAN_OR_EQUAL:
143163
filterValue = Values.getLowerBound(fieldFilter.getValue().getValueTypeCase());
144164
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;
154165
case EQUAL:
155166
case IN:
156-
case ARRAY_CONTAINS_ANY:
157-
case ARRAY_CONTAINS:
158167
case GREATER_THAN_OR_EQUAL:
159168
filterValue = fieldFilter.getValue();
160169
break;
161170
case GREATER_THAN:
162171
filterValue = fieldFilter.getValue();
163172
filterInclusive = false;
164173
break;
174+
default:
175+
// Remaining filters cannot be used as upper bounds.
165176
}
166177

167178
if (max(segmentValue, filterValue) == filterValue) {
@@ -206,7 +217,7 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
206217
List<Value> values = new ArrayList<>();
207218
boolean inclusive = true;
208219

209-
for (FieldIndex.Segment segment : fieldIndex) {
220+
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
210221
@Nullable Value segmentValue = null;
211222
boolean segmentInclusive = true;
212223

@@ -218,26 +229,22 @@ public Bound getLowerBound(FieldIndex fieldIndex) {
218229
boolean filterInclusive = true;
219230

220231
switch (fieldFilter.getOperator()) {
221-
case NOT_IN:
222-
case NOT_EQUAL:
223-
// These filters cannot be used as an upper bound. Skip.
224-
break;
225232
case GREATER_THAN_OR_EQUAL:
226233
case GREATER_THAN:
227234
filterValue = Values.getUpperBound(fieldFilter.getValue().getValueTypeCase());
228235
filterInclusive = false;
229236
break;
230237
case EQUAL:
231238
case IN:
232-
case ARRAY_CONTAINS_ANY:
233-
case ARRAY_CONTAINS:
234239
case LESS_THAN_OR_EQUAL:
235240
filterValue = fieldFilter.getValue();
236241
break;
237242
case LESS_THAN:
238243
filterValue = fieldFilter.getValue();
239244
filterInclusive = false;
240245
break;
246+
default:
247+
// Remaining filters cannot be used as upper bounds.
241248
}
242249

243250
if (min(segmentValue, filterValue) == filterValue) {
@@ -283,6 +290,11 @@ public List<OrderBy> getOrderBy() {
283290
return this.orderBys;
284291
}
285292

293+
/** Returns the first order by (which always exists). */
294+
public OrderBy getFirstOrderBy() {
295+
return this.orderBys.get(0);
296+
}
297+
286298
/** Returns a canonical string representing this target. */
287299
public String getCanonicalId() {
288300
if (memoizedCannonicalId != null) {
@@ -307,7 +319,7 @@ public String getCanonicalId() {
307319
builder.append("|ob:");
308320
for (OrderBy orderBy : getOrderBy()) {
309321
builder.append(orderBy.getField().canonicalString());
310-
builder.append(orderBy.getDirection().equals(Direction.ASCENDING) ? "asc" : "desc");
322+
builder.append(orderBy.getDirection().canonicalString());
311323
}
312324

313325
// 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)