@firebase/firestore 3.4.4-canary.f5ac47fb1 → 3.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/firestore/src/api/credentials.d.ts +1 -0
- package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/firestore/src/local/persistence.d.ts +8 -2
- package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/firestore/src/model/document_key.d.ts +5 -0
- package/dist/firestore/src/model/field_index.d.ts +2 -2
- package/dist/firestore/src/model/overlay.d.ts +32 -0
- package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/index.esm2017.js +4367 -3517
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4226 -3437
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1519 -242
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1501 -245
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4355 -3509
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +84 -12
- package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/lite/index.browser.esm2017.js +1121 -1112
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +60 -48
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.node.cjs.js +13 -2
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +13 -2
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +46 -37
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +5 -0
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +112 -112
- package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/packages/firestore/dist/index.esm2017.d.ts +187 -184
- package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +29 -2
- package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +83 -9
- package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/packages/firestore/src/platform/node/grpc_connection.d.ts +2 -2
- package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/private.d.ts +79 -12
- package/package.json +9 -9
package/dist/index.node.cjs.js
CHANGED
|
@@ -8,11 +8,32 @@ var logger = require('@firebase/logger');
|
|
|
8
8
|
var util = require('util');
|
|
9
9
|
var util$1 = require('@firebase/util');
|
|
10
10
|
var crypto = require('crypto');
|
|
11
|
-
var
|
|
11
|
+
var grpc = require('@grpc/grpc-js');
|
|
12
12
|
var protoLoader = require('@grpc/proto-loader');
|
|
13
13
|
|
|
14
|
+
function _interopNamespace(e) {
|
|
15
|
+
if (e && e.__esModule) return e;
|
|
16
|
+
var n = Object.create(null);
|
|
17
|
+
if (e) {
|
|
18
|
+
Object.keys(e).forEach(function (k) {
|
|
19
|
+
if (k !== 'default') {
|
|
20
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () { return e[k]; }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
n["default"] = e;
|
|
29
|
+
return Object.freeze(n);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
|
|
33
|
+
var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
|
|
34
|
+
|
|
14
35
|
const name = "@firebase/firestore";
|
|
15
|
-
const version$1 = "3.4.
|
|
36
|
+
const version$1 = "3.4.5";
|
|
16
37
|
|
|
17
38
|
/**
|
|
18
39
|
* @license
|
|
@@ -65,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
65
86
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
66
87
|
User.MOCK_USER = new User('mock-user');
|
|
67
88
|
|
|
68
|
-
const version = "9.6.
|
|
89
|
+
const version = "9.6.7";
|
|
69
90
|
|
|
70
91
|
/**
|
|
71
92
|
* @license
|
|
@@ -670,13 +691,19 @@ class FirebaseAppCheckTokenProvider {
|
|
|
670
691
|
this.appCheckProvider = appCheckProvider;
|
|
671
692
|
this.forceRefresh = false;
|
|
672
693
|
this.appCheck = null;
|
|
694
|
+
this.latestAppCheckToken = null;
|
|
673
695
|
}
|
|
674
696
|
start(asyncQueue, changeListener) {
|
|
675
697
|
const onTokenChanged = tokenResult => {
|
|
676
698
|
if (tokenResult.error != null) {
|
|
677
699
|
logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
|
|
678
700
|
}
|
|
679
|
-
|
|
701
|
+
const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
|
|
702
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
703
|
+
logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
|
|
704
|
+
return tokenUpdated
|
|
705
|
+
? changeListener(tokenResult.token)
|
|
706
|
+
: Promise.resolve();
|
|
680
707
|
};
|
|
681
708
|
this.tokenListener = (tokenResult) => {
|
|
682
709
|
asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
|
|
@@ -711,6 +738,7 @@ class FirebaseAppCheckTokenProvider {
|
|
|
711
738
|
return this.appCheck.getToken(forceRefresh).then(tokenResult => {
|
|
712
739
|
if (tokenResult) {
|
|
713
740
|
hardAssert(typeof tokenResult.token === 'string');
|
|
741
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
714
742
|
return new AppCheckToken(tokenResult.token);
|
|
715
743
|
}
|
|
716
744
|
else {
|
|
@@ -1214,9 +1242,10 @@ function decodeResourcePath(path) {
|
|
|
1214
1242
|
* an auto-incrementing ID. This is required for Index-Free queries.
|
|
1215
1243
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
1216
1244
|
* 11. Add bundles and named_queries for bundle support.
|
|
1217
|
-
* 12. Add
|
|
1245
|
+
* 12. Add document overlays.
|
|
1246
|
+
* 13. Add indexing support.
|
|
1218
1247
|
*/
|
|
1219
|
-
const SCHEMA_VERSION =
|
|
1248
|
+
const SCHEMA_VERSION = 12;
|
|
1220
1249
|
/**
|
|
1221
1250
|
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
|
|
1222
1251
|
*/
|
|
@@ -1778,7 +1807,10 @@ DbNamedQuery.keyPath = 'name';
|
|
|
1778
1807
|
/** An object representing the global configuration for a field index. */
|
|
1779
1808
|
class DbIndexConfiguration {
|
|
1780
1809
|
constructor(
|
|
1781
|
-
/**
|
|
1810
|
+
/**
|
|
1811
|
+
* The index id for this entry. Undefined for indexes that are not yet
|
|
1812
|
+
* persisted.
|
|
1813
|
+
*/
|
|
1782
1814
|
indexId,
|
|
1783
1815
|
/** The collection group this index belongs to. */
|
|
1784
1816
|
collectionGroup,
|
|
@@ -1792,6 +1824,16 @@ class DbIndexConfiguration {
|
|
|
1792
1824
|
/** Name of the IndexedDb object store. */
|
|
1793
1825
|
DbIndexConfiguration.store = 'indexConfiguration';
|
|
1794
1826
|
DbIndexConfiguration.keyPath = 'indexId';
|
|
1827
|
+
/**
|
|
1828
|
+
* An index that provides access to the index configurations by collection
|
|
1829
|
+
* group.
|
|
1830
|
+
*
|
|
1831
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1832
|
+
* not possible here as the Web client supports concurrent access to
|
|
1833
|
+
* persistence via multi-tab.
|
|
1834
|
+
*/
|
|
1835
|
+
DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
|
|
1836
|
+
DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
|
|
1795
1837
|
/**
|
|
1796
1838
|
* An object describing how up-to-date the index backfill is for each user and
|
|
1797
1839
|
* index.
|
|
@@ -1833,8 +1875,18 @@ class DbIndexState {
|
|
|
1833
1875
|
/** Name of the IndexedDb object store. */
|
|
1834
1876
|
DbIndexState.store = 'indexState';
|
|
1835
1877
|
DbIndexState.keyPath = ['indexId', 'uid'];
|
|
1878
|
+
/**
|
|
1879
|
+
* An index that provides access to documents in a collection sorted by last
|
|
1880
|
+
* update time. Used by the backfiller.
|
|
1881
|
+
*
|
|
1882
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1883
|
+
* not possible here as the Web client supports concurrent access to
|
|
1884
|
+
* persistence via multi-tab.
|
|
1885
|
+
*/
|
|
1886
|
+
DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
|
|
1887
|
+
DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
|
|
1836
1888
|
/** An object that stores the encoded entries for all documents and fields. */
|
|
1837
|
-
class
|
|
1889
|
+
class DbIndexEntry {
|
|
1838
1890
|
constructor(
|
|
1839
1891
|
/** The index id for this entry. */
|
|
1840
1892
|
indexId,
|
|
@@ -1854,14 +1906,56 @@ class DbIndexEntries {
|
|
|
1854
1906
|
}
|
|
1855
1907
|
}
|
|
1856
1908
|
/** Name of the IndexedDb object store. */
|
|
1857
|
-
|
|
1858
|
-
|
|
1909
|
+
DbIndexEntry.store = 'indexEntries';
|
|
1910
|
+
DbIndexEntry.keyPath = [
|
|
1859
1911
|
'indexId',
|
|
1860
1912
|
'uid',
|
|
1861
1913
|
'arrayValue',
|
|
1862
1914
|
'directionalValue',
|
|
1863
1915
|
'documentKey'
|
|
1864
1916
|
];
|
|
1917
|
+
DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
|
|
1918
|
+
DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
|
|
1919
|
+
/**
|
|
1920
|
+
* An object representing a document overlay.
|
|
1921
|
+
*/
|
|
1922
|
+
class DbDocumentOverlay {
|
|
1923
|
+
constructor(
|
|
1924
|
+
/** The user ID to whom this overlay belongs. */
|
|
1925
|
+
userId,
|
|
1926
|
+
/** The path to the collection that contains the document. */
|
|
1927
|
+
collectionPath,
|
|
1928
|
+
/** The ID (key) of the document within the collection. */
|
|
1929
|
+
documentId,
|
|
1930
|
+
/** The collection group to which the document belongs. */
|
|
1931
|
+
collectionGroup,
|
|
1932
|
+
/** The largest batch ID that's been applied for this overlay. */
|
|
1933
|
+
largestBatchId,
|
|
1934
|
+
/** The overlay mutation. */
|
|
1935
|
+
overlayMutation) {
|
|
1936
|
+
this.userId = userId;
|
|
1937
|
+
this.collectionPath = collectionPath;
|
|
1938
|
+
this.documentId = documentId;
|
|
1939
|
+
this.collectionGroup = collectionGroup;
|
|
1940
|
+
this.largestBatchId = largestBatchId;
|
|
1941
|
+
this.overlayMutation = overlayMutation;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
/** Name of the IndexedDb object store. */
|
|
1945
|
+
DbDocumentOverlay.store = 'documentOverlays';
|
|
1946
|
+
DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
|
|
1947
|
+
DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
|
|
1948
|
+
DbDocumentOverlay.collectionPathOverlayIndexPath = [
|
|
1949
|
+
'userId',
|
|
1950
|
+
'collectionPath',
|
|
1951
|
+
'largestBatchId'
|
|
1952
|
+
];
|
|
1953
|
+
DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
|
|
1954
|
+
DbDocumentOverlay.collectionGroupOverlayIndexPath = [
|
|
1955
|
+
'userId',
|
|
1956
|
+
'collectionGroup',
|
|
1957
|
+
'largestBatchId'
|
|
1958
|
+
];
|
|
1865
1959
|
// Visible for testing
|
|
1866
1960
|
const V1_STORES = [
|
|
1867
1961
|
DbMutationQueue.store,
|
|
@@ -1885,18 +1979,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
|
|
|
1885
1979
|
// V9 does not change the set of stores.
|
|
1886
1980
|
// V10 does not change the set of stores.
|
|
1887
1981
|
const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
|
|
1888
|
-
[
|
|
1889
|
-
|
|
1982
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1983
|
+
const V13_STORES = [
|
|
1984
|
+
...V12_STORES,
|
|
1890
1985
|
DbIndexConfiguration.store,
|
|
1891
1986
|
DbIndexState.store,
|
|
1892
|
-
|
|
1987
|
+
DbIndexEntry.store
|
|
1893
1988
|
];
|
|
1894
|
-
/**
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1989
|
+
/** Returns the object stores for the provided schema. */
|
|
1990
|
+
function getObjectStores(schemaVersion) {
|
|
1991
|
+
if (schemaVersion === 13) {
|
|
1992
|
+
return V13_STORES;
|
|
1993
|
+
}
|
|
1994
|
+
else if (schemaVersion === 12) {
|
|
1995
|
+
return V12_STORES;
|
|
1996
|
+
}
|
|
1997
|
+
else if (schemaVersion === 11) {
|
|
1998
|
+
return V11_STORES;
|
|
1999
|
+
}
|
|
2000
|
+
else {
|
|
2001
|
+
fail();
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
1900
2004
|
|
|
1901
2005
|
/**
|
|
1902
2006
|
* @license
|
|
@@ -2137,7 +2241,7 @@ class PersistencePromise {
|
|
|
2137
2241
|
*/
|
|
2138
2242
|
// References to `window` are guarded by SimpleDb.isAvailable()
|
|
2139
2243
|
/* eslint-disable no-restricted-globals */
|
|
2140
|
-
const LOG_TAG$
|
|
2244
|
+
const LOG_TAG$h = 'SimpleDb';
|
|
2141
2245
|
/**
|
|
2142
2246
|
* The maximum number of retry attempts for an IndexedDb transaction that fails
|
|
2143
2247
|
* with a DOMException.
|
|
@@ -2188,7 +2292,7 @@ class SimpleDbTransaction {
|
|
|
2188
2292
|
this.completionDeferred.reject(error);
|
|
2189
2293
|
}
|
|
2190
2294
|
if (!this.aborted) {
|
|
2191
|
-
logDebug(LOG_TAG$
|
|
2295
|
+
logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
|
|
2192
2296
|
this.aborted = true;
|
|
2193
2297
|
this.transaction.abort();
|
|
2194
2298
|
}
|
|
@@ -2251,7 +2355,7 @@ class SimpleDb {
|
|
|
2251
2355
|
}
|
|
2252
2356
|
/** Deletes the specified database. */
|
|
2253
2357
|
static delete(name) {
|
|
2254
|
-
logDebug(LOG_TAG$
|
|
2358
|
+
logDebug(LOG_TAG$h, 'Removing database:', name);
|
|
2255
2359
|
return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
|
|
2256
2360
|
}
|
|
2257
2361
|
/** Returns true if IndexedDB is available in the current environment. */
|
|
@@ -2330,7 +2434,7 @@ class SimpleDb {
|
|
|
2330
2434
|
*/
|
|
2331
2435
|
async ensureDb(action) {
|
|
2332
2436
|
if (!this.db) {
|
|
2333
|
-
logDebug(LOG_TAG$
|
|
2437
|
+
logDebug(LOG_TAG$h, 'Opening database:', this.name);
|
|
2334
2438
|
this.db = await new Promise((resolve, reject) => {
|
|
2335
2439
|
// TODO(mikelehen): Investigate browser compatibility.
|
|
2336
2440
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
|
@@ -2366,12 +2470,12 @@ class SimpleDb {
|
|
|
2366
2470
|
}
|
|
2367
2471
|
};
|
|
2368
2472
|
request.onupgradeneeded = (event) => {
|
|
2369
|
-
logDebug(LOG_TAG$
|
|
2473
|
+
logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
|
|
2370
2474
|
const db = event.target.result;
|
|
2371
2475
|
this.schemaConverter
|
|
2372
2476
|
.createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
|
|
2373
2477
|
.next(() => {
|
|
2374
|
-
logDebug(LOG_TAG$
|
|
2478
|
+
logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
|
|
2375
2479
|
});
|
|
2376
2480
|
};
|
|
2377
2481
|
});
|
|
@@ -2429,7 +2533,7 @@ class SimpleDb {
|
|
|
2429
2533
|
// exception is wrapped in a generic error by our async/await handling.
|
|
2430
2534
|
const retryable = error.name !== 'FirebaseError' &&
|
|
2431
2535
|
attemptNumber < TRANSACTION_RETRY_COUNT;
|
|
2432
|
-
logDebug(LOG_TAG$
|
|
2536
|
+
logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
|
|
2433
2537
|
this.close();
|
|
2434
2538
|
if (!retryable) {
|
|
2435
2539
|
return Promise.reject(error);
|
|
@@ -2516,11 +2620,11 @@ class SimpleDbStore {
|
|
|
2516
2620
|
put(keyOrValue, value) {
|
|
2517
2621
|
let request;
|
|
2518
2622
|
if (value !== undefined) {
|
|
2519
|
-
logDebug(LOG_TAG$
|
|
2623
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
|
|
2520
2624
|
request = this.store.put(value, keyOrValue);
|
|
2521
2625
|
}
|
|
2522
2626
|
else {
|
|
2523
|
-
logDebug(LOG_TAG$
|
|
2627
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
|
|
2524
2628
|
request = this.store.put(keyOrValue);
|
|
2525
2629
|
}
|
|
2526
2630
|
return wrapRequest(request);
|
|
@@ -2533,7 +2637,7 @@ class SimpleDbStore {
|
|
|
2533
2637
|
* @returns The key of the value to add.
|
|
2534
2638
|
*/
|
|
2535
2639
|
add(value) {
|
|
2536
|
-
logDebug(LOG_TAG$
|
|
2640
|
+
logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
|
|
2537
2641
|
const request = this.store.add(value);
|
|
2538
2642
|
return wrapRequest(request);
|
|
2539
2643
|
}
|
|
@@ -2553,12 +2657,12 @@ class SimpleDbStore {
|
|
|
2553
2657
|
if (result === undefined) {
|
|
2554
2658
|
result = null;
|
|
2555
2659
|
}
|
|
2556
|
-
logDebug(LOG_TAG$
|
|
2660
|
+
logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
|
|
2557
2661
|
return result;
|
|
2558
2662
|
});
|
|
2559
2663
|
}
|
|
2560
2664
|
delete(key) {
|
|
2561
|
-
logDebug(LOG_TAG$
|
|
2665
|
+
logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
|
|
2562
2666
|
const request = this.store.delete(key);
|
|
2563
2667
|
return wrapRequest(request);
|
|
2564
2668
|
}
|
|
@@ -2569,21 +2673,37 @@ class SimpleDbStore {
|
|
|
2569
2673
|
* Returns the number of rows in the store.
|
|
2570
2674
|
*/
|
|
2571
2675
|
count() {
|
|
2572
|
-
logDebug(LOG_TAG$
|
|
2676
|
+
logDebug(LOG_TAG$h, 'COUNT', this.store.name);
|
|
2573
2677
|
const request = this.store.count();
|
|
2574
2678
|
return wrapRequest(request);
|
|
2575
2679
|
}
|
|
2576
2680
|
loadAll(indexOrRange, range) {
|
|
2577
|
-
const
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
return
|
|
2583
|
-
|
|
2681
|
+
const iterateOptions = this.options(indexOrRange, range);
|
|
2682
|
+
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
|
|
2683
|
+
// 20% faster. Unfortunately, getAll() does not support custom indices.
|
|
2684
|
+
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
|
|
2685
|
+
const request = this.store.getAll(iterateOptions.range);
|
|
2686
|
+
return new PersistencePromise((resolve, reject) => {
|
|
2687
|
+
request.onerror = (event) => {
|
|
2688
|
+
reject(event.target.error);
|
|
2689
|
+
};
|
|
2690
|
+
request.onsuccess = (event) => {
|
|
2691
|
+
resolve(event.target.result);
|
|
2692
|
+
};
|
|
2693
|
+
});
|
|
2694
|
+
}
|
|
2695
|
+
else {
|
|
2696
|
+
const cursor = this.cursor(iterateOptions);
|
|
2697
|
+
const results = [];
|
|
2698
|
+
return this.iterateCursor(cursor, (key, value) => {
|
|
2699
|
+
results.push(value);
|
|
2700
|
+
}).next(() => {
|
|
2701
|
+
return results;
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2584
2704
|
}
|
|
2585
2705
|
deleteAll(indexOrRange, range) {
|
|
2586
|
-
logDebug(LOG_TAG$
|
|
2706
|
+
logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
|
|
2587
2707
|
const options = this.options(indexOrRange, range);
|
|
2588
2708
|
options.keysOnly = false;
|
|
2589
2709
|
const cursor = this.cursor(options);
|
|
@@ -3606,11 +3726,22 @@ class DocumentKey {
|
|
|
3606
3726
|
static empty() {
|
|
3607
3727
|
return new DocumentKey(ResourcePath.emptyPath());
|
|
3608
3728
|
}
|
|
3729
|
+
get collectionGroup() {
|
|
3730
|
+
return this.path.popLast().lastSegment();
|
|
3731
|
+
}
|
|
3609
3732
|
/** Returns true if the document is in the specified collectionId. */
|
|
3610
3733
|
hasCollectionId(collectionId) {
|
|
3611
3734
|
return (this.path.length >= 2 &&
|
|
3612
3735
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3613
3736
|
}
|
|
3737
|
+
/** Returns the collection group (i.e. the name of the parent collection) for this key. */
|
|
3738
|
+
getCollectionGroup() {
|
|
3739
|
+
return this.path.get(this.path.length - 2);
|
|
3740
|
+
}
|
|
3741
|
+
/** Returns the fully qualified path to the parent collection. */
|
|
3742
|
+
getCollectionPath() {
|
|
3743
|
+
return this.path.popLast();
|
|
3744
|
+
}
|
|
3614
3745
|
isEqual(other) {
|
|
3615
3746
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3616
3747
|
}
|
|
@@ -3650,6 +3781,13 @@ class DocumentKey {
|
|
|
3650
3781
|
* See the License for the specific language governing permissions and
|
|
3651
3782
|
* limitations under the License.
|
|
3652
3783
|
*/
|
|
3784
|
+
const MAX_VALUE = {
|
|
3785
|
+
mapValue: {
|
|
3786
|
+
fields: {
|
|
3787
|
+
'__type__': { stringValue: '__max___' }
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3653
3791
|
/** Extracts the backend's type order for the provided value. */
|
|
3654
3792
|
function typeOrder(value) {
|
|
3655
3793
|
if ('nullValue' in value) {
|
|
@@ -4059,6 +4197,10 @@ function deepClone(source) {
|
|
|
4059
4197
|
else {
|
|
4060
4198
|
return Object.assign({}, source);
|
|
4061
4199
|
}
|
|
4200
|
+
}
|
|
4201
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4202
|
+
function isMaxValue(value) {
|
|
4203
|
+
return valueEquals(value, MAX_VALUE);
|
|
4062
4204
|
}
|
|
4063
4205
|
|
|
4064
4206
|
/**
|
|
@@ -4434,17 +4576,25 @@ class FieldIndex {
|
|
|
4434
4576
|
/** The collection ID this index applies to. */
|
|
4435
4577
|
collectionGroup,
|
|
4436
4578
|
/** The field segments for this index. */
|
|
4437
|
-
|
|
4579
|
+
fields,
|
|
4438
4580
|
/** Shows how up-to-date the index is for the current user. */
|
|
4439
4581
|
indexState) {
|
|
4440
4582
|
this.indexId = indexId;
|
|
4441
4583
|
this.collectionGroup = collectionGroup;
|
|
4442
|
-
this.
|
|
4584
|
+
this.fields = fields;
|
|
4443
4585
|
this.indexState = indexState;
|
|
4444
4586
|
}
|
|
4445
4587
|
}
|
|
4446
4588
|
/** An ID for an index that has not yet been added to persistence. */
|
|
4447
4589
|
FieldIndex.UNKNOWN_ID = -1;
|
|
4590
|
+
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
4591
|
+
function fieldIndexGetArraySegment(fieldIndex) {
|
|
4592
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
4593
|
+
}
|
|
4594
|
+
/** Returns all directional (ascending/descending) segments for this index. */
|
|
4595
|
+
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4596
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4597
|
+
}
|
|
4448
4598
|
/** An index component consisting of field path and index type. */
|
|
4449
4599
|
class IndexSegment {
|
|
4450
4600
|
constructor(
|
|
@@ -5861,6 +6011,72 @@ class SortedSetIterator {
|
|
|
5861
6011
|
hasNext() {
|
|
5862
6012
|
return this.iter.hasNext();
|
|
5863
6013
|
}
|
|
6014
|
+
}
|
|
6015
|
+
/**
|
|
6016
|
+
* Compares two sorted sets for equality using their natural ordering. The
|
|
6017
|
+
* method computes the intersection and invokes `onAdd` for every element that
|
|
6018
|
+
* is in `after` but not `before`. `onRemove` is invoked for every element in
|
|
6019
|
+
* `before` but missing from `after`.
|
|
6020
|
+
*
|
|
6021
|
+
* The method creates a copy of both `before` and `after` and runs in O(n log
|
|
6022
|
+
* n), where n is the size of the two lists.
|
|
6023
|
+
*
|
|
6024
|
+
* @param before - The elements that exist in the original set.
|
|
6025
|
+
* @param after - The elements to diff against the original set.
|
|
6026
|
+
* @param comparator - The comparator for the elements in before and after.
|
|
6027
|
+
* @param onAdd - A function to invoke for every element that is part of `
|
|
6028
|
+
* after` but not `before`.
|
|
6029
|
+
* @param onRemove - A function to invoke for every element that is part of
|
|
6030
|
+
* `before` but not `after`.
|
|
6031
|
+
*/
|
|
6032
|
+
function diffSortedSets(before, after, comparator, onAdd, onRemove) {
|
|
6033
|
+
const beforeIt = before.getIterator();
|
|
6034
|
+
const afterIt = after.getIterator();
|
|
6035
|
+
let beforeValue = advanceIterator(beforeIt);
|
|
6036
|
+
let afterValue = advanceIterator(afterIt);
|
|
6037
|
+
// Walk through the two sets at the same time, using the ordering defined by
|
|
6038
|
+
// `comparator`.
|
|
6039
|
+
while (beforeValue || afterValue) {
|
|
6040
|
+
let added = false;
|
|
6041
|
+
let removed = false;
|
|
6042
|
+
if (beforeValue && afterValue) {
|
|
6043
|
+
const cmp = comparator(beforeValue, afterValue);
|
|
6044
|
+
if (cmp < 0) {
|
|
6045
|
+
// The element was removed if the next element in our ordered
|
|
6046
|
+
// walkthrough is only in `before`.
|
|
6047
|
+
removed = true;
|
|
6048
|
+
}
|
|
6049
|
+
else if (cmp > 0) {
|
|
6050
|
+
// The element was added if the next element in our ordered walkthrough
|
|
6051
|
+
// is only in `after`.
|
|
6052
|
+
added = true;
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
else if (beforeValue != null) {
|
|
6056
|
+
removed = true;
|
|
6057
|
+
}
|
|
6058
|
+
else {
|
|
6059
|
+
added = true;
|
|
6060
|
+
}
|
|
6061
|
+
if (added) {
|
|
6062
|
+
onAdd(afterValue);
|
|
6063
|
+
afterValue = advanceIterator(afterIt);
|
|
6064
|
+
}
|
|
6065
|
+
else if (removed) {
|
|
6066
|
+
onRemove(beforeValue);
|
|
6067
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6068
|
+
}
|
|
6069
|
+
else {
|
|
6070
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6071
|
+
afterValue = advanceIterator(afterIt);
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
/**
|
|
6076
|
+
* Returns the next element from the iterator or `undefined` if none available.
|
|
6077
|
+
*/
|
|
6078
|
+
function advanceIterator(it) {
|
|
6079
|
+
return it.hasNext() ? it.getNext() : undefined;
|
|
5864
6080
|
}
|
|
5865
6081
|
|
|
5866
6082
|
/**
|
|
@@ -6699,6 +6915,47 @@ class MutationBatchResult {
|
|
|
6699
6915
|
}
|
|
6700
6916
|
}
|
|
6701
6917
|
|
|
6918
|
+
/**
|
|
6919
|
+
* @license
|
|
6920
|
+
* Copyright 2022 Google LLC
|
|
6921
|
+
*
|
|
6922
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6923
|
+
* you may not use this file except in compliance with the License.
|
|
6924
|
+
* You may obtain a copy of the License at
|
|
6925
|
+
*
|
|
6926
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
6927
|
+
*
|
|
6928
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
6929
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
6930
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6931
|
+
* See the License for the specific language governing permissions and
|
|
6932
|
+
* limitations under the License.
|
|
6933
|
+
*/
|
|
6934
|
+
/**
|
|
6935
|
+
* Representation of an overlay computed by Firestore.
|
|
6936
|
+
*
|
|
6937
|
+
* Holds information about a mutation and the largest batch id in Firestore when
|
|
6938
|
+
* the mutation was created.
|
|
6939
|
+
*/
|
|
6940
|
+
class Overlay {
|
|
6941
|
+
constructor(largestBatchId, mutation) {
|
|
6942
|
+
this.largestBatchId = largestBatchId;
|
|
6943
|
+
this.mutation = mutation;
|
|
6944
|
+
}
|
|
6945
|
+
getKey() {
|
|
6946
|
+
return this.mutation.key;
|
|
6947
|
+
}
|
|
6948
|
+
isEqual(other) {
|
|
6949
|
+
return other !== null && this.mutation === other.mutation;
|
|
6950
|
+
}
|
|
6951
|
+
toString() {
|
|
6952
|
+
return `Overlay{
|
|
6953
|
+
largestBatchId: ${this.largestBatchId},
|
|
6954
|
+
mutation: ${this.mutation.toString()}
|
|
6955
|
+
}`;
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
|
|
6702
6959
|
/**
|
|
6703
6960
|
* @license
|
|
6704
6961
|
* Copyright 2017 Google LLC
|
|
@@ -7171,7 +7428,7 @@ class TargetState {
|
|
|
7171
7428
|
this._current = true;
|
|
7172
7429
|
}
|
|
7173
7430
|
}
|
|
7174
|
-
const LOG_TAG$
|
|
7431
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
7175
7432
|
/**
|
|
7176
7433
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
7177
7434
|
*/
|
|
@@ -7466,7 +7723,7 @@ class WatchChangeAggregator {
|
|
|
7466
7723
|
isActiveTarget(targetId) {
|
|
7467
7724
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7468
7725
|
if (!targetActive) {
|
|
7469
|
-
logDebug(LOG_TAG$
|
|
7726
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7470
7727
|
}
|
|
7471
7728
|
return targetActive;
|
|
7472
7729
|
}
|
|
@@ -8700,6 +8957,37 @@ function fromBundleMetadata(metadata) {
|
|
|
8700
8957
|
version: metadata.version,
|
|
8701
8958
|
createTime: fromVersion(metadata.createTime)
|
|
8702
8959
|
};
|
|
8960
|
+
}
|
|
8961
|
+
/** Encodes a DbDocumentOverlay object to an Overlay model object. */
|
|
8962
|
+
function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
|
|
8963
|
+
return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
|
|
8964
|
+
}
|
|
8965
|
+
/** Decodes an Overlay model object into a DbDocumentOverlay object. */
|
|
8966
|
+
function toDbDocumentOverlay(localSerializer, userId, overlay) {
|
|
8967
|
+
const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
|
|
8968
|
+
return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
|
|
8969
|
+
}
|
|
8970
|
+
/**
|
|
8971
|
+
* Returns the DbDocumentOverlayKey corresponding to the given user and
|
|
8972
|
+
* document key.
|
|
8973
|
+
*/
|
|
8974
|
+
function toDbDocumentOverlayKey(userId, docKey) {
|
|
8975
|
+
const docId = docKey.path.lastSegment();
|
|
8976
|
+
const collectionPath = encodeResourcePath(docKey.path.popLast());
|
|
8977
|
+
return [userId, collectionPath, docId];
|
|
8978
|
+
}
|
|
8979
|
+
function toDbIndexConfiguration(index) {
|
|
8980
|
+
return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
|
|
8981
|
+
}
|
|
8982
|
+
function fromDbIndexConfiguration(index, state) {
|
|
8983
|
+
const decodedState = state
|
|
8984
|
+
? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
|
|
8985
|
+
: IndexState.empty();
|
|
8986
|
+
const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
|
|
8987
|
+
return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
|
|
8988
|
+
}
|
|
8989
|
+
function toDbIndexState(indexId, user, sequenceNumber, offset) {
|
|
8990
|
+
return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
|
|
8703
8991
|
}
|
|
8704
8992
|
|
|
8705
8993
|
/**
|
|
@@ -8761,7 +9049,7 @@ function namedQueriesStore(txn) {
|
|
|
8761
9049
|
|
|
8762
9050
|
/**
|
|
8763
9051
|
* @license
|
|
8764
|
-
* Copyright
|
|
9052
|
+
* Copyright 2022 Google LLC
|
|
8765
9053
|
*
|
|
8766
9054
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8767
9055
|
* you may not use this file except in compliance with the License.
|
|
@@ -8776,87 +9064,278 @@ function namedQueriesStore(txn) {
|
|
|
8776
9064
|
* limitations under the License.
|
|
8777
9065
|
*/
|
|
8778
9066
|
/**
|
|
8779
|
-
*
|
|
9067
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
8780
9068
|
*/
|
|
8781
|
-
class
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
|
|
8788
|
-
|
|
8789
|
-
getCollectionParents(transaction, collectionId) {
|
|
8790
|
-
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8791
|
-
}
|
|
8792
|
-
addFieldIndex(transaction, index) {
|
|
8793
|
-
// Field indices are not supported with memory persistence.
|
|
8794
|
-
return PersistencePromise.resolve();
|
|
9069
|
+
class IndexedDbDocumentOverlayCache {
|
|
9070
|
+
/**
|
|
9071
|
+
* @param serializer - The document serializer.
|
|
9072
|
+
* @param userId - The userId for which we are accessing overlays.
|
|
9073
|
+
*/
|
|
9074
|
+
constructor(serializer, userId) {
|
|
9075
|
+
this.serializer = serializer;
|
|
9076
|
+
this.userId = userId;
|
|
8795
9077
|
}
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
return
|
|
9078
|
+
static forUser(serializer, user) {
|
|
9079
|
+
const userId = user.uid || '';
|
|
9080
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
8799
9081
|
}
|
|
8800
|
-
|
|
8801
|
-
|
|
8802
|
-
|
|
9082
|
+
getOverlay(transaction, key) {
|
|
9083
|
+
return documentOverlayStore(transaction)
|
|
9084
|
+
.get(toDbDocumentOverlayKey(this.userId, key))
|
|
9085
|
+
.next(dbOverlay => {
|
|
9086
|
+
if (dbOverlay) {
|
|
9087
|
+
return fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9088
|
+
}
|
|
9089
|
+
return null;
|
|
9090
|
+
});
|
|
8803
9091
|
}
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
9092
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
9093
|
+
const promises = [];
|
|
9094
|
+
overlays.forEach(mutation => {
|
|
9095
|
+
const overlay = new Overlay(largestBatchId, mutation);
|
|
9096
|
+
promises.push(this.saveOverlay(transaction, overlay));
|
|
9097
|
+
});
|
|
9098
|
+
return PersistencePromise.waitFor(promises);
|
|
8807
9099
|
}
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
9100
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
9101
|
+
const collectionPaths = new Set();
|
|
9102
|
+
// Get the set of unique collection paths.
|
|
9103
|
+
documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
|
|
9104
|
+
const promises = [];
|
|
9105
|
+
collectionPaths.forEach(collectionPath => {
|
|
9106
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
|
|
9107
|
+
/*lowerOpen=*/ false,
|
|
9108
|
+
/*upperOpen=*/ true);
|
|
9109
|
+
promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
|
|
9110
|
+
});
|
|
9111
|
+
return PersistencePromise.waitFor(promises);
|
|
8811
9112
|
}
|
|
8812
|
-
|
|
8813
|
-
|
|
8814
|
-
|
|
9113
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
9114
|
+
const result = new Map();
|
|
9115
|
+
const collectionPath = encodeResourcePath(collection);
|
|
9116
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9117
|
+
// is not inclusive.
|
|
9118
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
|
|
9119
|
+
/*lowerOpen=*/ true);
|
|
9120
|
+
return documentOverlayStore(transaction)
|
|
9121
|
+
.loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
|
|
9122
|
+
.next(dbOverlays => {
|
|
9123
|
+
for (const dbOverlay of dbOverlays) {
|
|
9124
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9125
|
+
result.set(overlay.getKey(), overlay);
|
|
9126
|
+
}
|
|
9127
|
+
return result;
|
|
9128
|
+
});
|
|
8815
9129
|
}
|
|
8816
|
-
|
|
8817
|
-
|
|
8818
|
-
|
|
9130
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
9131
|
+
const result = new Map();
|
|
9132
|
+
let currentBatchId = undefined;
|
|
9133
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9134
|
+
// is not inclusive.
|
|
9135
|
+
const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
|
|
9136
|
+
/*lowerOpen=*/ true);
|
|
9137
|
+
return documentOverlayStore(transaction)
|
|
9138
|
+
.iterate({
|
|
9139
|
+
index: DbDocumentOverlay.collectionGroupOverlayIndex,
|
|
9140
|
+
range
|
|
9141
|
+
}, (_, dbOverlay, control) => {
|
|
9142
|
+
// We do not want to return partial batch overlays, even if the size
|
|
9143
|
+
// of the result set exceeds the given `count` argument. Therefore, we
|
|
9144
|
+
// continue to aggregate results even after the result size exceeds
|
|
9145
|
+
// `count` if there are more overlays from the `currentBatchId`.
|
|
9146
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9147
|
+
if (result.size < count ||
|
|
9148
|
+
overlay.largestBatchId === currentBatchId) {
|
|
9149
|
+
result.set(overlay.getKey(), overlay);
|
|
9150
|
+
currentBatchId = overlay.largestBatchId;
|
|
9151
|
+
}
|
|
9152
|
+
else {
|
|
9153
|
+
control.done();
|
|
9154
|
+
}
|
|
9155
|
+
})
|
|
9156
|
+
.next(() => result);
|
|
8819
9157
|
}
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
return PersistencePromise.resolve();
|
|
9158
|
+
saveOverlay(transaction, overlay) {
|
|
9159
|
+
return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
|
|
8823
9160
|
}
|
|
8824
9161
|
}
|
|
8825
9162
|
/**
|
|
8826
|
-
*
|
|
8827
|
-
* Also used for in-memory caching by IndexedDbIndexManager and initial index population
|
|
8828
|
-
* in indexeddb_schema.ts
|
|
9163
|
+
* Helper to get a typed SimpleDbStore for the document overlay object store.
|
|
8829
9164
|
*/
|
|
8830
|
-
|
|
8831
|
-
|
|
8832
|
-
|
|
9165
|
+
function documentOverlayStore(txn) {
|
|
9166
|
+
return getStore(txn, DbDocumentOverlay.store);
|
|
9167
|
+
}
|
|
9168
|
+
|
|
9169
|
+
/**
|
|
9170
|
+
* @license
|
|
9171
|
+
* Copyright 2021 Google LLC
|
|
9172
|
+
*
|
|
9173
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9174
|
+
* you may not use this file except in compliance with the License.
|
|
9175
|
+
* You may obtain a copy of the License at
|
|
9176
|
+
*
|
|
9177
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9178
|
+
*
|
|
9179
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9180
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9181
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9182
|
+
* See the License for the specific language governing permissions and
|
|
9183
|
+
* limitations under the License.
|
|
9184
|
+
*/
|
|
9185
|
+
// Note: This code is copied from the backend. Code that is not used by
|
|
9186
|
+
// Firestore was removed.
|
|
9187
|
+
const INDEX_TYPE_NULL = 5;
|
|
9188
|
+
const INDEX_TYPE_BOOLEAN = 10;
|
|
9189
|
+
const INDEX_TYPE_NAN = 13;
|
|
9190
|
+
const INDEX_TYPE_NUMBER = 15;
|
|
9191
|
+
const INDEX_TYPE_TIMESTAMP = 20;
|
|
9192
|
+
const INDEX_TYPE_STRING = 25;
|
|
9193
|
+
const INDEX_TYPE_BLOB = 30;
|
|
9194
|
+
const INDEX_TYPE_REFERENCE = 37;
|
|
9195
|
+
const INDEX_TYPE_GEOPOINT = 45;
|
|
9196
|
+
const INDEX_TYPE_ARRAY = 50;
|
|
9197
|
+
const INDEX_TYPE_MAP = 55;
|
|
9198
|
+
const INDEX_TYPE_REFERENCE_SEGMENT = 60;
|
|
9199
|
+
// A terminator that indicates that a truncatable value was not truncated.
|
|
9200
|
+
// This must be smaller than all other type labels.
|
|
9201
|
+
const NOT_TRUNCATED = 2;
|
|
9202
|
+
/** Firestore index value writer. */
|
|
9203
|
+
class FirestoreIndexValueWriter {
|
|
9204
|
+
constructor() { }
|
|
9205
|
+
// The write methods below short-circuit writing terminators for values
|
|
9206
|
+
// containing a (terminating) truncated value.
|
|
9207
|
+
//
|
|
9208
|
+
// As an example, consider the resulting encoding for:
|
|
9209
|
+
//
|
|
9210
|
+
// ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
|
|
9211
|
+
// ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
|
|
9212
|
+
// ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
|
|
9213
|
+
/** Writes an index value. */
|
|
9214
|
+
writeIndexValue(value, encoder) {
|
|
9215
|
+
this.writeIndexValueAux(value, encoder);
|
|
9216
|
+
// Write separator to split index values
|
|
9217
|
+
// (see go/firestore-storage-format#encodings).
|
|
9218
|
+
encoder.writeInfinity();
|
|
9219
|
+
}
|
|
9220
|
+
writeIndexValueAux(indexValue, encoder) {
|
|
9221
|
+
if ('nullValue' in indexValue) {
|
|
9222
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
|
|
9223
|
+
}
|
|
9224
|
+
else if ('booleanValue' in indexValue) {
|
|
9225
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
|
|
9226
|
+
encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
|
|
9227
|
+
}
|
|
9228
|
+
else if ('integerValue' in indexValue) {
|
|
9229
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9230
|
+
encoder.writeNumber(normalizeNumber(indexValue.integerValue));
|
|
9231
|
+
}
|
|
9232
|
+
else if ('doubleValue' in indexValue) {
|
|
9233
|
+
const n = normalizeNumber(indexValue.doubleValue);
|
|
9234
|
+
if (isNaN(n)) {
|
|
9235
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
|
|
9236
|
+
}
|
|
9237
|
+
else {
|
|
9238
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9239
|
+
if (isNegativeZero(n)) {
|
|
9240
|
+
// -0.0, 0 and 0.0 are all considered the same
|
|
9241
|
+
encoder.writeNumber(0.0);
|
|
9242
|
+
}
|
|
9243
|
+
else {
|
|
9244
|
+
encoder.writeNumber(n);
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
else if ('timestampValue' in indexValue) {
|
|
9249
|
+
const timestamp = indexValue.timestampValue;
|
|
9250
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
|
|
9251
|
+
if (typeof timestamp === 'string') {
|
|
9252
|
+
encoder.writeString(timestamp);
|
|
9253
|
+
}
|
|
9254
|
+
else {
|
|
9255
|
+
encoder.writeString(`${timestamp.seconds || ''}`);
|
|
9256
|
+
encoder.writeNumber(timestamp.nanos || 0);
|
|
9257
|
+
}
|
|
9258
|
+
}
|
|
9259
|
+
else if ('stringValue' in indexValue) {
|
|
9260
|
+
this.writeIndexString(indexValue.stringValue, encoder);
|
|
9261
|
+
this.writeTruncationMarker(encoder);
|
|
9262
|
+
}
|
|
9263
|
+
else if ('bytesValue' in indexValue) {
|
|
9264
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
|
|
9265
|
+
encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
|
|
9266
|
+
this.writeTruncationMarker(encoder);
|
|
9267
|
+
}
|
|
9268
|
+
else if ('referenceValue' in indexValue) {
|
|
9269
|
+
this.writeIndexEntityRef(indexValue.referenceValue, encoder);
|
|
9270
|
+
}
|
|
9271
|
+
else if ('geoPointValue' in indexValue) {
|
|
9272
|
+
const geoPoint = indexValue.geoPointValue;
|
|
9273
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
|
|
9274
|
+
encoder.writeNumber(geoPoint.latitude || 0);
|
|
9275
|
+
encoder.writeNumber(geoPoint.longitude || 0);
|
|
9276
|
+
}
|
|
9277
|
+
else if ('mapValue' in indexValue) {
|
|
9278
|
+
if (isMaxValue(indexValue)) {
|
|
9279
|
+
this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
|
|
9280
|
+
}
|
|
9281
|
+
else {
|
|
9282
|
+
this.writeIndexMap(indexValue.mapValue, encoder);
|
|
9283
|
+
this.writeTruncationMarker(encoder);
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
9286
|
+
else if ('arrayValue' in indexValue) {
|
|
9287
|
+
this.writeIndexArray(indexValue.arrayValue, encoder);
|
|
9288
|
+
this.writeTruncationMarker(encoder);
|
|
9289
|
+
}
|
|
9290
|
+
else {
|
|
9291
|
+
fail();
|
|
9292
|
+
}
|
|
8833
9293
|
}
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
8837
|
-
const parentPath = collectionPath.popLast();
|
|
8838
|
-
const existingParents = this.index[collectionId] ||
|
|
8839
|
-
new SortedSet(ResourcePath.comparator);
|
|
8840
|
-
const added = !existingParents.has(parentPath);
|
|
8841
|
-
this.index[collectionId] = existingParents.add(parentPath);
|
|
8842
|
-
return added;
|
|
9294
|
+
writeIndexString(stringIndexValue, encoder) {
|
|
9295
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
|
|
9296
|
+
this.writeUnlabeledIndexString(stringIndexValue, encoder);
|
|
8843
9297
|
}
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
const parentPath = collectionPath.popLast();
|
|
8847
|
-
const existingParents = this.index[collectionId];
|
|
8848
|
-
return existingParents && existingParents.has(parentPath);
|
|
9298
|
+
writeUnlabeledIndexString(stringIndexValue, encoder) {
|
|
9299
|
+
encoder.writeString(stringIndexValue);
|
|
8849
9300
|
}
|
|
8850
|
-
|
|
8851
|
-
const
|
|
8852
|
-
|
|
8853
|
-
|
|
9301
|
+
writeIndexMap(mapIndexValue, encoder) {
|
|
9302
|
+
const map = mapIndexValue.fields || {};
|
|
9303
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
|
|
9304
|
+
for (const key of Object.keys(map)) {
|
|
9305
|
+
this.writeIndexString(key, encoder);
|
|
9306
|
+
this.writeIndexValueAux(map[key], encoder);
|
|
9307
|
+
}
|
|
8854
9308
|
}
|
|
8855
|
-
|
|
9309
|
+
writeIndexArray(arrayIndexValue, encoder) {
|
|
9310
|
+
const values = arrayIndexValue.values || [];
|
|
9311
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
|
|
9312
|
+
for (const element of values) {
|
|
9313
|
+
this.writeIndexValueAux(element, encoder);
|
|
9314
|
+
}
|
|
9315
|
+
}
|
|
9316
|
+
writeIndexEntityRef(referenceValue, encoder) {
|
|
9317
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
|
|
9318
|
+
const path = DocumentKey.fromName(referenceValue).path;
|
|
9319
|
+
path.forEach(segment => {
|
|
9320
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
|
|
9321
|
+
this.writeUnlabeledIndexString(segment, encoder);
|
|
9322
|
+
});
|
|
9323
|
+
}
|
|
9324
|
+
writeValueTypeLabel(encoder, typeOrder) {
|
|
9325
|
+
encoder.writeNumber(typeOrder);
|
|
9326
|
+
}
|
|
9327
|
+
writeTruncationMarker(encoder) {
|
|
9328
|
+
// While the SDK does not implement truncation, the truncation marker is
|
|
9329
|
+
// used to terminate all variable length values (which are strings, bytes,
|
|
9330
|
+
// references, arrays and maps).
|
|
9331
|
+
encoder.writeNumber(NOT_TRUNCATED);
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
|
|
8856
9335
|
|
|
8857
9336
|
/**
|
|
8858
9337
|
* @license
|
|
8859
|
-
* Copyright
|
|
9338
|
+
* Copyright 2021 Google LLC
|
|
8860
9339
|
*
|
|
8861
9340
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8862
9341
|
* you may not use this file except in compliance with the License.
|
|
@@ -8864,25 +9343,538 @@ class MemoryCollectionParentIndex {
|
|
|
8864
9343
|
*
|
|
8865
9344
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8866
9345
|
*
|
|
8867
|
-
* Unless required by applicable law
|
|
9346
|
+
* Unless required by applicable law | agreed to in writing, software
|
|
8868
9347
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
8869
|
-
* WITHOUT WARRANTIES
|
|
9348
|
+
* WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
|
|
8870
9349
|
* See the License for the specific language governing permissions and
|
|
8871
9350
|
* limitations under the License.
|
|
8872
9351
|
*/
|
|
9352
|
+
/** These constants are taken from the backend. */
|
|
9353
|
+
const MIN_SURROGATE = '\uD800';
|
|
9354
|
+
const MAX_SURROGATE = '\uDBFF';
|
|
9355
|
+
const ESCAPE1 = 0x00;
|
|
9356
|
+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
|
|
9357
|
+
const SEPARATOR = 0x01; // Combined with ESCAPE1
|
|
9358
|
+
const ESCAPE2 = 0xff;
|
|
9359
|
+
const INFINITY = 0xff; // Combined with ESCAPE2
|
|
9360
|
+
const FF_BYTE = 0x00; // Combined with ESCAPE2
|
|
9361
|
+
const LONG_SIZE = 64;
|
|
9362
|
+
const BYTE_SIZE = 8;
|
|
9363
|
+
/**
|
|
9364
|
+
* The default size of the buffer. This is arbitrary, but likely larger than
|
|
9365
|
+
* most index values so that less copies of the underlying buffer will be made.
|
|
9366
|
+
* For large values, a single copy will made to double the buffer length.
|
|
9367
|
+
*/
|
|
9368
|
+
const DEFAULT_BUFFER_SIZE = 1024;
|
|
9369
|
+
/** Converts a JavaScript number to a byte array (using big endian encoding). */
|
|
9370
|
+
function doubleToLongBits(value) {
|
|
9371
|
+
const dv = new DataView(new ArrayBuffer(8));
|
|
9372
|
+
dv.setFloat64(0, value, /* littleEndian= */ false);
|
|
9373
|
+
return new Uint8Array(dv.buffer);
|
|
9374
|
+
}
|
|
9375
|
+
/**
|
|
9376
|
+
* Counts the number of zeros in a byte.
|
|
9377
|
+
*
|
|
9378
|
+
* Visible for testing.
|
|
9379
|
+
*/
|
|
9380
|
+
function numberOfLeadingZerosInByte(x) {
|
|
9381
|
+
if (x === 0) {
|
|
9382
|
+
return 8;
|
|
9383
|
+
}
|
|
9384
|
+
let zeros = 0;
|
|
9385
|
+
if (x >> 4 === 0) {
|
|
9386
|
+
// Test if the first four bits are zero.
|
|
9387
|
+
zeros += 4;
|
|
9388
|
+
x = x << 4;
|
|
9389
|
+
}
|
|
9390
|
+
if (x >> 6 === 0) {
|
|
9391
|
+
// Test if the first two (or next two) bits are zero.
|
|
9392
|
+
zeros += 2;
|
|
9393
|
+
x = x << 2;
|
|
9394
|
+
}
|
|
9395
|
+
if (x >> 7 === 0) {
|
|
9396
|
+
// Test if the remaining bit is zero.
|
|
9397
|
+
zeros += 1;
|
|
9398
|
+
}
|
|
9399
|
+
return zeros;
|
|
9400
|
+
}
|
|
9401
|
+
/** Counts the number of leading zeros in the given byte array. */
|
|
9402
|
+
function numberOfLeadingZeros(bytes) {
|
|
9403
|
+
let leadingZeros = 0;
|
|
9404
|
+
for (let i = 0; i < 8; ++i) {
|
|
9405
|
+
const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
|
|
9406
|
+
leadingZeros += zeros;
|
|
9407
|
+
if (zeros !== 8) {
|
|
9408
|
+
break;
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
return leadingZeros;
|
|
9412
|
+
}
|
|
9413
|
+
/**
|
|
9414
|
+
* Returns the number of bytes required to store "value". Leading zero bytes
|
|
9415
|
+
* are skipped.
|
|
9416
|
+
*/
|
|
9417
|
+
function unsignedNumLength(value) {
|
|
9418
|
+
// This is just the number of bytes for the unsigned representation of the number.
|
|
9419
|
+
const numBits = LONG_SIZE - numberOfLeadingZeros(value);
|
|
9420
|
+
return Math.ceil(numBits / BYTE_SIZE);
|
|
9421
|
+
}
|
|
9422
|
+
/**
|
|
9423
|
+
* OrderedCodeWriter is a minimal-allocation implementation of the writing
|
|
9424
|
+
* behavior defined by the backend.
|
|
9425
|
+
*
|
|
9426
|
+
* The code is ported from its Java counterpart.
|
|
9427
|
+
*/
|
|
9428
|
+
class OrderedCodeWriter {
|
|
9429
|
+
constructor() {
|
|
9430
|
+
this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
|
9431
|
+
this.position = 0;
|
|
9432
|
+
}
|
|
9433
|
+
writeBytesAscending(value) {
|
|
9434
|
+
const it = value[Symbol.iterator]();
|
|
9435
|
+
let byte = it.next();
|
|
9436
|
+
while (!byte.done) {
|
|
9437
|
+
this.writeByteAscending(byte.value);
|
|
9438
|
+
byte = it.next();
|
|
9439
|
+
}
|
|
9440
|
+
this.writeSeparatorAscending();
|
|
9441
|
+
}
|
|
9442
|
+
writeBytesDescending(value) {
|
|
9443
|
+
const it = value[Symbol.iterator]();
|
|
9444
|
+
let byte = it.next();
|
|
9445
|
+
while (!byte.done) {
|
|
9446
|
+
this.writeByteDescending(byte.value);
|
|
9447
|
+
byte = it.next();
|
|
9448
|
+
}
|
|
9449
|
+
this.writeSeparatorDescending();
|
|
9450
|
+
}
|
|
9451
|
+
/** Writes utf8 bytes into this byte sequence, ascending. */
|
|
9452
|
+
writeUtf8Ascending(sequence) {
|
|
9453
|
+
for (const c of sequence) {
|
|
9454
|
+
const charCode = c.charCodeAt(0);
|
|
9455
|
+
if (charCode < 0x80) {
|
|
9456
|
+
this.writeByteAscending(charCode);
|
|
9457
|
+
}
|
|
9458
|
+
else if (charCode < 0x800) {
|
|
9459
|
+
this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
|
|
9460
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9461
|
+
}
|
|
9462
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9463
|
+
this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
|
|
9464
|
+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9465
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9466
|
+
}
|
|
9467
|
+
else {
|
|
9468
|
+
const codePoint = c.codePointAt(0);
|
|
9469
|
+
this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
|
|
9470
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9471
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9472
|
+
this.writeByteAscending(0x80 | (0x3f & codePoint));
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
this.writeSeparatorAscending();
|
|
9476
|
+
}
|
|
9477
|
+
/** Writes utf8 bytes into this byte sequence, descending */
|
|
9478
|
+
writeUtf8Descending(sequence) {
|
|
9479
|
+
for (const c of sequence) {
|
|
9480
|
+
const charCode = c.charCodeAt(0);
|
|
9481
|
+
if (charCode < 0x80) {
|
|
9482
|
+
this.writeByteDescending(charCode);
|
|
9483
|
+
}
|
|
9484
|
+
else if (charCode < 0x800) {
|
|
9485
|
+
this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
|
|
9486
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9487
|
+
}
|
|
9488
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9489
|
+
this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
|
|
9490
|
+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9491
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9492
|
+
}
|
|
9493
|
+
else {
|
|
9494
|
+
const codePoint = c.codePointAt(0);
|
|
9495
|
+
this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
|
|
9496
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9497
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9498
|
+
this.writeByteDescending(0x80 | (0x3f & codePoint));
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
this.writeSeparatorDescending();
|
|
9502
|
+
}
|
|
9503
|
+
writeNumberAscending(val) {
|
|
9504
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9505
|
+
// actual value in big-endian format with leading 0 bytes dropped.
|
|
9506
|
+
const value = this.toOrderedBits(val);
|
|
9507
|
+
const len = unsignedNumLength(value);
|
|
9508
|
+
this.ensureAvailable(1 + len);
|
|
9509
|
+
this.buffer[this.position++] = len & 0xff; // Write the length
|
|
9510
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9511
|
+
this.buffer[this.position++] = value[i] & 0xff;
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
writeNumberDescending(val) {
|
|
9515
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9516
|
+
// inverted value in big-endian format with leading 0 bytes dropped.
|
|
9517
|
+
const value = this.toOrderedBits(val);
|
|
9518
|
+
const len = unsignedNumLength(value);
|
|
9519
|
+
this.ensureAvailable(1 + len);
|
|
9520
|
+
this.buffer[this.position++] = ~(len & 0xff); // Write the length
|
|
9521
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9522
|
+
this.buffer[this.position++] = ~(value[i] & 0xff);
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
/**
|
|
9526
|
+
* Writes the "infinity" byte sequence that sorts after all other byte
|
|
9527
|
+
* sequences written in ascending order.
|
|
9528
|
+
*/
|
|
9529
|
+
writeInfinityAscending() {
|
|
9530
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9531
|
+
this.writeEscapedByteAscending(INFINITY);
|
|
9532
|
+
}
|
|
9533
|
+
/**
|
|
9534
|
+
* Writes the "infinity" byte sequence that sorts before all other byte
|
|
9535
|
+
* sequences written in descending order.
|
|
9536
|
+
*/
|
|
9537
|
+
writeInfinityDescending() {
|
|
9538
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9539
|
+
this.writeEscapedByteDescending(INFINITY);
|
|
9540
|
+
}
|
|
9541
|
+
/**
|
|
9542
|
+
* Resets the buffer such that it is the same as when it was newly
|
|
9543
|
+
* constructed.
|
|
9544
|
+
*/
|
|
9545
|
+
reset() {
|
|
9546
|
+
this.position = 0;
|
|
9547
|
+
}
|
|
9548
|
+
seed(encodedBytes) {
|
|
9549
|
+
this.ensureAvailable(encodedBytes.length);
|
|
9550
|
+
this.buffer.set(encodedBytes, this.position);
|
|
9551
|
+
this.position += encodedBytes.length;
|
|
9552
|
+
}
|
|
9553
|
+
/** Makes a copy of the encoded bytes in this buffer. */
|
|
9554
|
+
encodedBytes() {
|
|
9555
|
+
return this.buffer.slice(0, this.position);
|
|
9556
|
+
}
|
|
9557
|
+
/**
|
|
9558
|
+
* Encodes `val` into an encoding so that the order matches the IEEE 754
|
|
9559
|
+
* floating-point comparison results with the following exceptions:
|
|
9560
|
+
* -0.0 < 0.0
|
|
9561
|
+
* all non-NaN < NaN
|
|
9562
|
+
* NaN = NaN
|
|
9563
|
+
*/
|
|
9564
|
+
toOrderedBits(val) {
|
|
9565
|
+
const value = doubleToLongBits(val);
|
|
9566
|
+
// Check if the first bit is set. We use a bit mask since value[0] is
|
|
9567
|
+
// encoded as a number from 0 to 255.
|
|
9568
|
+
const isNegative = (value[0] & 0x80) !== 0;
|
|
9569
|
+
// Revert the two complement to get natural ordering
|
|
9570
|
+
value[0] ^= isNegative ? 0xff : 0x80;
|
|
9571
|
+
for (let i = 1; i < value.length; ++i) {
|
|
9572
|
+
value[i] ^= isNegative ? 0xff : 0x00;
|
|
9573
|
+
}
|
|
9574
|
+
return value;
|
|
9575
|
+
}
|
|
9576
|
+
/** Writes a single byte ascending to the buffer. */
|
|
9577
|
+
writeByteAscending(b) {
|
|
9578
|
+
const masked = b & 0xff;
|
|
9579
|
+
if (masked === ESCAPE1) {
|
|
9580
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9581
|
+
this.writeEscapedByteAscending(NULL_BYTE);
|
|
9582
|
+
}
|
|
9583
|
+
else if (masked === ESCAPE2) {
|
|
9584
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9585
|
+
this.writeEscapedByteAscending(FF_BYTE);
|
|
9586
|
+
}
|
|
9587
|
+
else {
|
|
9588
|
+
this.writeEscapedByteAscending(masked);
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
/** Writes a single byte descending to the buffer. */
|
|
9592
|
+
writeByteDescending(b) {
|
|
9593
|
+
const masked = b & 0xff;
|
|
9594
|
+
if (masked === ESCAPE1) {
|
|
9595
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9596
|
+
this.writeEscapedByteDescending(NULL_BYTE);
|
|
9597
|
+
}
|
|
9598
|
+
else if (masked === ESCAPE2) {
|
|
9599
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9600
|
+
this.writeEscapedByteDescending(FF_BYTE);
|
|
9601
|
+
}
|
|
9602
|
+
else {
|
|
9603
|
+
this.writeEscapedByteDescending(b);
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9606
|
+
writeSeparatorAscending() {
|
|
9607
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9608
|
+
this.writeEscapedByteAscending(SEPARATOR);
|
|
9609
|
+
}
|
|
9610
|
+
writeSeparatorDescending() {
|
|
9611
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9612
|
+
this.writeEscapedByteDescending(SEPARATOR);
|
|
9613
|
+
}
|
|
9614
|
+
writeEscapedByteAscending(b) {
|
|
9615
|
+
this.ensureAvailable(1);
|
|
9616
|
+
this.buffer[this.position++] = b;
|
|
9617
|
+
}
|
|
9618
|
+
writeEscapedByteDescending(b) {
|
|
9619
|
+
this.ensureAvailable(1);
|
|
9620
|
+
this.buffer[this.position++] = ~b;
|
|
9621
|
+
}
|
|
9622
|
+
ensureAvailable(bytes) {
|
|
9623
|
+
const minCapacity = bytes + this.position;
|
|
9624
|
+
if (minCapacity <= this.buffer.length) {
|
|
9625
|
+
return;
|
|
9626
|
+
}
|
|
9627
|
+
// Try doubling.
|
|
9628
|
+
let newLength = this.buffer.length * 2;
|
|
9629
|
+
// Still not big enough? Just allocate the right size.
|
|
9630
|
+
if (newLength < minCapacity) {
|
|
9631
|
+
newLength = minCapacity;
|
|
9632
|
+
}
|
|
9633
|
+
// Create the new buffer.
|
|
9634
|
+
const newBuffer = new Uint8Array(newLength);
|
|
9635
|
+
newBuffer.set(this.buffer); // copy old data
|
|
9636
|
+
this.buffer = newBuffer;
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
|
|
9640
|
+
class AscendingIndexByteEncoder {
|
|
9641
|
+
constructor(orderedCode) {
|
|
9642
|
+
this.orderedCode = orderedCode;
|
|
9643
|
+
}
|
|
9644
|
+
writeBytes(value) {
|
|
9645
|
+
this.orderedCode.writeBytesAscending(value);
|
|
9646
|
+
}
|
|
9647
|
+
writeString(value) {
|
|
9648
|
+
this.orderedCode.writeUtf8Ascending(value);
|
|
9649
|
+
}
|
|
9650
|
+
writeNumber(value) {
|
|
9651
|
+
this.orderedCode.writeNumberAscending(value);
|
|
9652
|
+
}
|
|
9653
|
+
writeInfinity() {
|
|
9654
|
+
this.orderedCode.writeInfinityAscending();
|
|
9655
|
+
}
|
|
9656
|
+
}
|
|
9657
|
+
class DescendingIndexByteEncoder {
|
|
9658
|
+
constructor(orderedCode) {
|
|
9659
|
+
this.orderedCode = orderedCode;
|
|
9660
|
+
}
|
|
9661
|
+
writeBytes(value) {
|
|
9662
|
+
this.orderedCode.writeBytesDescending(value);
|
|
9663
|
+
}
|
|
9664
|
+
writeString(value) {
|
|
9665
|
+
this.orderedCode.writeUtf8Descending(value);
|
|
9666
|
+
}
|
|
9667
|
+
writeNumber(value) {
|
|
9668
|
+
this.orderedCode.writeNumberDescending(value);
|
|
9669
|
+
}
|
|
9670
|
+
writeInfinity() {
|
|
9671
|
+
this.orderedCode.writeInfinityDescending();
|
|
9672
|
+
}
|
|
9673
|
+
}
|
|
9674
|
+
/**
|
|
9675
|
+
* Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
|
|
9676
|
+
* actual encoding.
|
|
9677
|
+
*/
|
|
9678
|
+
class IndexByteEncoder {
|
|
9679
|
+
constructor() {
|
|
9680
|
+
this.orderedCode = new OrderedCodeWriter();
|
|
9681
|
+
this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
|
|
9682
|
+
this.descending = new DescendingIndexByteEncoder(this.orderedCode);
|
|
9683
|
+
}
|
|
9684
|
+
seed(encodedBytes) {
|
|
9685
|
+
this.orderedCode.seed(encodedBytes);
|
|
9686
|
+
}
|
|
9687
|
+
forKind(kind) {
|
|
9688
|
+
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
9689
|
+
}
|
|
9690
|
+
encodedBytes() {
|
|
9691
|
+
return this.orderedCode.encodedBytes();
|
|
9692
|
+
}
|
|
9693
|
+
reset() {
|
|
9694
|
+
this.orderedCode.reset();
|
|
9695
|
+
}
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9698
|
+
/**
|
|
9699
|
+
* @license
|
|
9700
|
+
* Copyright 2022 Google LLC
|
|
9701
|
+
*
|
|
9702
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9703
|
+
* you may not use this file except in compliance with the License.
|
|
9704
|
+
* You may obtain a copy of the License at
|
|
9705
|
+
*
|
|
9706
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9707
|
+
*
|
|
9708
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9709
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9710
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9711
|
+
* See the License for the specific language governing permissions and
|
|
9712
|
+
* limitations under the License.
|
|
9713
|
+
*/
|
|
9714
|
+
/** Represents an index entry saved by the SDK in persisted storage. */
|
|
9715
|
+
class IndexEntry {
|
|
9716
|
+
constructor(indexId, documentKey, arrayValue, directionalValue) {
|
|
9717
|
+
this.indexId = indexId;
|
|
9718
|
+
this.documentKey = documentKey;
|
|
9719
|
+
this.arrayValue = arrayValue;
|
|
9720
|
+
this.directionalValue = directionalValue;
|
|
9721
|
+
}
|
|
9722
|
+
}
|
|
9723
|
+
function indexEntryComparator(left, right) {
|
|
9724
|
+
let cmp = left.indexId - right.indexId;
|
|
9725
|
+
if (cmp !== 0) {
|
|
9726
|
+
return cmp;
|
|
9727
|
+
}
|
|
9728
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9729
|
+
if (cmp !== 0) {
|
|
9730
|
+
return cmp;
|
|
9731
|
+
}
|
|
9732
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9733
|
+
if (cmp !== 0) {
|
|
9734
|
+
return cmp;
|
|
9735
|
+
}
|
|
9736
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
9737
|
+
}
|
|
9738
|
+
function compareByteArrays(left, right) {
|
|
9739
|
+
for (let i = 0; i < left.length && i < right.length; ++i) {
|
|
9740
|
+
const compare = left[i] - right[i];
|
|
9741
|
+
if (compare !== 0) {
|
|
9742
|
+
return compare;
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
return left.length - right.length;
|
|
9746
|
+
}
|
|
9747
|
+
|
|
9748
|
+
/**
|
|
9749
|
+
* @license
|
|
9750
|
+
* Copyright 2019 Google LLC
|
|
9751
|
+
*
|
|
9752
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9753
|
+
* you may not use this file except in compliance with the License.
|
|
9754
|
+
* You may obtain a copy of the License at
|
|
9755
|
+
*
|
|
9756
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9757
|
+
*
|
|
9758
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9759
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9760
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9761
|
+
* See the License for the specific language governing permissions and
|
|
9762
|
+
* limitations under the License.
|
|
9763
|
+
*/
|
|
9764
|
+
/**
|
|
9765
|
+
* An in-memory implementation of IndexManager.
|
|
9766
|
+
*/
|
|
9767
|
+
class MemoryIndexManager {
|
|
9768
|
+
constructor() {
|
|
9769
|
+
this.collectionParentIndex = new MemoryCollectionParentIndex();
|
|
9770
|
+
}
|
|
9771
|
+
addToCollectionParentIndex(transaction, collectionPath) {
|
|
9772
|
+
this.collectionParentIndex.add(collectionPath);
|
|
9773
|
+
return PersistencePromise.resolve();
|
|
9774
|
+
}
|
|
9775
|
+
getCollectionParents(transaction, collectionId) {
|
|
9776
|
+
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
9777
|
+
}
|
|
9778
|
+
addFieldIndex(transaction, index) {
|
|
9779
|
+
// Field indices are not supported with memory persistence.
|
|
9780
|
+
return PersistencePromise.resolve();
|
|
9781
|
+
}
|
|
9782
|
+
deleteFieldIndex(transaction, index) {
|
|
9783
|
+
// Field indices are not supported with memory persistence.
|
|
9784
|
+
return PersistencePromise.resolve();
|
|
9785
|
+
}
|
|
9786
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9787
|
+
// Field indices are not supported with memory persistence.
|
|
9788
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9789
|
+
}
|
|
9790
|
+
getFieldIndex(transaction, target) {
|
|
9791
|
+
// Field indices are not supported with memory persistence.
|
|
9792
|
+
return PersistencePromise.resolve(null);
|
|
9793
|
+
}
|
|
9794
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9795
|
+
// Field indices are not supported with memory persistence.
|
|
9796
|
+
return PersistencePromise.resolve([]);
|
|
9797
|
+
}
|
|
9798
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9799
|
+
// Field indices are not supported with memory persistence.
|
|
9800
|
+
return PersistencePromise.resolve(null);
|
|
9801
|
+
}
|
|
9802
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9803
|
+
// Field indices are not supported with memory persistence.
|
|
9804
|
+
return PersistencePromise.resolve();
|
|
9805
|
+
}
|
|
9806
|
+
updateIndexEntries(transaction, documents) {
|
|
9807
|
+
// Field indices are not supported with memory persistence.
|
|
9808
|
+
return PersistencePromise.resolve();
|
|
9809
|
+
}
|
|
9810
|
+
}
|
|
9811
|
+
/**
|
|
9812
|
+
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
9813
|
+
* Also used for in-memory caching by IndexedDbIndexManager and initial index population
|
|
9814
|
+
* in indexeddb_schema.ts
|
|
9815
|
+
*/
|
|
9816
|
+
class MemoryCollectionParentIndex {
|
|
9817
|
+
constructor() {
|
|
9818
|
+
this.index = {};
|
|
9819
|
+
}
|
|
9820
|
+
// Returns false if the entry already existed.
|
|
9821
|
+
add(collectionPath) {
|
|
9822
|
+
const collectionId = collectionPath.lastSegment();
|
|
9823
|
+
const parentPath = collectionPath.popLast();
|
|
9824
|
+
const existingParents = this.index[collectionId] ||
|
|
9825
|
+
new SortedSet(ResourcePath.comparator);
|
|
9826
|
+
const added = !existingParents.has(parentPath);
|
|
9827
|
+
this.index[collectionId] = existingParents.add(parentPath);
|
|
9828
|
+
return added;
|
|
9829
|
+
}
|
|
9830
|
+
has(collectionPath) {
|
|
9831
|
+
const collectionId = collectionPath.lastSegment();
|
|
9832
|
+
const parentPath = collectionPath.popLast();
|
|
9833
|
+
const existingParents = this.index[collectionId];
|
|
9834
|
+
return existingParents && existingParents.has(parentPath);
|
|
9835
|
+
}
|
|
9836
|
+
getEntries(collectionId) {
|
|
9837
|
+
const parentPaths = this.index[collectionId] ||
|
|
9838
|
+
new SortedSet(ResourcePath.comparator);
|
|
9839
|
+
return parentPaths.toArray();
|
|
9840
|
+
}
|
|
9841
|
+
}
|
|
9842
|
+
|
|
9843
|
+
/**
|
|
9844
|
+
* @license
|
|
9845
|
+
* Copyright 2019 Google LLC
|
|
9846
|
+
*
|
|
9847
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9848
|
+
* you may not use this file except in compliance with the License.
|
|
9849
|
+
* You may obtain a copy of the License at
|
|
9850
|
+
*
|
|
9851
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9852
|
+
*
|
|
9853
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9854
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9855
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9856
|
+
* See the License for the specific language governing permissions and
|
|
9857
|
+
* limitations under the License.
|
|
9858
|
+
*/
|
|
9859
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8873
9860
|
/**
|
|
8874
9861
|
* A persisted implementation of IndexManager.
|
|
9862
|
+
*
|
|
9863
|
+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
|
|
9864
|
+
* data as it supports multi-tab access.
|
|
8875
9865
|
*/
|
|
8876
9866
|
class IndexedDbIndexManager {
|
|
8877
|
-
constructor() {
|
|
9867
|
+
constructor(user) {
|
|
9868
|
+
this.user = user;
|
|
8878
9869
|
/**
|
|
8879
9870
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8880
9871
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8881
9872
|
*
|
|
8882
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8883
|
-
* satisfy reads.
|
|
9873
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9874
|
+
* used to satisfy reads.
|
|
8884
9875
|
*/
|
|
8885
9876
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9877
|
+
this.uid = user.uid || '';
|
|
8886
9878
|
}
|
|
8887
9879
|
/**
|
|
8888
9880
|
* Adds a new entry to the collection parent index.
|
|
@@ -8930,12 +9922,25 @@ class IndexedDbIndexManager {
|
|
|
8930
9922
|
});
|
|
8931
9923
|
}
|
|
8932
9924
|
addFieldIndex(transaction, index) {
|
|
8933
|
-
// TODO(indexing):
|
|
8934
|
-
|
|
9925
|
+
// TODO(indexing): Verify that the auto-incrementing index ID works in
|
|
9926
|
+
// Safari & Firefox.
|
|
9927
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9928
|
+
const dbIndex = toDbIndexConfiguration(index);
|
|
9929
|
+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
|
|
9930
|
+
return indexes.add(dbIndex).next();
|
|
8935
9931
|
}
|
|
8936
9932
|
deleteFieldIndex(transaction, index) {
|
|
8937
|
-
|
|
8938
|
-
|
|
9933
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9934
|
+
const states = indexStateStore(transaction);
|
|
9935
|
+
const entries = indexEntriesStore(transaction);
|
|
9936
|
+
return indexes
|
|
9937
|
+
.delete(index.indexId)
|
|
9938
|
+
.next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9939
|
+
/*lowerOpen=*/ false,
|
|
9940
|
+
/*upperOpen=*/ true)))
|
|
9941
|
+
.next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9942
|
+
/*lowerOpen=*/ false,
|
|
9943
|
+
/*upperOpen=*/ true)));
|
|
8939
9944
|
}
|
|
8940
9945
|
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
8941
9946
|
// TODO(indexing): Implement
|
|
@@ -8945,21 +9950,168 @@ class IndexedDbIndexManager {
|
|
|
8945
9950
|
// TODO(indexing): Implement
|
|
8946
9951
|
return PersistencePromise.resolve(null);
|
|
8947
9952
|
}
|
|
9953
|
+
/**
|
|
9954
|
+
* Returns the byte encoded form of the directional values in the field index.
|
|
9955
|
+
* Returns `null` if the document does not have all fields specified in the
|
|
9956
|
+
* index.
|
|
9957
|
+
*/
|
|
9958
|
+
encodeDirectionalElements(fieldIndex, document) {
|
|
9959
|
+
const encoder = new IndexByteEncoder();
|
|
9960
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
9961
|
+
const field = document.data.field(segment.fieldPath);
|
|
9962
|
+
if (field == null) {
|
|
9963
|
+
return null;
|
|
9964
|
+
}
|
|
9965
|
+
const directionalEncoder = encoder.forKind(segment.kind);
|
|
9966
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
|
|
9967
|
+
}
|
|
9968
|
+
return encoder.encodedBytes();
|
|
9969
|
+
}
|
|
9970
|
+
/** Encodes a single value to the ascending index format. */
|
|
9971
|
+
encodeSingleElement(value) {
|
|
9972
|
+
const encoder = new IndexByteEncoder();
|
|
9973
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
9974
|
+
return encoder.encodedBytes();
|
|
9975
|
+
}
|
|
8948
9976
|
getFieldIndexes(transaction, collectionGroup) {
|
|
8949
|
-
|
|
8950
|
-
|
|
9977
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9978
|
+
const states = indexStateStore(transaction);
|
|
9979
|
+
return (collectionGroup
|
|
9980
|
+
? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9981
|
+
: indexes.loadAll()).next(indexConfigs => {
|
|
9982
|
+
const result = [];
|
|
9983
|
+
return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
|
|
9984
|
+
return states
|
|
9985
|
+
.get([indexConfig.indexId, this.uid])
|
|
9986
|
+
.next(indexState => {
|
|
9987
|
+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
|
|
9988
|
+
});
|
|
9989
|
+
}).next(() => result);
|
|
9990
|
+
});
|
|
8951
9991
|
}
|
|
8952
9992
|
getNextCollectionGroupToUpdate(transaction) {
|
|
8953
|
-
|
|
8954
|
-
|
|
9993
|
+
return this.getFieldIndexes(transaction).next(indexes => {
|
|
9994
|
+
if (indexes.length === 0) {
|
|
9995
|
+
return null;
|
|
9996
|
+
}
|
|
9997
|
+
indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
|
|
9998
|
+
return indexes[0].collectionGroup;
|
|
9999
|
+
});
|
|
8955
10000
|
}
|
|
8956
10001
|
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
8957
|
-
|
|
8958
|
-
|
|
10002
|
+
const indexes = indexConfigurationStore(transaction);
|
|
10003
|
+
const states = indexStateStore(transaction);
|
|
10004
|
+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
|
|
10005
|
+
.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
10006
|
+
.next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
|
|
8959
10007
|
}
|
|
8960
10008
|
updateIndexEntries(transaction, documents) {
|
|
8961
|
-
//
|
|
8962
|
-
|
|
10009
|
+
// Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
|
|
10010
|
+
// it could be used across different IndexedDB transactions. As any cached
|
|
10011
|
+
// data might be invalidated by other multi-tab clients, we can only trust
|
|
10012
|
+
// data within a single IndexedDB transaction. We therefore add a cache
|
|
10013
|
+
// here.
|
|
10014
|
+
const memoizedIndexes = new Map();
|
|
10015
|
+
return PersistencePromise.forEach(documents, (key, doc) => {
|
|
10016
|
+
const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
|
|
10017
|
+
const fieldIndexes = memoizedCollectionIndexes
|
|
10018
|
+
? PersistencePromise.resolve(memoizedCollectionIndexes)
|
|
10019
|
+
: this.getFieldIndexes(transaction, key.collectionGroup);
|
|
10020
|
+
return fieldIndexes.next(fieldIndexes => {
|
|
10021
|
+
memoizedIndexes.set(key.collectionGroup, fieldIndexes);
|
|
10022
|
+
return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
|
|
10023
|
+
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
10024
|
+
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10025
|
+
if (!existingEntries.isEqual(newEntries)) {
|
|
10026
|
+
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10027
|
+
}
|
|
10028
|
+
return PersistencePromise.resolve();
|
|
10029
|
+
});
|
|
10030
|
+
});
|
|
10031
|
+
});
|
|
10032
|
+
});
|
|
10033
|
+
}
|
|
10034
|
+
addIndexEntry(transaction, document, indexEntry) {
|
|
10035
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10036
|
+
return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
|
|
10037
|
+
}
|
|
10038
|
+
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10039
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10040
|
+
return indexEntries.delete([
|
|
10041
|
+
indexEntry.indexId,
|
|
10042
|
+
this.uid,
|
|
10043
|
+
indexEntry.arrayValue,
|
|
10044
|
+
indexEntry.directionalValue,
|
|
10045
|
+
encodeResourcePath(document.key.path)
|
|
10046
|
+
]);
|
|
10047
|
+
}
|
|
10048
|
+
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
10049
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10050
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10051
|
+
return indexEntries
|
|
10052
|
+
.iterate({
|
|
10053
|
+
index: DbIndexEntry.documentKeyIndex,
|
|
10054
|
+
range: IDBKeyRange.only([
|
|
10055
|
+
fieldIndex.indexId,
|
|
10056
|
+
this.uid,
|
|
10057
|
+
encodeResourcePath(documentKey.path)
|
|
10058
|
+
])
|
|
10059
|
+
}, (_, entry) => {
|
|
10060
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
10061
|
+
})
|
|
10062
|
+
.next(() => results);
|
|
10063
|
+
}
|
|
10064
|
+
/** Creates the index entries for the given document. */
|
|
10065
|
+
computeIndexEntries(document, fieldIndex) {
|
|
10066
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10067
|
+
const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
|
|
10068
|
+
if (directionalValue == null) {
|
|
10069
|
+
return results;
|
|
10070
|
+
}
|
|
10071
|
+
const arraySegment = fieldIndexGetArraySegment(fieldIndex);
|
|
10072
|
+
if (arraySegment != null) {
|
|
10073
|
+
const value = document.data.field(arraySegment.fieldPath);
|
|
10074
|
+
if (isArray(value)) {
|
|
10075
|
+
for (const arrayValue of value.arrayValue.values || []) {
|
|
10076
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
10080
|
+
else {
|
|
10081
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
|
|
10082
|
+
}
|
|
10083
|
+
return results;
|
|
10084
|
+
}
|
|
10085
|
+
/**
|
|
10086
|
+
* Updates the index entries for the provided document by deleting entries
|
|
10087
|
+
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10088
|
+
* entries.
|
|
10089
|
+
*/
|
|
10090
|
+
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10091
|
+
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10092
|
+
const promises = [];
|
|
10093
|
+
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10094
|
+
/* onAdd= */ entry => {
|
|
10095
|
+
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10096
|
+
},
|
|
10097
|
+
/* onRemove= */ entry => {
|
|
10098
|
+
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10099
|
+
});
|
|
10100
|
+
return PersistencePromise.waitFor(promises);
|
|
10101
|
+
}
|
|
10102
|
+
getNextSequenceNumber(transaction) {
|
|
10103
|
+
let nextSequenceNumber = 1;
|
|
10104
|
+
const states = indexStateStore(transaction);
|
|
10105
|
+
return states
|
|
10106
|
+
.iterate({
|
|
10107
|
+
index: DbIndexState.sequenceNumberIndex,
|
|
10108
|
+
reverse: true,
|
|
10109
|
+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
|
|
10110
|
+
}, (_, state, controller) => {
|
|
10111
|
+
controller.done();
|
|
10112
|
+
nextSequenceNumber = state.sequenceNumber + 1;
|
|
10113
|
+
})
|
|
10114
|
+
.next(() => nextSequenceNumber);
|
|
8963
10115
|
}
|
|
8964
10116
|
}
|
|
8965
10117
|
/**
|
|
@@ -8968,6 +10120,24 @@ class IndexedDbIndexManager {
|
|
|
8968
10120
|
*/
|
|
8969
10121
|
function collectionParentsStore(txn) {
|
|
8970
10122
|
return getStore(txn, DbCollectionParent.store);
|
|
10123
|
+
}
|
|
10124
|
+
/**
|
|
10125
|
+
* Helper to get a typed SimpleDbStore for the index entry object store.
|
|
10126
|
+
*/
|
|
10127
|
+
function indexEntriesStore(txn) {
|
|
10128
|
+
return getStore(txn, DbIndexEntry.store);
|
|
10129
|
+
}
|
|
10130
|
+
/**
|
|
10131
|
+
* Helper to get a typed SimpleDbStore for the index configuration object store.
|
|
10132
|
+
*/
|
|
10133
|
+
function indexConfigurationStore(txn) {
|
|
10134
|
+
return getStore(txn, DbIndexConfiguration.store);
|
|
10135
|
+
}
|
|
10136
|
+
/**
|
|
10137
|
+
* Helper to get a typed SimpleDbStore for the index state object store.
|
|
10138
|
+
*/
|
|
10139
|
+
function indexStateStore(txn) {
|
|
10140
|
+
return getStore(txn, DbIndexState.store);
|
|
8971
10141
|
}
|
|
8972
10142
|
|
|
8973
10143
|
/**
|
|
@@ -10437,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10437
11607
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10438
11608
|
*/
|
|
10439
11609
|
class IndexedDbRemoteDocumentCacheImpl {
|
|
10440
|
-
|
|
10441
|
-
* @param serializer - The document serializer.
|
|
10442
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10443
|
-
*/
|
|
10444
|
-
constructor(serializer, indexManager) {
|
|
11610
|
+
constructor(serializer) {
|
|
10445
11611
|
this.serializer = serializer;
|
|
11612
|
+
}
|
|
11613
|
+
setIndexManager(indexManager) {
|
|
10446
11614
|
this.indexManager = indexManager;
|
|
10447
11615
|
}
|
|
10448
11616
|
/**
|
|
@@ -10564,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10564
11732
|
}
|
|
10565
11733
|
});
|
|
10566
11734
|
}
|
|
10567
|
-
|
|
11735
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10568
11736
|
let results = mutableDocumentMap();
|
|
10569
|
-
const immediateChildrenPathLength =
|
|
11737
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10570
11738
|
const iterationOptions = {};
|
|
10571
11739
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10572
11740
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10573
11741
|
// down the documents we need to match the query against.
|
|
10574
|
-
const startKey =
|
|
11742
|
+
const startKey = collection.toArray();
|
|
10575
11743
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10576
11744
|
}
|
|
10577
11745
|
else {
|
|
10578
11746
|
// Execute an index-free query and filter by read time. This is safe
|
|
10579
11747
|
// since all document changes to queries that have a
|
|
10580
11748
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10581
|
-
const collectionKey =
|
|
11749
|
+
const collectionKey = collection.toArray();
|
|
10582
11750
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10583
11751
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10584
11752
|
/* open= */ true);
|
|
@@ -10595,12 +11763,12 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10595
11763
|
return;
|
|
10596
11764
|
}
|
|
10597
11765
|
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
10598
|
-
if (
|
|
10599
|
-
control.done();
|
|
10600
|
-
}
|
|
10601
|
-
else if (queryMatches(query, document)) {
|
|
11766
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10602
11767
|
results = results.insert(document.key, document);
|
|
10603
11768
|
}
|
|
11769
|
+
else {
|
|
11770
|
+
control.done();
|
|
11771
|
+
}
|
|
10604
11772
|
})
|
|
10605
11773
|
.next(() => results);
|
|
10606
11774
|
}
|
|
@@ -10638,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10638
11806
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10639
11807
|
}
|
|
10640
11808
|
}
|
|
10641
|
-
/**
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
* @param serializer - The document serializer.
|
|
10645
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10646
|
-
*/
|
|
10647
|
-
function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
|
|
10648
|
-
return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
|
|
11809
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11810
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11811
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10649
11812
|
}
|
|
10650
11813
|
/**
|
|
10651
11814
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10879,6 +12042,11 @@ class SchemaConverter {
|
|
|
10879
12042
|
});
|
|
10880
12043
|
}
|
|
10881
12044
|
if (fromVersion < 12 && toVersion >= 12) {
|
|
12045
|
+
p = p.next(() => {
|
|
12046
|
+
createDocumentOverlayStore(db);
|
|
12047
|
+
});
|
|
12048
|
+
}
|
|
12049
|
+
if (fromVersion < 13 && toVersion >= 13) {
|
|
10882
12050
|
p = p.next(() => {
|
|
10883
12051
|
createFieldIndex(db);
|
|
10884
12052
|
});
|
|
@@ -11091,15 +12259,26 @@ function createNamedQueriesStore(db) {
|
|
|
11091
12259
|
});
|
|
11092
12260
|
}
|
|
11093
12261
|
function createFieldIndex(db) {
|
|
11094
|
-
db.createObjectStore(DbIndexConfiguration.store, {
|
|
11095
|
-
keyPath: DbIndexConfiguration.keyPath
|
|
12262
|
+
const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
|
|
12263
|
+
keyPath: DbIndexConfiguration.keyPath,
|
|
12264
|
+
autoIncrement: true
|
|
11096
12265
|
});
|
|
11097
|
-
|
|
12266
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12267
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
11098
12268
|
keyPath: DbIndexState.keyPath
|
|
11099
12269
|
});
|
|
11100
|
-
|
|
11101
|
-
|
|
12270
|
+
indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
|
|
12271
|
+
const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
|
|
12272
|
+
keyPath: DbIndexEntry.keyPath
|
|
12273
|
+
});
|
|
12274
|
+
indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
|
|
12275
|
+
}
|
|
12276
|
+
function createDocumentOverlayStore(db) {
|
|
12277
|
+
const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
|
|
12278
|
+
keyPath: DbDocumentOverlay.keyPath
|
|
11102
12279
|
});
|
|
12280
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
|
|
12281
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
|
|
11103
12282
|
}
|
|
11104
12283
|
|
|
11105
12284
|
/**
|
|
@@ -11211,7 +12390,7 @@ class IndexedDbPersistence {
|
|
|
11211
12390
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
11212
12391
|
* no longer be able to access IndexedDB.
|
|
11213
12392
|
*/
|
|
11214
|
-
forceOwningTab) {
|
|
12393
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
11215
12394
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
11216
12395
|
this.persistenceKey = persistenceKey;
|
|
11217
12396
|
this.clientId = clientId;
|
|
@@ -11220,6 +12399,7 @@ class IndexedDbPersistence {
|
|
|
11220
12399
|
this.document = document;
|
|
11221
12400
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
11222
12401
|
this.forceOwningTab = forceOwningTab;
|
|
12402
|
+
this.schemaVersion = schemaVersion;
|
|
11223
12403
|
this.listenSequence = null;
|
|
11224
12404
|
this._started = false;
|
|
11225
12405
|
this.isPrimary = false;
|
|
@@ -11241,10 +12421,9 @@ class IndexedDbPersistence {
|
|
|
11241
12421
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
11242
12422
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
11243
12423
|
this.serializer = new LocalSerializer(serializer);
|
|
11244
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12424
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
11245
12425
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
11246
|
-
this.
|
|
11247
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12426
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
11248
12427
|
this.bundleCache = new IndexedDbBundleCache();
|
|
11249
12428
|
if (this.window && this.window.localStorage) {
|
|
11250
12429
|
this.webStorage = this.window.localStorage;
|
|
@@ -11580,8 +12759,8 @@ class IndexedDbPersistence {
|
|
|
11580
12759
|
get started() {
|
|
11581
12760
|
return this._started;
|
|
11582
12761
|
}
|
|
11583
|
-
getMutationQueue(user) {
|
|
11584
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12762
|
+
getMutationQueue(user, indexManager) {
|
|
12763
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11585
12764
|
}
|
|
11586
12765
|
getTargetCache() {
|
|
11587
12766
|
return this.targetCache;
|
|
@@ -11589,8 +12768,11 @@ class IndexedDbPersistence {
|
|
|
11589
12768
|
getRemoteDocumentCache() {
|
|
11590
12769
|
return this.remoteDocumentCache;
|
|
11591
12770
|
}
|
|
11592
|
-
getIndexManager() {
|
|
11593
|
-
return
|
|
12771
|
+
getIndexManager(user) {
|
|
12772
|
+
return new IndexedDbIndexManager(user);
|
|
12773
|
+
}
|
|
12774
|
+
getDocumentOverlayCache(user) {
|
|
12775
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11594
12776
|
}
|
|
11595
12777
|
getBundleCache() {
|
|
11596
12778
|
return this.bundleCache;
|
|
@@ -11598,11 +12780,12 @@ class IndexedDbPersistence {
|
|
|
11598
12780
|
runTransaction(action, mode, transactionOperation) {
|
|
11599
12781
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11600
12782
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12783
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11601
12784
|
let persistenceTransaction;
|
|
11602
12785
|
// Do all transactions as readwrite against all object stores, since we
|
|
11603
12786
|
// are the only reader/writer.
|
|
11604
12787
|
return this.simpleDb
|
|
11605
|
-
.runTransaction(action, simpleDbMode,
|
|
12788
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11606
12789
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11607
12790
|
? this.listenSequence.next()
|
|
11608
12791
|
: ListenSequence.INVALID);
|
|
@@ -11974,39 +13157,28 @@ class LocalDocumentsView {
|
|
|
11974
13157
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11975
13158
|
// Query the remote documents and overlay mutations.
|
|
11976
13159
|
let results;
|
|
11977
|
-
let mutationBatches;
|
|
11978
13160
|
return this.remoteDocumentCache
|
|
11979
|
-
.
|
|
13161
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11980
13162
|
.next(queryResults => {
|
|
11981
13163
|
results = queryResults;
|
|
11982
13164
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11983
13165
|
})
|
|
11984
|
-
.next(
|
|
11985
|
-
mutationBatches
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11990
|
-
|
|
11991
|
-
|
|
11992
|
-
|
|
11993
|
-
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
let document = results.get(key);
|
|
11998
|
-
if (document == null) {
|
|
11999
|
-
// Create invalid document to apply mutations on top of
|
|
12000
|
-
document = MutableDocument.newInvalidDocument(key);
|
|
12001
|
-
results = results.insert(key, document);
|
|
12002
|
-
}
|
|
12003
|
-
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
12004
|
-
if (!document.isFoundDocument()) {
|
|
12005
|
-
results = results.remove(key);
|
|
12006
|
-
}
|
|
13166
|
+
.next(mutationBatches => {
|
|
13167
|
+
for (const batch of mutationBatches) {
|
|
13168
|
+
for (const mutation of batch.mutations) {
|
|
13169
|
+
const key = mutation.key;
|
|
13170
|
+
let document = results.get(key);
|
|
13171
|
+
if (document == null) {
|
|
13172
|
+
// Create invalid document to apply mutations on top of
|
|
13173
|
+
document = MutableDocument.newInvalidDocument(key);
|
|
13174
|
+
results = results.insert(key, document);
|
|
13175
|
+
}
|
|
13176
|
+
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
13177
|
+
if (!document.isFoundDocument()) {
|
|
13178
|
+
results = results.remove(key);
|
|
12007
13179
|
}
|
|
12008
13180
|
}
|
|
12009
|
-
}
|
|
13181
|
+
}
|
|
12010
13182
|
})
|
|
12011
13183
|
.next(() => {
|
|
12012
13184
|
// Finally, filter out any documents that don't actually match
|
|
@@ -12019,29 +13191,6 @@ class LocalDocumentsView {
|
|
|
12019
13191
|
return results;
|
|
12020
13192
|
});
|
|
12021
13193
|
}
|
|
12022
|
-
addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
|
|
12023
|
-
let missingBaseDocEntriesForPatching = documentKeySet();
|
|
12024
|
-
for (const batch of matchingMutationBatches) {
|
|
12025
|
-
for (const mutation of batch.mutations) {
|
|
12026
|
-
if (mutation instanceof PatchMutation &&
|
|
12027
|
-
existingDocuments.get(mutation.key) === null) {
|
|
12028
|
-
missingBaseDocEntriesForPatching =
|
|
12029
|
-
missingBaseDocEntriesForPatching.add(mutation.key);
|
|
12030
|
-
}
|
|
12031
|
-
}
|
|
12032
|
-
}
|
|
12033
|
-
let mergedDocuments = existingDocuments;
|
|
12034
|
-
return this.remoteDocumentCache
|
|
12035
|
-
.getEntries(transaction, missingBaseDocEntriesForPatching)
|
|
12036
|
-
.next(missingBaseDocs => {
|
|
12037
|
-
missingBaseDocs.forEach((key, doc) => {
|
|
12038
|
-
if (doc.isFoundDocument()) {
|
|
12039
|
-
mergedDocuments = mergedDocuments.insert(key, doc);
|
|
12040
|
-
}
|
|
12041
|
-
});
|
|
12042
|
-
return mergedDocuments;
|
|
12043
|
-
});
|
|
12044
|
-
}
|
|
12045
13194
|
}
|
|
12046
13195
|
|
|
12047
13196
|
/**
|
|
@@ -12100,11 +13249,18 @@ class LocalStoreImpl {
|
|
|
12100
13249
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
12101
13250
|
*/
|
|
12102
13251
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
12103
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
12104
13252
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
12105
13253
|
this.targetCache = persistence.getTargetCache();
|
|
12106
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
12107
13254
|
this.bundleCache = persistence.getBundleCache();
|
|
13255
|
+
this.initializeUserComponents(initialUser);
|
|
13256
|
+
}
|
|
13257
|
+
initializeUserComponents(user) {
|
|
13258
|
+
// TODO(indexing): Add spec tests that test these components change after a
|
|
13259
|
+
// user change
|
|
13260
|
+
this.indexManager = this.persistence.getIndexManager(user);
|
|
13261
|
+
this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
|
|
13262
|
+
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
|
|
13263
|
+
this.remoteDocuments.setIndexManager(this.indexManager);
|
|
12108
13264
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
12109
13265
|
}
|
|
12110
13266
|
collectGarbage(garbageCollector) {
|
|
@@ -12126,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
12126
13282
|
// change.
|
|
12127
13283
|
async function localStoreHandleUserChange(localStore, user) {
|
|
12128
13284
|
const localStoreImpl = debugCast(localStore);
|
|
12129
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
12130
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
12131
13285
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
12132
13286
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
12133
13287
|
// before and after.
|
|
@@ -12136,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
12136
13290
|
.getAllMutationBatches(txn)
|
|
12137
13291
|
.next(promisedOldBatches => {
|
|
12138
13292
|
oldBatches = promisedOldBatches;
|
|
12139
|
-
|
|
12140
|
-
|
|
12141
|
-
// MutationQueue.
|
|
12142
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
12143
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13293
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13294
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
12144
13295
|
})
|
|
12145
13296
|
.next(newBatches => {
|
|
12146
13297
|
const removedBatchIds = [];
|
|
@@ -12161,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
12161
13312
|
}
|
|
12162
13313
|
// Return the set of all (potentially) changed documents and the list
|
|
12163
13314
|
// of mutation batch IDs that were affected by change.
|
|
12164
|
-
return
|
|
13315
|
+
return localStoreImpl.localDocuments
|
|
12165
13316
|
.getDocuments(txn, changedKeys)
|
|
12166
13317
|
.next(affectedDocuments => {
|
|
12167
13318
|
return {
|
|
@@ -12172,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
12172
13323
|
});
|
|
12173
13324
|
});
|
|
12174
13325
|
});
|
|
12175
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
12176
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
12177
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
12178
13326
|
return result;
|
|
12179
13327
|
}
|
|
12180
13328
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -12887,6 +14035,122 @@ class MemoryBundleCache {
|
|
|
12887
14035
|
}
|
|
12888
14036
|
}
|
|
12889
14037
|
|
|
14038
|
+
/**
|
|
14039
|
+
* @license
|
|
14040
|
+
* Copyright 2022 Google LLC
|
|
14041
|
+
*
|
|
14042
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14043
|
+
* you may not use this file except in compliance with the License.
|
|
14044
|
+
* You may obtain a copy of the License at
|
|
14045
|
+
*
|
|
14046
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14047
|
+
*
|
|
14048
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14049
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14050
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14051
|
+
* See the License for the specific language governing permissions and
|
|
14052
|
+
* limitations under the License.
|
|
14053
|
+
*/
|
|
14054
|
+
/**
|
|
14055
|
+
* An in-memory implementation of DocumentOverlayCache.
|
|
14056
|
+
*/
|
|
14057
|
+
class MemoryDocumentOverlayCache {
|
|
14058
|
+
constructor() {
|
|
14059
|
+
// A map sorted by DocumentKey, whose value is a pair of the largest batch id
|
|
14060
|
+
// for the overlay and the overlay itself.
|
|
14061
|
+
this.overlays = new SortedMap(DocumentKey.comparator);
|
|
14062
|
+
this.overlayByBatchId = new Map();
|
|
14063
|
+
}
|
|
14064
|
+
getOverlay(transaction, key) {
|
|
14065
|
+
return PersistencePromise.resolve(this.overlays.get(key));
|
|
14066
|
+
}
|
|
14067
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
14068
|
+
overlays.forEach(mutation => {
|
|
14069
|
+
this.saveOverlay(transaction, largestBatchId, mutation);
|
|
14070
|
+
});
|
|
14071
|
+
return PersistencePromise.resolve();
|
|
14072
|
+
}
|
|
14073
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
14074
|
+
const keys = this.overlayByBatchId.get(batchId);
|
|
14075
|
+
if (keys !== undefined) {
|
|
14076
|
+
keys.forEach(key => (this.overlays = this.overlays.remove(key)));
|
|
14077
|
+
this.overlayByBatchId.delete(batchId);
|
|
14078
|
+
}
|
|
14079
|
+
return PersistencePromise.resolve();
|
|
14080
|
+
}
|
|
14081
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
14082
|
+
const result = new Map();
|
|
14083
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
14084
|
+
const prefix = new DocumentKey(collection.child(''));
|
|
14085
|
+
const iter = this.overlays.getIteratorFrom(prefix);
|
|
14086
|
+
while (iter.hasNext()) {
|
|
14087
|
+
const entry = iter.getNext();
|
|
14088
|
+
const overlay = entry.value;
|
|
14089
|
+
const key = overlay.getKey();
|
|
14090
|
+
if (!collection.isPrefixOf(key.path)) {
|
|
14091
|
+
break;
|
|
14092
|
+
}
|
|
14093
|
+
// Documents from sub-collections
|
|
14094
|
+
if (key.path.length !== immediateChildrenPathLength) {
|
|
14095
|
+
continue;
|
|
14096
|
+
}
|
|
14097
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14098
|
+
result.set(overlay.getKey(), overlay);
|
|
14099
|
+
}
|
|
14100
|
+
}
|
|
14101
|
+
return PersistencePromise.resolve(result);
|
|
14102
|
+
}
|
|
14103
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
14104
|
+
let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
|
|
14105
|
+
const iter = this.overlays.getIterator();
|
|
14106
|
+
while (iter.hasNext()) {
|
|
14107
|
+
const entry = iter.getNext();
|
|
14108
|
+
const overlay = entry.value;
|
|
14109
|
+
const key = overlay.getKey();
|
|
14110
|
+
if (key.getCollectionGroup() !== collectionGroup) {
|
|
14111
|
+
continue;
|
|
14112
|
+
}
|
|
14113
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14114
|
+
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
|
|
14115
|
+
if (overlaysForBatchId === null) {
|
|
14116
|
+
overlaysForBatchId = new Map();
|
|
14117
|
+
batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
|
|
14118
|
+
}
|
|
14119
|
+
overlaysForBatchId.set(overlay.getKey(), overlay);
|
|
14120
|
+
}
|
|
14121
|
+
}
|
|
14122
|
+
const result = new Map();
|
|
14123
|
+
const batchIter = batchIdToOverlays.getIterator();
|
|
14124
|
+
while (batchIter.hasNext()) {
|
|
14125
|
+
const entry = batchIter.getNext();
|
|
14126
|
+
const overlays = entry.value;
|
|
14127
|
+
overlays.forEach((overlay, key) => result.set(key, overlay));
|
|
14128
|
+
if (result.size >= count) {
|
|
14129
|
+
break;
|
|
14130
|
+
}
|
|
14131
|
+
}
|
|
14132
|
+
return PersistencePromise.resolve(result);
|
|
14133
|
+
}
|
|
14134
|
+
saveOverlay(transaction, largestBatchId, mutation) {
|
|
14135
|
+
if (mutation === null) {
|
|
14136
|
+
return;
|
|
14137
|
+
}
|
|
14138
|
+
// Remove the association of the overlay to its batch id.
|
|
14139
|
+
const existing = this.overlays.get(mutation.key);
|
|
14140
|
+
if (existing !== null) {
|
|
14141
|
+
this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
|
|
14142
|
+
}
|
|
14143
|
+
this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
|
|
14144
|
+
// Create the association of this overlay to the given largestBatchId.
|
|
14145
|
+
let batch = this.overlayByBatchId.get(largestBatchId);
|
|
14146
|
+
if (batch === undefined) {
|
|
14147
|
+
batch = new Set();
|
|
14148
|
+
this.overlayByBatchId.set(largestBatchId, batch);
|
|
14149
|
+
}
|
|
14150
|
+
batch.add(mutation.key);
|
|
14151
|
+
}
|
|
14152
|
+
}
|
|
14153
|
+
|
|
12890
14154
|
/**
|
|
12891
14155
|
* @license
|
|
12892
14156
|
* Copyright 2017 Google LLC
|
|
@@ -13240,14 +14504,16 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13240
14504
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13241
14505
|
* calculating the size.
|
|
13242
14506
|
*/
|
|
13243
|
-
constructor(
|
|
13244
|
-
this.indexManager = indexManager;
|
|
14507
|
+
constructor(sizer) {
|
|
13245
14508
|
this.sizer = sizer;
|
|
13246
14509
|
/** Underlying cache of documents and their read times. */
|
|
13247
14510
|
this.docs = documentEntryMap();
|
|
13248
14511
|
/** Size of all cached documents. */
|
|
13249
14512
|
this.size = 0;
|
|
13250
14513
|
}
|
|
14514
|
+
setIndexManager(indexManager) {
|
|
14515
|
+
this.indexManager = indexManager;
|
|
14516
|
+
}
|
|
13251
14517
|
/**
|
|
13252
14518
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
13253
14519
|
*
|
|
@@ -13295,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13295
14561
|
});
|
|
13296
14562
|
return PersistencePromise.resolve(results);
|
|
13297
14563
|
}
|
|
13298
|
-
|
|
14564
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
13299
14565
|
let results = mutableDocumentMap();
|
|
13300
14566
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
13301
14567
|
// the documents we need to match the query against.
|
|
13302
|
-
const prefix = new DocumentKey(
|
|
14568
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
13303
14569
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
13304
14570
|
while (iterator.hasNext()) {
|
|
13305
14571
|
const { key, value: { document } } = iterator.getNext();
|
|
13306
|
-
if (!
|
|
14572
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
13307
14573
|
break;
|
|
13308
14574
|
}
|
|
13309
|
-
if (
|
|
14575
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14576
|
+
// Exclude entries from subcollections.
|
|
13310
14577
|
continue;
|
|
13311
14578
|
}
|
|
13312
|
-
if (
|
|
14579
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
13313
14580
|
continue;
|
|
13314
14581
|
}
|
|
13315
14582
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -13331,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13331
14598
|
/**
|
|
13332
14599
|
* Creates a new memory-only RemoteDocumentCache.
|
|
13333
14600
|
*
|
|
13334
|
-
* @param indexManager - A class that manages collection group indices.
|
|
13335
14601
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
13336
14602
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13337
14603
|
* calculating the size.
|
|
13338
14604
|
*/
|
|
13339
|
-
function newMemoryRemoteDocumentCache(
|
|
13340
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14605
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14606
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
13341
14607
|
}
|
|
13342
14608
|
/**
|
|
13343
14609
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -13531,6 +14797,7 @@ class MemoryPersistence {
|
|
|
13531
14797
|
*/
|
|
13532
14798
|
constructor(referenceDelegateFactory, serializer) {
|
|
13533
14799
|
this.mutationQueues = {};
|
|
14800
|
+
this.overlays = {};
|
|
13534
14801
|
this.listenSequence = new ListenSequence(0);
|
|
13535
14802
|
this._started = false;
|
|
13536
14803
|
this._started = true;
|
|
@@ -13538,7 +14805,7 @@ class MemoryPersistence {
|
|
|
13538
14805
|
this.targetCache = new MemoryTargetCache(this);
|
|
13539
14806
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13540
14807
|
this.indexManager = new MemoryIndexManager();
|
|
13541
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14808
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13542
14809
|
this.serializer = new LocalSerializer(serializer);
|
|
13543
14810
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13544
14811
|
}
|
|
@@ -13559,13 +14826,23 @@ class MemoryPersistence {
|
|
|
13559
14826
|
setNetworkEnabled() {
|
|
13560
14827
|
// No op.
|
|
13561
14828
|
}
|
|
13562
|
-
getIndexManager() {
|
|
14829
|
+
getIndexManager(user) {
|
|
14830
|
+
// We do not currently support indices for memory persistence, so we can
|
|
14831
|
+
// return the same shared instance of the memory index manager.
|
|
13563
14832
|
return this.indexManager;
|
|
13564
14833
|
}
|
|
13565
|
-
|
|
14834
|
+
getDocumentOverlayCache(user) {
|
|
14835
|
+
let overlay = this.overlays[user.toKey()];
|
|
14836
|
+
if (!overlay) {
|
|
14837
|
+
overlay = new MemoryDocumentOverlayCache();
|
|
14838
|
+
this.overlays[user.toKey()] = overlay;
|
|
14839
|
+
}
|
|
14840
|
+
return overlay;
|
|
14841
|
+
}
|
|
14842
|
+
getMutationQueue(user, indexManager) {
|
|
13566
14843
|
let queue = this.mutationQueues[user.toKey()];
|
|
13567
14844
|
if (!queue) {
|
|
13568
|
-
queue = new MemoryMutationQueue(
|
|
14845
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13569
14846
|
this.mutationQueues[user.toKey()] = queue;
|
|
13570
14847
|
}
|
|
13571
14848
|
return queue;
|
|
@@ -14710,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
|
|
|
14710
15987
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
14711
15988
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
14712
15989
|
hardAssert(authToken === null || authToken.type === 'OAuth');
|
|
14713
|
-
const metadata = new
|
|
15990
|
+
const metadata = new grpc__namespace.Metadata();
|
|
14714
15991
|
if (authToken) {
|
|
14715
15992
|
authToken.headers.forEach((value, key) => metadata.set(key, value));
|
|
14716
15993
|
}
|
|
@@ -14741,10 +16018,10 @@ class GrpcConnection {
|
|
|
14741
16018
|
ensureActiveStub() {
|
|
14742
16019
|
if (!this.cachedStub) {
|
|
14743
16020
|
logDebug(LOG_TAG$9, 'Creating Firestore stub.');
|
|
14744
|
-
const credentials
|
|
14745
|
-
?
|
|
14746
|
-
:
|
|
14747
|
-
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials
|
|
16021
|
+
const credentials = this.databaseInfo.ssl
|
|
16022
|
+
? grpc__namespace.credentials.createSsl()
|
|
16023
|
+
: grpc__namespace.credentials.createInsecure();
|
|
16024
|
+
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials);
|
|
14748
16025
|
}
|
|
14749
16026
|
return this.cachedStub;
|
|
14750
16027
|
}
|
|
@@ -17576,8 +18853,8 @@ const protoLoaderOptions = {
|
|
|
17576
18853
|
* @returns The GrpcObject representing our protos.
|
|
17577
18854
|
*/
|
|
17578
18855
|
function loadProtos() {
|
|
17579
|
-
const packageDefinition =
|
|
17580
|
-
return
|
|
18856
|
+
const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
|
|
18857
|
+
return grpc__namespace.loadPackageDefinition(packageDefinition);
|
|
17581
18858
|
}
|
|
17582
18859
|
|
|
17583
18860
|
/**
|