Skip to content

Commit

Permalink
Add Index Schema (#5954)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Feb 1, 2022
1 parent 0a04a1c commit 4cec423
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 11 deletions.
120 changes: 112 additions & 8 deletions packages/firestore/src/local/indexeddb_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

import { BatchId, ListenSequenceNumber, TargetId } from '../core/types';
import { IndexKind } from '../model/field_index';
import { ResourcePath } from '../model/path';
import { BundledQuery } from '../protos/firestore_bundle_proto';
import {
Expand All @@ -31,6 +32,11 @@ import {
encodeResourcePath
} from './encoded_resource_path';

// TODO(indexing): Remove this constant
const INDEXING_ENABLED = false;

export const INDEXING_SCHEMA_VERSION = 12;

/**
* Schema Version for the Web client:
* 1. Initial version including Mutation Queue, Query Cache, and Remote
Expand All @@ -49,8 +55,9 @@ import {
* an auto-incrementing ID. This is required for Index-Free queries.
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
* 11. Add bundles and named_queries for bundle support.
* 12. Add indexing support.
*/
export const SCHEMA_VERSION = 11;
export const SCHEMA_VERSION = INDEXING_ENABLED ? INDEXING_SCHEMA_VERSION : 11;

/**
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
Expand Down Expand Up @@ -655,9 +662,7 @@ export type DbClientMetadataKey = string;

export type DbBundlesKey = string;

/**
* A object representing a bundle loaded by the SDK.
*/
/** An object representing a bundle loaded by the SDK. */
export class DbBundle {
/** Name of the IndexedDb object store. */
static store = 'bundles';
Expand All @@ -676,9 +681,7 @@ export class DbBundle {

export type DbNamedQueriesKey = string;

/**
* A object representing a named query loaded by the SDK via a bundle.
*/
/** An object representing a named query loaded by the SDK via a bundle. */
export class DbNamedQuery {
/** Name of the IndexedDb object store. */
static store = 'namedQueries';
Expand All @@ -695,6 +698,101 @@ export class DbNamedQuery {
) {}
}

/** The key for each index consisting of just the index id. */
export type DbIndexConfigurationKey = number;

/** An object representing the global configuration for a field index. */
export class DbIndexConfiguration {
/** Name of the IndexedDb object store. */
static store = 'indexConfiguration';

static keyPath = 'indexId';

constructor(
/** The index id for this entry. */
public indexId: number,
/** The collection group this index belongs to. */
public collectionGroup: string,
/** The fields to index for this index. */
public fields: [[name: string, kind: IndexKind]]
) {}
}

/** The key for each index state consisting of the index id and its user id. */
export type DbIndexStateKey = [number, string];

/**
* An object describing how up-to-date the index backfill is for each user and
* index.
*/
export class DbIndexState {
/** Name of the IndexedDb object store. */
static store = 'indexState';

static keyPath = ['indexId', 'uid'];

constructor(
/** The index id for this entry. */
public indexId: number,
/** The user id for this entry. */
public uid: string,
/**
* A number that indicates when the index was last updated (relative to
* other indexes).
*/
public sequenceNumber: number,
/**
* The latest read time that has been indexed by Firestore for this field
* index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
*/
public readTime: DbTimestamp,
/**
* The last document that has been indexed for this field index. Empty if
* no documents have been indexed.
*/
public documentKey: EncodedResourcePath,
/**
* The largest mutation batch id that has been processed for this index. -1
* if no mutations have been indexed.
*/
public largestBatchId: number
) {}
}

/**
* The key for each index entry consists of the index id and its user id,
* the encoded array and directional value for the indexed fields as well as
* the encoded document path for the indexed document.
*/
export type DbIndexEntryKey = [number, string, Uint8Array, Uint8Array, string];

/** An object that stores the encoded entries for all documents and fields. */
export class DbIndexEntries {
/** Name of the IndexedDb object store. */
static store = 'indexEntries';

static keyPath = [
'indexId',
'uid',
'arrayValue',
'directionalValue',
'documentKey'
];

constructor(
/** The index id for this entry. */
public indexId: number,
/** The user id for this entry. */
public uid: string,
/** The encoded array index value for this entry. */
public arrayValue: Uint8Array,
/** The encoded directional value for equality and inequality filters. */
public directionalValue: Uint8Array,
/** The document key this entry points to. */
public documentKey: EncodedResourcePath
) {}
}

// Visible for testing
export const V1_STORES = [
DbMutationQueue.store,
Expand All @@ -712,7 +810,6 @@ export const V1_STORES = [
// Visible for testing
export const V3_STORES = V1_STORES;

// Visible for testing
// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
export const V4_STORES = [...V3_STORES, DbClientMetadata.store];

Expand All @@ -730,6 +827,13 @@ export const V8_STORES = [...V6_STORES, DbCollectionParent.store];

export const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];

export const V12_STORES = [
...V11_STORES,
DbIndexConfiguration.store,
DbIndexState.store,
DbIndexEntries.store
];

/**
* The list of all default IndexedDB stores used throughout the SDK. This is
* used when creating transactions so that access across all stores is done
Expand Down
28 changes: 25 additions & 3 deletions packages/firestore/src/local/indexeddb_schema_converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
DbCollectionParentKey,
DbDocumentMutation,
DbDocumentMutationKey,
DbIndexConfiguration,
DbIndexEntries,
DbIndexState,
DbMutationBatch,
DbMutationBatchKey,
DbMutationQueue,
Expand All @@ -51,7 +54,7 @@ import {
DbTargetGlobal,
DbTargetGlobalKey,
DbTargetKey,
SCHEMA_VERSION
INDEXING_SCHEMA_VERSION
} from './indexeddb_schema';
import {
fromDbMutationBatch,
Expand Down Expand Up @@ -80,10 +83,10 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
fromVersion: number,
toVersion: number
): PersistencePromise<void> {
hardAssert(
debugAssert(
fromVersion < toVersion &&
fromVersion >= 0 &&
toVersion <= SCHEMA_VERSION,
toVersion <= INDEXING_SCHEMA_VERSION,
`Unexpected schema upgrade from v${fromVersion} to v${toVersion}.`
);

Expand Down Expand Up @@ -169,6 +172,13 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
createNamedQueriesStore(db);
});
}

if (fromVersion < 12 && toVersion >= 12) {
p = p.next(() => {
createFieldIndex(db);
});
}

return p;
}

Expand Down Expand Up @@ -504,3 +514,15 @@ function createNamedQueriesStore(db: IDBDatabase): void {
keyPath: DbNamedQuery.keyPath
});
}

function createFieldIndex(db: IDBDatabase): void {
db.createObjectStore(DbIndexConfiguration.store, {
keyPath: DbIndexConfiguration.keyPath
});
db.createObjectStore(DbIndexState.store, {
keyPath: DbIndexState.keyPath
});
db.createObjectStore(DbIndexEntries.store, {
keyPath: DbIndexEntries.keyPath
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
DbTargetKey,
DbTimestamp,
SCHEMA_VERSION,
V12_STORES,
V1_STORES,
V3_STORES,
V4_STORES,
Expand Down Expand Up @@ -1024,6 +1025,14 @@ describe('IndexedDbSchema: createOrUpgradeDb', () => {
});
});

it('can upgrade from version 11 to 12', async () => {
await withDb(11, async () => {});
await withDb(12, async (db, version, objectStores) => {
expect(version).to.have.equal(12);
expect(objectStores).to.have.members(V12_STORES);
});
});

it('downgrading throws a custom error', async function (this: Context) {
// Upgrade to latest version
await withDb(SCHEMA_VERSION, async (db, version) => {
Expand Down

0 comments on commit 4cec423

Please sign in to comment.