Skip to content

Commit cef59a7

Browse files
Provide CRUD methods for IndexManager
1 parent 0e2d44d commit cef59a7

File tree

4 files changed

+285
-30
lines changed

4 files changed

+285
-30
lines changed

packages/firestore/src/local/indexeddb_index_manager.ts

Lines changed: 173 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { User } from '../auth/user';
1819
import { Target } from '../core/target';
1920
import {
2021
documentKeySet,
@@ -31,26 +32,45 @@ import {
3132
encodeResourcePath
3233
} from './encoded_resource_path';
3334
import { IndexManager } from './index_manager';
34-
import { DbCollectionParent, DbCollectionParentKey } from './indexeddb_schema';
35+
import {
36+
DbCollectionParent,
37+
DbCollectionParentKey,
38+
DbIndexConfiguration,
39+
DbIndexConfigurationKey,
40+
DbIndexEntry,
41+
DbIndexEntryKey,
42+
DbIndexState,
43+
DbIndexStateKey
44+
} from './indexeddb_schema';
3545
import { getStore } from './indexeddb_transaction';
46+
import {
47+
fromDbIndexConfiguration,
48+
toDbIndexConfiguration,
49+
toDbIndexState
50+
} from './local_serializer';
3651
import { MemoryCollectionParentIndex } from './memory_index_manager';
3752
import { PersistencePromise } from './persistence_promise';
3853
import { PersistenceTransaction } from './persistence_transaction';
3954
import { SimpleDbStore } from './simple_db';
4055

4156
/**
4257
* A persisted implementation of IndexManager.
58+
*
59+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
60+
* data as it supports multi-tab access.
4361
*/
4462
export class IndexedDbIndexManager implements IndexManager {
4563
/**
4664
* An in-memory copy of the index entries we've already written since the SDK
4765
* launched. Used to avoid re-writing the same entry repeatedly.
4866
*
49-
* This is *NOT* a complete cache of what's in persistence and so can never be used to
50-
* satisfy reads.
67+
* This is *NOT* a complete cache of what's in persistence and so can never be
68+
* used to satisfy reads.
5169
*/
5270
private collectionParentsCache = new MemoryCollectionParentIndex();
5371

72+
constructor(private readonly user: User) {}
73+
5474
/**
5575
* Adds a new entry to the collection parent index.
5676
*
@@ -114,16 +134,40 @@ export class IndexedDbIndexManager implements IndexManager {
114134
transaction: PersistenceTransaction,
115135
index: FieldIndex
116136
): PersistencePromise<void> {
117-
// TODO(indexing): Implement
118-
return PersistencePromise.resolve();
137+
const indexes = indexConfigurationStore(transaction);
138+
const dbIndex = toDbIndexConfiguration(index);
139+
return indexes.put(dbIndex).next();
119140
}
120141

121142
deleteFieldIndex(
122143
transaction: PersistenceTransaction,
123144
index: FieldIndex
124145
): PersistencePromise<void> {
125-
// TODO(indexing): Implement
126-
return PersistencePromise.resolve();
146+
const indexes = indexConfigurationStore(transaction);
147+
const states = indexStateStore(transaction);
148+
const entries = indexEntriesStore(transaction);
149+
return indexes
150+
.delete(index.indexId)
151+
.next(() =>
152+
states.delete(
153+
IDBKeyRange.bound(
154+
[index.indexId, ''],
155+
[index.indexId + 1, ''],
156+
/*lowerOpen=*/ false,
157+
/*upperOpen=*/ true
158+
)
159+
)
160+
)
161+
.next(() =>
162+
entries.delete(
163+
IDBKeyRange.bound(
164+
[index.indexId, '', new Uint8Array(), new Uint8Array(), ''],
165+
[index.indexId + 1, '', new Uint8Array(), new Uint8Array(), ''],
166+
/*lowerOpen=*/ false,
167+
/*upperOpen=*/ true
168+
)
169+
)
170+
);
127171
}
128172

129173
getDocumentsMatchingTarget(
@@ -147,24 +191,87 @@ export class IndexedDbIndexManager implements IndexManager {
147191
transaction: PersistenceTransaction,
148192
collectionGroup?: string
149193
): PersistencePromise<FieldIndex[]> {
150-
// TODO(indexing): Implement
151-
return PersistencePromise.resolve<FieldIndex[]>([]);
194+
const indexes = indexConfigurationStore(transaction);
195+
const states = indexStateStore(transaction);
196+
197+
return (
198+
collectionGroup
199+
? indexes.loadAll(
200+
DbIndexConfiguration.collectionGroupIndex,
201+
IDBKeyRange.bound(collectionGroup, collectionGroup)
202+
)
203+
: indexes.loadAll()
204+
).next(indexEntries => {
205+
const result: FieldIndex[] = [];
206+
return PersistencePromise.forEach(
207+
indexEntries,
208+
(indexEntry: DbIndexConfiguration) => {
209+
return states
210+
.get([indexEntry.indexId!, this.user.uid || ''])
211+
.next(indexState => {
212+
result.push(fromDbIndexConfiguration(indexEntry, indexState!));
213+
});
214+
}
215+
).next(() => result);
216+
});
152217
}
153218

154219
getNextCollectionGroupToUpdate(
155220
transaction: PersistenceTransaction
156221
): PersistencePromise<string | null> {
157-
// TODO(indexing): Implement
158-
return PersistencePromise.resolve<string | null>(null);
222+
let nextIndexId: number | null = null;
223+
224+
const indexes = indexConfigurationStore(transaction);
225+
const states = indexStateStore(transaction);
226+
return states
227+
.iterate(
228+
{
229+
index: DbIndexState.sequenceNumberIndex,
230+
range: IDBKeyRange.upperBound([
231+
this.user.uid || '',
232+
Number.MAX_SAFE_INTEGER
233+
])
234+
},
235+
(_, state, controller) => {
236+
controller.done();
237+
nextIndexId = state.indexId;
238+
}
239+
)
240+
.next(() =>
241+
nextIndexId == null
242+
? PersistencePromise.resolve<string | null>(null)
243+
: indexes
244+
.get(nextIndexId)
245+
.next(v => v!.collectionGroup as string | null)
246+
);
159247
}
160248

161249
updateCollectionGroup(
162250
transaction: PersistenceTransaction,
163251
collectionGroup: string,
164252
offset: IndexOffset
165253
): PersistencePromise<void> {
166-
// TODO(indexing): Implement
167-
return PersistencePromise.resolve();
254+
const indexes = indexConfigurationStore(transaction);
255+
const states = indexStateStore(transaction);
256+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber =>
257+
indexes
258+
.loadAll(
259+
DbIndexConfiguration.collectionGroupIndex,
260+
IDBKeyRange.bound(collectionGroup, collectionGroup)
261+
)
262+
.next(configs =>
263+
PersistencePromise.forEach(configs, (config: DbIndexConfiguration) =>
264+
states.put(
265+
toDbIndexState(
266+
config.indexId!,
267+
this.user,
268+
nextSequenceNumber,
269+
offset
270+
)
271+
)
272+
)
273+
)
274+
);
168275
}
169276

170277
updateIndexEntries(
@@ -174,6 +281,29 @@ export class IndexedDbIndexManager implements IndexManager {
174281
// TODO(indexing): Implement
175282
return PersistencePromise.resolve();
176283
}
284+
285+
private getNextSequenceNumber(
286+
transaction: PersistenceTransaction
287+
): PersistencePromise<number> {
288+
let nextSequenceNumber = 1;
289+
const states = indexStateStore(transaction);
290+
return states
291+
.iterate(
292+
{
293+
index: DbIndexState.sequenceNumberIndex,
294+
reverse: true,
295+
range: IDBKeyRange.upperBound([
296+
this.user.uid || '',
297+
Number.MAX_SAFE_INTEGER
298+
])
299+
},
300+
(_, state, controller) => {
301+
controller.done();
302+
nextSequenceNumber = state.sequenceNumber + 1;
303+
}
304+
)
305+
.next(() => nextSequenceNumber);
306+
}
177307
}
178308

179309
/**
@@ -188,3 +318,33 @@ function collectionParentsStore(
188318
DbCollectionParent.store
189319
);
190320
}
321+
322+
/**
323+
* Helper to get a typed SimpleDbStore for the index entry object store.
324+
*/
325+
function indexEntriesStore(
326+
txn: PersistenceTransaction
327+
): SimpleDbStore<DbIndexEntryKey, DbIndexEntry> {
328+
return getStore<DbIndexEntryKey, DbIndexEntry>(txn, DbIndexEntry.store);
329+
}
330+
331+
/**
332+
* Helper to get a typed SimpleDbStore for the index configuration object store.
333+
*/
334+
function indexConfigurationStore(
335+
txn: PersistenceTransaction
336+
): SimpleDbStore<DbIndexConfigurationKey, DbIndexConfiguration> {
337+
return getStore<DbIndexConfigurationKey, DbIndexConfiguration>(
338+
txn,
339+
DbIndexConfiguration.store
340+
);
341+
}
342+
343+
/**
344+
* Helper to get a typed SimpleDbStore for the index state object store.
345+
*/
346+
function indexStateStore(
347+
txn: PersistenceTransaction
348+
): SimpleDbStore<DbIndexStateKey, DbIndexState> {
349+
return getStore<DbIndexStateKey, DbIndexState>(txn, DbIndexState.store);
350+
}

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -708,13 +708,20 @@ export class DbIndexConfiguration {
708708

709709
static keyPath = 'indexId';
710710

711+
static collectionGroupIndex = 'collectionGroupIndex';
712+
713+
static collectionGroupIndexPath = ['collectionGroup'];
714+
711715
constructor(
712-
/** The index id for this entry. */
713-
public indexId: number,
716+
/**
717+
* The index id for this entry. Undefined for indexes that are not yet
718+
* persisted.
719+
*/
720+
public indexId: number | undefined,
714721
/** The collection group this index belongs to. */
715722
public collectionGroup: string,
716723
/** The fields to index for this index. */
717-
public fields: [[name: string, kind: IndexKind]]
724+
public fields: Array<[name: string, kind: IndexKind]>
718725
) {}
719726
}
720727

@@ -731,6 +738,16 @@ export class DbIndexState {
731738

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

741+
/**
742+
* An index that provides access to documents in a collection sorted by last
743+
* update time. Used by the backfiller.
744+
*
745+
* PORTING NOTE: iOS and Android maintain this index in-memory.
746+
*/
747+
static sequenceNumberIndex = 'sequenceNumberIndex';
748+
749+
static sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
750+
734751
constructor(
735752
/** The index id for this entry. */
736753
public indexId: number,
@@ -767,7 +784,7 @@ export class DbIndexState {
767784
export type DbIndexEntryKey = [number, string, Uint8Array, Uint8Array, string];
768785

769786
/** An object that stores the encoded entries for all documents and fields. */
770-
export class DbIndexEntries {
787+
export class DbIndexEntry {
771788
/** Name of the IndexedDb object store. */
772789
static store = 'indexEntries';
773790

@@ -831,7 +848,7 @@ export const V12_STORES = [
831848
...V11_STORES,
832849
DbIndexConfiguration.store,
833850
DbIndexState.store,
834-
DbIndexEntries.store
851+
DbIndexEntry.store
835852
];
836853

837854
/**

packages/firestore/src/local/indexeddb_schema_converter.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
DbDocumentMutation,
3737
DbDocumentMutationKey,
3838
DbIndexConfiguration,
39-
DbIndexEntries,
39+
DbIndexEntry,
4040
DbIndexState,
4141
DbMutationBatch,
4242
DbMutationBatchKey,
@@ -175,7 +175,7 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
175175

176176
if (fromVersion < 12 && toVersion >= 12) {
177177
p = p.next(() => {
178-
createFieldIndex(db);
178+
createFieldIndex(db, txn);
179179
});
180180
}
181181

@@ -515,14 +515,30 @@ function createNamedQueriesStore(db: IDBDatabase): void {
515515
});
516516
}
517517

518-
function createFieldIndex(db: IDBDatabase): void {
519-
db.createObjectStore(DbIndexConfiguration.store, {
520-
keyPath: DbIndexConfiguration.keyPath
521-
});
522-
db.createObjectStore(DbIndexState.store, {
518+
function createFieldIndex(db: IDBDatabase, txn: IDBTransaction): void {
519+
const indexConfigurationStore = db.createObjectStore(
520+
DbIndexConfiguration.store,
521+
{
522+
keyPath: DbIndexConfiguration.keyPath,
523+
autoIncrement: true
524+
}
525+
);
526+
indexConfigurationStore.createIndex(
527+
DbIndexConfiguration.collectionGroupIndex,
528+
DbIndexConfiguration.collectionGroupIndexPath,
529+
{ unique: false }
530+
);
531+
532+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
523533
keyPath: DbIndexState.keyPath
524534
});
525-
db.createObjectStore(DbIndexEntries.store, {
526-
keyPath: DbIndexEntries.keyPath
535+
indexStateStore.createIndex(
536+
DbIndexState.sequenceNumberIndex,
537+
DbIndexState.sequenceNumberIndexPath,
538+
{ unique: false }
539+
);
540+
541+
db.createObjectStore(DbIndexEntry.store, {
542+
keyPath: DbIndexEntry.keyPath
527543
});
528544
}

0 commit comments

Comments
 (0)