Skip to content

Commit 09e6243

Browse files
authored
Use ObjectMap instead of Map with proper comparator. (#6018)
* Use ObjectMap instead of Map with proper comparator. * use documentKeySet. * Address comments. * prettier. * Update names.
1 parent 27ed845 commit 09e6243

8 files changed

+110
-49
lines changed

packages/firestore/src/local/document_overlay_cache.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { DocumentKeySet } from '../model/collections';
18+
import { DocumentKeySet, MutationMap, OverlayMap } from '../model/collections';
1919
import { DocumentKey } from '../model/document_key';
20-
import { Mutation } from '../model/mutation';
2120
import { Overlay } from '../model/overlay';
2221
import { ResourcePath } from '../model/path';
2322

@@ -51,7 +50,7 @@ export interface DocumentOverlayCache {
5150
saveOverlays(
5251
transaction: PersistenceTransaction,
5352
largestBatchId: number,
54-
overlays: Map<DocumentKey, Mutation>
53+
overlays: MutationMap
5554
): PersistencePromise<void>;
5655

5756
/** Removes overlays for the given document keys and batch ID. */
@@ -74,7 +73,7 @@ export interface DocumentOverlayCache {
7473
transaction: PersistenceTransaction,
7574
collection: ResourcePath,
7675
sinceBatchId: number
77-
): PersistencePromise<Map<DocumentKey, Overlay>>;
76+
): PersistencePromise<OverlayMap>;
7877

7978
/**
8079
* Returns `count` overlays with a batch ID higher than `sinceBatchId` for the
@@ -95,5 +94,5 @@ export interface DocumentOverlayCache {
9594
collectionGroup: string,
9695
sinceBatchId: number,
9796
count: number
98-
): PersistencePromise<Map<DocumentKey, Overlay>>;
97+
): PersistencePromise<OverlayMap>;
9998
}

packages/firestore/src/local/indexeddb_document_overlay_cache.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
*/
1717

1818
import { User } from '../auth/user';
19-
import { DocumentKeySet } from '../model/collections';
19+
import {
20+
DocumentKeySet,
21+
MutationMap,
22+
OverlayMap,
23+
newOverlayMap
24+
} from '../model/collections';
2025
import { DocumentKey } from '../model/document_key';
21-
import { Mutation } from '../model/mutation';
2226
import { Overlay } from '../model/overlay';
2327
import { ResourcePath } from '../model/path';
2428

@@ -80,10 +84,10 @@ export class IndexedDbDocumentOverlayCache implements DocumentOverlayCache {
8084
saveOverlays(
8185
transaction: PersistenceTransaction,
8286
largestBatchId: number,
83-
overlays: Map<DocumentKey, Mutation>
87+
overlays: MutationMap
8488
): PersistencePromise<void> {
8589
const promises: Array<PersistencePromise<void>> = [];
86-
overlays.forEach(mutation => {
90+
overlays.forEach((_, mutation) => {
8791
const overlay = new Overlay(largestBatchId, mutation);
8892
promises.push(this.saveOverlay(transaction, overlay));
8993
});
@@ -124,8 +128,8 @@ export class IndexedDbDocumentOverlayCache implements DocumentOverlayCache {
124128
transaction: PersistenceTransaction,
125129
collection: ResourcePath,
126130
sinceBatchId: number
127-
): PersistencePromise<Map<DocumentKey, Overlay>> {
128-
const result = new Map<DocumentKey, Overlay>();
131+
): PersistencePromise<OverlayMap> {
132+
const result = newOverlayMap();
129133
const collectionPath = encodeResourcePath(collection);
130134
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
131135
// is not inclusive.
@@ -150,8 +154,8 @@ export class IndexedDbDocumentOverlayCache implements DocumentOverlayCache {
150154
collectionGroup: string,
151155
sinceBatchId: number,
152156
count: number
153-
): PersistencePromise<Map<DocumentKey, Overlay>> {
154-
const result = new Map<DocumentKey, Overlay>();
157+
): PersistencePromise<OverlayMap> {
158+
const result = newOverlayMap();
155159
let currentBatchId: number | undefined = undefined;
156160
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
157161
// is not inclusive.
@@ -173,7 +177,7 @@ export class IndexedDbDocumentOverlayCache implements DocumentOverlayCache {
173177
// `count` if there are more overlays from the `currentBatchId`.
174178
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
175179
if (
176-
result.size < count ||
180+
result.size() < count ||
177181
overlay.largestBatchId === currentBatchId
178182
) {
179183
result.set(overlay.getKey(), overlay);

packages/firestore/src/local/memory_document_overlay_cache.ts

+24-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { DocumentKeySet } from '../model/collections';
18+
import {
19+
documentKeySet,
20+
DocumentKeySet,
21+
MutationMap,
22+
OverlayMap,
23+
newOverlayMap
24+
} from '../model/collections';
1925
import { DocumentKey } from '../model/document_key';
2026
import { Mutation } from '../model/mutation';
2127
import { Overlay } from '../model/overlay';
@@ -35,7 +41,7 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
3541
private overlays = new SortedMap<DocumentKey, Overlay>(
3642
DocumentKey.comparator
3743
);
38-
private overlayByBatchId = new Map<number, Set<DocumentKey>>();
44+
private overlayByBatchId = new Map<number, DocumentKeySet>();
3945

4046
getOverlay(
4147
transaction: PersistenceTransaction,
@@ -47,9 +53,9 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
4753
saveOverlays(
4854
transaction: PersistenceTransaction,
4955
largestBatchId: number,
50-
overlays: Map<DocumentKey, Mutation>
56+
overlays: MutationMap
5157
): PersistencePromise<void> {
52-
overlays.forEach(mutation => {
58+
overlays.forEach((_, mutation) => {
5359
this.saveOverlay(transaction, largestBatchId, mutation);
5460
});
5561
return PersistencePromise.resolve();
@@ -72,8 +78,8 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
7278
transaction: PersistenceTransaction,
7379
collection: ResourcePath,
7480
sinceBatchId: number
75-
): PersistencePromise<Map<DocumentKey, Overlay>> {
76-
const result = new Map<DocumentKey, Overlay>();
81+
): PersistencePromise<OverlayMap> {
82+
const result = newOverlayMap();
7783

7884
const immediateChildrenPathLength = collection.length + 1;
7985
const prefix = new DocumentKey(collection.child(''));
@@ -102,8 +108,8 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
102108
collectionGroup: string,
103109
sinceBatchId: number,
104110
count: number
105-
): PersistencePromise<Map<DocumentKey, Overlay>> {
106-
let batchIdToOverlays = new SortedMap<number, Map<DocumentKey, Overlay>>(
111+
): PersistencePromise<OverlayMap> {
112+
let batchIdToOverlays = new SortedMap<number, OverlayMap>(
107113
(key1: number, key2: number) => key1 - key2
108114
);
109115

@@ -118,7 +124,7 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
118124
if (overlay.largestBatchId > sinceBatchId) {
119125
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
120126
if (overlaysForBatchId === null) {
121-
overlaysForBatchId = new Map<DocumentKey, Overlay>();
127+
overlaysForBatchId = newOverlayMap();
122128
batchIdToOverlays = batchIdToOverlays.insert(
123129
overlay.largestBatchId,
124130
overlaysForBatchId
@@ -128,13 +134,13 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
128134
}
129135
}
130136

131-
const result = new Map<DocumentKey, Overlay>();
137+
const result = newOverlayMap();
132138
const batchIter = batchIdToOverlays.getIterator();
133139
while (batchIter.hasNext()) {
134140
const entry = batchIter.getNext();
135141
const overlays = entry.value;
136-
overlays.forEach((overlay, key) => result.set(key, overlay));
137-
if (result.size >= count) {
142+
overlays.forEach((key, overlay) => result.set(key, overlay));
143+
if (result.size() >= count) {
138144
break;
139145
}
140146
}
@@ -153,7 +159,10 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
153159
// Remove the association of the overlay to its batch id.
154160
const existing = this.overlays.get(mutation.key);
155161
if (existing !== null) {
156-
this.overlayByBatchId.get(existing.largestBatchId)!.delete(mutation.key);
162+
const newSet = this.overlayByBatchId
163+
.get(existing.largestBatchId)!
164+
.delete(mutation.key);
165+
this.overlayByBatchId.set(existing.largestBatchId, newSet);
157166
}
158167

159168
this.overlays = this.overlays.insert(
@@ -164,9 +173,9 @@ export class MemoryDocumentOverlayCache implements DocumentOverlayCache {
164173
// Create the association of this overlay to the given largestBatchId.
165174
let batch = this.overlayByBatchId.get(largestBatchId);
166175
if (batch === undefined) {
167-
batch = new Set<DocumentKey>();
176+
batch = documentKeySet();
168177
this.overlayByBatchId.set(largestBatchId, batch);
169178
}
170-
batch.add(mutation.key);
179+
this.overlayByBatchId.set(largestBatchId, batch.add(mutation.key));
171180
}
172181
}

packages/firestore/src/model/collections.ts

+19
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818
import { SnapshotVersion } from '../core/snapshot_version';
1919
import { TargetId } from '../core/types';
2020
import { primitiveComparator } from '../util/misc';
21+
import { ObjectMap } from '../util/obj_map';
2122
import { SortedMap } from '../util/sorted_map';
2223
import { SortedSet } from '../util/sorted_set';
2324

2425
import { Document, MutableDocument } from './document';
2526
import { DocumentKey } from './document_key';
27+
import { Mutation } from './mutation';
28+
import { Overlay } from './overlay';
2629

2730
/** Miscellaneous collection types / constants. */
2831

@@ -47,6 +50,22 @@ export function documentMap(): DocumentMap {
4750
return EMPTY_DOCUMENT_MAP;
4851
}
4952

53+
export type OverlayMap = ObjectMap<DocumentKey, Overlay>;
54+
export function newOverlayMap(): OverlayMap {
55+
return new ObjectMap<DocumentKey, Overlay>(
56+
key => key.toString(),
57+
(l, r) => l.isEqual(r)
58+
);
59+
}
60+
61+
export type MutationMap = ObjectMap<DocumentKey, Mutation>;
62+
export function newMutationMap(): MutationMap {
63+
return new ObjectMap<DocumentKey, Mutation>(
64+
key => key.toString(),
65+
(l, r) => l.isEqual(r)
66+
);
67+
}
68+
5069
export type DocumentVersionMap = SortedMap<DocumentKey, SnapshotVersion>;
5170
const EMPTY_DOCUMENT_VERSION_MAP = new SortedMap<DocumentKey, SnapshotVersion>(
5271
DocumentKey.comparator

packages/firestore/src/util/obj_map.ts

+11
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export class ObjectMap<KeyType, ValueType> {
3636
[canonicalId: string]: Array<Entry<KeyType, ValueType>>;
3737
} = {};
3838

39+
/** The number of entries stored in the map */
40+
private innerSize = 0;
41+
3942
constructor(
4043
private mapKeyFn: (key: KeyType) => string,
4144
private equalsFn: (l: KeyType, r: KeyType) => boolean
@@ -66,15 +69,18 @@ export class ObjectMap<KeyType, ValueType> {
6669
const matches = this.inner[id];
6770
if (matches === undefined) {
6871
this.inner[id] = [[key, value]];
72+
this.innerSize++;
6973
return;
7074
}
7175
for (let i = 0; i < matches.length; i++) {
7276
if (this.equalsFn(matches[i][0], key)) {
77+
// This is updating an existing entry and does not increase `innerSize`.
7378
matches[i] = [key, value];
7479
return;
7580
}
7681
}
7782
matches.push([key, value]);
83+
this.innerSize++;
7884
}
7985

8086
/**
@@ -93,6 +99,7 @@ export class ObjectMap<KeyType, ValueType> {
9399
} else {
94100
matches.splice(i, 1);
95101
}
102+
this.innerSize--;
96103
return true;
97104
}
98105
}
@@ -110,4 +117,8 @@ export class ObjectMap<KeyType, ValueType> {
110117
isEmpty(): boolean {
111118
return isEmpty(this.inner);
112119
}
120+
121+
size(): number {
122+
return this.innerSize;
123+
}
113124
}

packages/firestore/test/unit/local/document_overlay_cache.test.ts

+14-13
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ import { expect } from 'chai';
1919
import { User } from '../../../src/auth/user';
2020
import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence';
2121
import { Persistence } from '../../../src/local/persistence';
22-
import { documentKeySet, DocumentKeySet } from '../../../src/model/collections';
23-
import { DocumentKey } from '../../../src/model/document_key';
22+
import {
23+
documentKeySet,
24+
MutationMap,
25+
OverlayMap,
26+
newMutationMap
27+
} from '../../../src/model/collections';
2428
import { Mutation, mutationEquals } from '../../../src/model/mutation';
25-
import { Overlay } from '../../../src/model/overlay';
2629
import { addEqualityMatcher } from '../../util/equality_matcher';
2730
import {
2831
deleteMutation,
@@ -86,7 +89,7 @@ function genericDocumentOverlayCacheTests(): void {
8689
largestBatch: number,
8790
...mutations: Mutation[]
8891
): Promise<void> {
89-
const data: Map<DocumentKey, Mutation> = new Map<DocumentKey, Mutation>();
92+
const data: MutationMap = newMutationMap();
9093
for (const mutation of mutations) {
9194
data.set(mutation.key, mutation);
9295
}
@@ -97,7 +100,7 @@ function genericDocumentOverlayCacheTests(): void {
97100
largestBatch: number,
98101
...overlayKeys: string[]
99102
): Promise<void> {
100-
const data: Map<DocumentKey, Mutation> = new Map<DocumentKey, Mutation>();
103+
const data: MutationMap = newMutationMap();
101104
for (const overlayKey of overlayKeys) {
102105
data.set(key(overlayKey), setMutation(overlayKey, {}));
103106
}
@@ -115,16 +118,14 @@ function genericDocumentOverlayCacheTests(): void {
115118
}
116119

117120
function verifyOverlayContains(
118-
overlays: Map<DocumentKey, Overlay>,
121+
overlays: OverlayMap,
119122
...keys: string[]
120123
): void {
121-
const overlayKeys: DocumentKeySet = documentKeySet(
122-
...Array.from(overlays.keys())
123-
);
124-
const expectedKeys: DocumentKeySet = documentKeySet(
125-
...Array.from(keys.map(value => key(value)))
126-
);
127-
expect(overlayKeys.isEqual(expectedKeys)).to.equal(true);
124+
let overlayKeys = documentKeySet();
125+
overlays.forEach(overlayKey => (overlayKeys = overlayKeys.add(overlayKey)));
126+
let expectedKeys = documentKeySet();
127+
keys.forEach(value => (expectedKeys = expectedKeys.add(key(value))));
128+
expect(overlayKeys.isEqual(expectedKeys)).to.deep.equal(true);
128129
}
129130

130131
it('returns null when overlay is not found', async () => {

packages/firestore/test/unit/local/test_document_overlay_cache.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717

1818
import { DocumentOverlayCache } from '../../../src/local/document_overlay_cache';
1919
import { Persistence } from '../../../src/local/persistence';
20-
import { DocumentKeySet } from '../../../src/model/collections';
20+
import {
21+
DocumentKeySet,
22+
MutationMap,
23+
OverlayMap
24+
} from '../../../src/model/collections';
2125
import { DocumentKey } from '../../../src/model/document_key';
2226
import { Mutation } from '../../../src/model/mutation';
2327
import { Overlay } from '../../../src/model/overlay';
@@ -34,10 +38,7 @@ export class TestDocumentOverlayCache {
3438
private cache: DocumentOverlayCache
3539
) {}
3640

37-
saveOverlays(
38-
largestBatch: number,
39-
data: Map<DocumentKey, Mutation>
40-
): Promise<void> {
41+
saveOverlays(largestBatch: number, data: MutationMap): Promise<void> {
4142
return this.persistence.runTransaction('saveOverlays', 'readwrite', txn => {
4243
return this.cache.saveOverlays(txn, largestBatch, data);
4344
});
@@ -61,7 +62,7 @@ export class TestDocumentOverlayCache {
6162
getOverlaysForCollection(
6263
path: ResourcePath,
6364
sinceBatchId: number
64-
): Promise<Map<DocumentKey, Overlay>> {
65+
): Promise<OverlayMap> {
6566
return this.persistence.runTransaction(
6667
'getOverlaysForCollection',
6768
'readonly',
@@ -75,7 +76,7 @@ export class TestDocumentOverlayCache {
7576
collectionGroup: string,
7677
sinceBatchId: number,
7778
count: number
78-
): Promise<Map<DocumentKey, Overlay>> {
79+
): Promise<OverlayMap> {
7980
return this.persistence.runTransaction(
8081
'getOverlaysForCollectionGroup',
8182
'readonly',

0 commit comments

Comments
 (0)