use of com.google.firebase.firestore.model.FieldIndex in project firebase-android-sdk by firebase.
the class SQLiteIndexManager method memoizeIndex.
/**
* Stores the index in the memoized indexes table and updates {@link #nextIndexToUpdate}, {@link
* #memoizedMaxIndexId} and {@link #memoizedMaxSequenceNumber}.
*/
private void memoizeIndex(FieldIndex fieldIndex) {
Map<Integer, FieldIndex> existingIndexes = memoizedIndexes.get(fieldIndex.getCollectionGroup());
if (existingIndexes == null) {
existingIndexes = new HashMap<>();
memoizedIndexes.put(fieldIndex.getCollectionGroup(), existingIndexes);
}
FieldIndex existingIndex = existingIndexes.get(fieldIndex.getIndexId());
if (existingIndex != null) {
nextIndexToUpdate.remove(existingIndex);
}
existingIndexes.put(fieldIndex.getIndexId(), fieldIndex);
nextIndexToUpdate.add(fieldIndex);
memoizedMaxIndexId = Math.max(memoizedMaxIndexId, fieldIndex.getIndexId());
memoizedMaxSequenceNumber = Math.max(memoizedMaxSequenceNumber, fieldIndex.getIndexState().getSequenceNumber());
}
use of com.google.firebase.firestore.model.FieldIndex in project firebase-android-sdk by firebase.
the class SQLiteIndexManager method start.
@Override
public void start() {
Map<Integer, FieldIndex.IndexState> indexStates = new HashMap<>();
// Fetch all index states if persisted for the user. These states contain per user information
// on how up to date the index is.
db.query("SELECT index_id, sequence_number, read_time_seconds, read_time_nanos, document_key, " + "largest_batch_id FROM index_state WHERE uid = ?").binding(uid).forEach(row -> {
int indexId = row.getInt(0);
long sequenceNumber = row.getLong(1);
SnapshotVersion readTime = new SnapshotVersion(new Timestamp(row.getLong(2), row.getInt(3)));
DocumentKey documentKey = DocumentKey.fromPath(EncodedPath.decodeResourcePath(row.getString(4)));
int largestBatchId = row.getInt(5);
indexStates.put(indexId, FieldIndex.IndexState.create(sequenceNumber, readTime, documentKey, largestBatchId));
});
// Fetch all indices and combine with user's index state if available.
db.query("SELECT index_id, collection_group, index_proto FROM index_configuration").forEach(row -> {
try {
int indexId = row.getInt(0);
String collectionGroup = row.getString(1);
List<FieldIndex.Segment> segments = serializer.decodeFieldIndexSegments(Index.parseFrom(row.getBlob(2)));
// If we fetched an index state for the user above, combine it with this index.
// We use the default state if we don't have an index state (e.g. the index was
// created while a different user as logged in).
FieldIndex.IndexState indexState = indexStates.containsKey(indexId) ? indexStates.get(indexId) : FieldIndex.INITIAL_STATE;
FieldIndex fieldIndex = FieldIndex.create(indexId, collectionGroup, segments, indexState);
// Store the index and update `memoizedMaxIndexId` and `memoizedMaxSequenceNumber`.
memoizeIndex(fieldIndex);
} catch (InvalidProtocolBufferException e) {
throw fail("Failed to decode index: " + e);
}
});
started = true;
}
use of com.google.firebase.firestore.model.FieldIndex in project firebase-android-sdk by firebase.
the class SQLiteIndexManager method computeIndexEntries.
/**
* Creates the index entries for the given document.
*/
private SortedSet<IndexEntry> computeIndexEntries(Document document, FieldIndex fieldIndex) {
SortedSet<IndexEntry> result = new TreeSet<>();
@Nullable byte[] directionalValue = encodeDirectionalElements(fieldIndex, document);
if (directionalValue == null) {
return result;
}
@Nullable FieldIndex.Segment arraySegment = fieldIndex.getArraySegment();
if (arraySegment != null) {
Value value = document.getField(arraySegment.getFieldPath());
if (isArray(value)) {
for (Value arrayValue : value.getArrayValue().getValuesList()) {
result.add(IndexEntry.create(fieldIndex.getIndexId(), document.getKey(), encodeSingleElement(arrayValue), directionalValue));
}
}
} else {
result.add(IndexEntry.create(fieldIndex.getIndexId(), document.getKey(), new byte[] {}, directionalValue));
}
return result;
}
use of com.google.firebase.firestore.model.FieldIndex in project firebase-android-sdk by firebase.
the class Target method getLowerBound.
/**
* Returns a lower bound of field values that can be used as a starting point to scan the index
* defined by {@code fieldIndex}. Returns {@code null} if no lower bound exists.
*/
@Nullable
public Bound getLowerBound(FieldIndex fieldIndex) {
List<Value> values = new ArrayList<>();
boolean inclusive = true;
// For each segment, retrieve a lower bound if there is a suitable filter or startAt.
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
Value segmentValue = null;
boolean segmentInclusive = true;
// Process all filters to find a value for the current field segment
for (FieldFilter fieldFilter : getFieldFiltersForPath(segment.getFieldPath())) {
Value filterValue = null;
boolean filterInclusive = true;
switch(fieldFilter.getOperator()) {
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
filterValue = Values.getLowerBound(fieldFilter.getValue().getValueTypeCase());
break;
case EQUAL:
case IN:
case GREATER_THAN_OR_EQUAL:
filterValue = fieldFilter.getValue();
break;
case GREATER_THAN:
filterValue = fieldFilter.getValue();
filterInclusive = false;
break;
case NOT_EQUAL:
case NOT_IN:
filterValue = Values.MIN_VALUE;
break;
default:
}
if (max(segmentValue, filterValue) == filterValue) {
segmentValue = filterValue;
segmentInclusive = filterInclusive;
}
}
// if we can narrow the scope.
if (startAt != null) {
for (int i = 0; i < orderBys.size(); ++i) {
OrderBy orderBy = this.orderBys.get(i);
if (orderBy.getField().equals(segment.getFieldPath())) {
Value cursorValue = startAt.getPosition().get(i);
if (max(segmentValue, cursorValue) == cursorValue) {
segmentValue = cursorValue;
segmentInclusive = startAt.isInclusive();
}
break;
}
}
}
if (segmentValue == null) {
// No lower bound exists
return null;
}
values.add(segmentValue);
inclusive &= segmentInclusive;
}
return new Bound(values, inclusive);
}
use of com.google.firebase.firestore.model.FieldIndex in project firebase-android-sdk by firebase.
the class Target method getUpperBound.
/**
* Returns an upper bound of field values that can be used as an ending point when scanning the
* index defined by {@code fieldIndex}. Returns {@code null} if no upper bound exists.
*/
@Nullable
public Bound getUpperBound(FieldIndex fieldIndex) {
List<Value> values = new ArrayList<>();
boolean inclusive = true;
// For each segment, retrieve an upper bound if there is a suitable filter or endAt.
for (FieldIndex.Segment segment : fieldIndex.getDirectionalSegments()) {
@Nullable Value segmentValue = null;
boolean segmentInclusive = true;
// Process all filters to find a value for the current field segment
for (FieldFilter fieldFilter : getFieldFiltersForPath(segment.getFieldPath())) {
Value filterValue = null;
boolean filterInclusive = true;
switch(fieldFilter.getOperator()) {
case GREATER_THAN_OR_EQUAL:
case GREATER_THAN:
filterValue = Values.getUpperBound(fieldFilter.getValue().getValueTypeCase());
filterInclusive = false;
break;
case EQUAL:
case IN:
case LESS_THAN_OR_EQUAL:
filterValue = fieldFilter.getValue();
break;
case LESS_THAN:
filterValue = fieldFilter.getValue();
filterInclusive = false;
break;
case NOT_EQUAL:
case NOT_IN:
filterValue = Values.MAX_VALUE;
break;
default:
}
if (min(segmentValue, filterValue) == filterValue) {
segmentValue = filterValue;
segmentInclusive = filterInclusive;
}
}
// if we can narrow the scope.
if (endAt != null) {
for (int i = 0; i < orderBys.size(); ++i) {
OrderBy orderBy = this.orderBys.get(i);
if (orderBy.getField().equals(segment.getFieldPath())) {
Value cursorValue = endAt.getPosition().get(i);
if (min(segmentValue, cursorValue) == cursorValue) {
segmentValue = cursorValue;
segmentInclusive = endAt.isInclusive();
}
break;
}
}
}
if (segmentValue == null) {
// No upper bound exists
return null;
}
values.add(segmentValue);
inclusive &= segmentInclusive;
}
return new Bound(values, inclusive);
}
Aggregations