Search in sources :

Example 26 with ResourcePath

use of com.google.firebase.firestore.model.ResourcePath in project firebase-android-sdk by firebase.

the class SQLiteMutationQueue method performConsistencyCheck.

@Override
public void performConsistencyCheck() {
    if (!isEmpty()) {
        return;
    }
    // Verify that there are no entries in the document_mutations index if the queue is empty.
    List<ResourcePath> danglingMutationReferences = new ArrayList<>();
    db.query("SELECT path FROM document_mutations WHERE uid = ?").binding(uid).forEach(row -> {
        ResourcePath path = EncodedPath.decodeResourcePath(row.getString(0));
        danglingMutationReferences.add(path);
    });
    hardAssert(danglingMutationReferences.isEmpty(), "Document leak -- detected dangling mutation references when queue is empty. " + "Dangling keys: %s", danglingMutationReferences);
}
Also used : ResourcePath(com.google.firebase.firestore.model.ResourcePath) ArrayList(java.util.ArrayList)

Example 27 with ResourcePath

use of com.google.firebase.firestore.model.ResourcePath in project firebase-android-sdk by firebase.

the class SQLiteMutationQueue method getAllMutationBatchesAffectingQuery.

@Override
public List<MutationBatch> getAllMutationBatchesAffectingQuery(Query query) {
    hardAssert(!query.isCollectionGroupQuery(), "CollectionGroup queries should be handled in LocalDocumentsView");
    // Use the query path as a prefix for testing if a document matches the query.
    ResourcePath prefix = query.getPath();
    int immediateChildrenPathLength = prefix.length() + 1;
    // Scan the document_mutations table looking for documents whose path has a prefix that matches
    // the query path.
    // 
    // The most obvious way to do this would be with a LIKE query with a trailing wildcard (e.g.
    // path LIKE 'foo/%'). Unfortunately SQLite does not convert a trailing wildcard like that into
    // the equivalent range scan so a LIKE query ends up being a table scan. The query below is
    // equivalent but hits the index on both uid and path, so it's much faster.
    // TODO: Actually implement a single-collection query
    // 
    // This is actually executing an ancestor query, traversing the whole subtree below the
    // collection which can be horrifically inefficient for some structures. The right way to
    // solve this is to implement the full value index, but that's not in the cards in the near
    // future so this is the best we can do for the moment.
    String prefixPath = EncodedPath.encode(prefix);
    String prefixSuccessorPath = EncodedPath.prefixSuccessor(prefixPath);
    List<MutationBatch> result = new ArrayList<>();
    db.query("SELECT dm.batch_id, dm.path, SUBSTR(m.mutations, 1, ?) " + "FROM document_mutations dm, mutations m " + "WHERE dm.uid = ? " + "AND dm.path >= ? " + "AND dm.path < ? " + "AND dm.uid = m.uid " + "AND dm.batch_id = m.batch_id " + "ORDER BY dm.batch_id").binding(BLOB_MAX_INLINE_LENGTH, uid, prefixPath, prefixSuccessorPath).forEach(row -> {
        // Ensure unique batches only. This works because the batches come out in order so
        // we only need to ensure that the batchId of this row is different from the
        // preceding one.
        int batchId = row.getInt(0);
        int size = result.size();
        if (size > 0 && batchId == result.get(size - 1).getBatchId()) {
            return;
        }
        // The query is actually returning any path that starts with the query path prefix
        // which may include documents in subcollections. For example, a query on 'rooms'
        // will return rooms/abc/messages/xyx but we shouldn't match it. Fix this by
        // discarding rows with document keys more than one segment longer than the query
        // path.
        ResourcePath path = EncodedPath.decodeResourcePath(row.getString(1));
        if (path.length() != immediateChildrenPathLength) {
            return;
        }
        result.add(decodeInlineMutationBatch(batchId, row.getBlob(2)));
    });
    return result;
}
Also used : ResourcePath(com.google.firebase.firestore.model.ResourcePath) MutationBatch(com.google.firebase.firestore.model.mutation.MutationBatch) ArrayList(java.util.ArrayList) ByteString(com.google.protobuf.ByteString)

Example 28 with ResourcePath

use of com.google.firebase.firestore.model.ResourcePath in project firebase-android-sdk by firebase.

the class SQLiteDocumentOverlayCache method getOverlays.

@Override
public Map<DocumentKey, Overlay> getOverlays(SortedSet<DocumentKey> keys) {
    hardAssert(keys.comparator() == null, "getOverlays() requires natural order");
    Map<DocumentKey, Overlay> result = new HashMap<>();
    BackgroundQueue backgroundQueue = new BackgroundQueue();
    ResourcePath currentCollection = ResourcePath.EMPTY;
    List<Object> accumulatedDocumentIds = new ArrayList<>();
    for (DocumentKey key : keys) {
        if (!currentCollection.equals(key.getCollectionPath())) {
            processSingleCollection(result, backgroundQueue, currentCollection, accumulatedDocumentIds);
            currentCollection = key.getCollectionPath();
            accumulatedDocumentIds.clear();
        }
        accumulatedDocumentIds.add(key.getDocumentId());
    }
    processSingleCollection(result, backgroundQueue, currentCollection, accumulatedDocumentIds);
    backgroundQueue.drain();
    return result;
}
Also used : BackgroundQueue(com.google.firebase.firestore.util.BackgroundQueue) ResourcePath(com.google.firebase.firestore.model.ResourcePath) HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) ArrayList(java.util.ArrayList) Overlay(com.google.firebase.firestore.model.mutation.Overlay)

Example 29 with ResourcePath

use of com.google.firebase.firestore.model.ResourcePath in project firebase-android-sdk by firebase.

the class SQLiteSchema method ensurePathLength.

/**
 * Populates the remote_document's path_length column.
 */
private void ensurePathLength() {
    SQLitePersistence.Query documentsToMigrate = new SQLitePersistence.Query(db, "SELECT path FROM remote_documents WHERE path_length IS NULL LIMIT ?").binding(MIGRATION_BATCH_SIZE);
    SQLiteStatement insertKey = db.compileStatement("UPDATE remote_documents SET path_length = ? WHERE path = ?");
    boolean[] resultsRemaining = new boolean[1];
    do {
        resultsRemaining[0] = false;
        documentsToMigrate.forEach(row -> {
            resultsRemaining[0] = true;
            String encodedPath = row.getString(0);
            ResourcePath decodedPath = EncodedPath.decodeResourcePath(encodedPath);
            insertKey.clearBindings();
            insertKey.bindLong(1, decodedPath.length());
            insertKey.bindString(2, encodedPath);
            hardAssert(insertKey.executeUpdateDelete() != -1, "Failed to update document path");
        });
    } while (resultsRemaining[0]);
}
Also used : ResourcePath(com.google.firebase.firestore.model.ResourcePath) SQLiteStatement(android.database.sqlite.SQLiteStatement)

Example 30 with ResourcePath

use of com.google.firebase.firestore.model.ResourcePath in project firebase-android-sdk by firebase.

the class SQLiteSchema method createV8CollectionParentsIndex.

private void createV8CollectionParentsIndex() {
    ifTablesDontExist(new String[] { "collection_parents" }, () -> {
        // A table storing associations between a Collection ID (e.g. 'messages') to a parent path
        // (e.g. '/chats/123') that contains it as a (sub)collection. This is used to efficiently
        // find all collections to query when performing a Collection Group query. Note that the
        // parent path will be an empty path in the case of root-level collections.
        db.execSQL("CREATE TABLE collection_parents (" + "collection_id TEXT, " + "parent TEXT, " + "PRIMARY KEY(collection_id, parent))");
    });
    // Helper to add an index entry iff we haven't already written it.
    MemoryIndexManager.MemoryCollectionParentIndex cache = new MemoryIndexManager.MemoryCollectionParentIndex();
    SQLiteStatement addIndexEntry = db.compileStatement("INSERT OR REPLACE INTO collection_parents (collection_id, parent) VALUES (?, ?)");
    Consumer<ResourcePath> addEntry = collectionPath -> {
        if (cache.add(collectionPath)) {
            String collectionId = collectionPath.getLastSegment();
            ResourcePath parentPath = collectionPath.popLast();
            addIndexEntry.clearBindings();
            addIndexEntry.bindString(1, collectionId);
            addIndexEntry.bindString(2, EncodedPath.encode(parentPath));
            addIndexEntry.execute();
        }
    };
    // Index existing remote documents.
    SQLitePersistence.Query remoteDocumentsQuery = new SQLitePersistence.Query(db, "SELECT path FROM remote_documents");
    remoteDocumentsQuery.forEach(row -> {
        ResourcePath path = EncodedPath.decodeResourcePath(row.getString(0));
        addEntry.accept(path.popLast());
    });
    // Index existing mutations.
    SQLitePersistence.Query documentMutationsQuery = new SQLitePersistence.Query(db, "SELECT path FROM document_mutations");
    documentMutationsQuery.forEach(row -> {
        ResourcePath path = EncodedPath.decodeResourcePath(row.getString(0));
        addEntry.accept(path.popLast());
    });
}
Also used : InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) Assert.hardAssert(com.google.firebase.firestore.util.Assert.hardAssert) Logger(com.google.firebase.firestore.util.Logger) TextUtils(android.text.TextUtils) ResourcePath(com.google.firebase.firestore.model.ResourcePath) ArrayList(java.util.ArrayList) Target(com.google.firebase.firestore.proto.Target) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) Consumer(com.google.firebase.firestore.util.Consumer) DatabaseUtils(android.database.DatabaseUtils) List(java.util.List) Assert.fail(com.google.firebase.firestore.util.Assert.fail) ContentValues(android.content.ContentValues) SQLiteStatement(android.database.sqlite.SQLiteStatement) VisibleForTesting(androidx.annotation.VisibleForTesting) Cursor(android.database.Cursor) ResourcePath(com.google.firebase.firestore.model.ResourcePath) SQLiteStatement(android.database.sqlite.SQLiteStatement)

Aggregations

ResourcePath (com.google.firebase.firestore.model.ResourcePath)34 MutableDocument (com.google.firebase.firestore.model.MutableDocument)13 DocumentKey (com.google.firebase.firestore.model.DocumentKey)12 Test (org.junit.Test)11 ArrayList (java.util.ArrayList)8 OrderBy (com.google.firebase.firestore.core.OrderBy)4 HashMap (java.util.HashMap)4 Bound (com.google.firebase.firestore.core.Bound)3 Cursor (android.database.Cursor)2 SQLiteStatement (android.database.sqlite.SQLiteStatement)2 ImmutableSortedMap (com.google.firebase.database.collection.ImmutableSortedMap)2 ImmutableSortedSet (com.google.firebase.database.collection.ImmutableSortedSet)2 FieldFilter (com.google.firebase.firestore.core.FieldFilter)2 Filter (com.google.firebase.firestore.core.Filter)2 Query (com.google.firebase.firestore.core.Query)2 EncodedPath.decodeResourcePath (com.google.firebase.firestore.local.EncodedPath.decodeResourcePath)2 DocumentCollections.emptyDocumentMap (com.google.firebase.firestore.model.DocumentCollections.emptyDocumentMap)2 SnapshotVersion (com.google.firebase.firestore.model.SnapshotVersion)2 BackgroundQueue (com.google.firebase.firestore.util.BackgroundQueue)2 CollectionSelector (com.google.firestore.v1.StructuredQuery.CollectionSelector)2