@firebase/firestore 3.4.5 → 3.4.6-2022216223411

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/firestore/src/index/index_entry.d.ts +6 -0
  3. package/dist/firestore/src/local/document_overlay_cache.d.ts +4 -5
  4. package/dist/firestore/src/local/index_manager.d.ts +2 -2
  5. package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +4 -5
  6. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +48 -1
  7. package/dist/firestore/src/local/indexeddb_schema.d.ts +34 -553
  8. package/dist/firestore/src/local/indexeddb_sentinels.d.ts +238 -0
  9. package/dist/firestore/src/local/indexeddb_target_cache.d.ts +2 -1
  10. package/dist/firestore/src/local/local_serializer.d.ts +2 -1
  11. package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +4 -5
  12. package/dist/firestore/src/local/memory_index_manager.d.ts +1 -1
  13. package/dist/firestore/src/local/simple_db.d.ts +11 -0
  14. package/dist/firestore/src/model/collections.d.ts +7 -0
  15. package/dist/firestore/src/model/field_index.d.ts +2 -0
  16. package/dist/firestore/src/model/type_order.d.ts +2 -1
  17. package/dist/firestore/src/platform/node/base64.d.ts +1 -0
  18. package/dist/firestore/src/util/obj_map.d.ts +3 -0
  19. package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +4 -4
  20. package/dist/firestore/test/unit/local/test_index_manager.d.ts +1 -1
  21. package/dist/index.esm2017.js +4160 -4201
  22. package/dist/index.esm2017.js.map +1 -1
  23. package/dist/index.esm5.js +4332 -4311
  24. package/dist/index.esm5.js.map +1 -1
  25. package/dist/index.node.cjs.js +1223 -956
  26. package/dist/index.node.cjs.js.map +1 -1
  27. package/dist/index.node.mjs +1223 -956
  28. package/dist/index.node.mjs.map +1 -1
  29. package/dist/index.rn.js +4157 -4198
  30. package/dist/index.rn.js.map +1 -1
  31. package/dist/internal.d.ts +12 -5
  32. package/dist/lite/firestore/src/index/index_entry.d.ts +6 -0
  33. package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +4 -5
  34. package/dist/lite/firestore/src/local/index_manager.d.ts +2 -2
  35. package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +4 -5
  36. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +48 -1
  37. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +34 -553
  38. package/dist/lite/firestore/src/local/indexeddb_sentinels.d.ts +238 -0
  39. package/dist/lite/firestore/src/local/indexeddb_target_cache.d.ts +2 -1
  40. package/dist/lite/firestore/src/local/local_serializer.d.ts +2 -1
  41. package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +4 -5
  42. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +1 -1
  43. package/dist/lite/firestore/src/local/simple_db.d.ts +11 -0
  44. package/dist/lite/firestore/src/model/collections.d.ts +7 -0
  45. package/dist/lite/firestore/src/model/field_index.d.ts +2 -0
  46. package/dist/lite/firestore/src/model/type_order.d.ts +2 -1
  47. package/dist/lite/firestore/src/platform/node/base64.d.ts +1 -0
  48. package/dist/lite/firestore/src/util/obj_map.d.ts +3 -0
  49. package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +4 -4
  50. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +1 -1
  51. package/dist/lite/index.browser.esm2017.js +34 -24
  52. package/dist/lite/index.browser.esm2017.js.map +1 -1
  53. package/dist/lite/index.browser.esm5.js +12 -23
  54. package/dist/lite/index.browser.esm5.js.map +1 -1
  55. package/dist/lite/index.node.cjs.js +21 -9
  56. package/dist/lite/index.node.cjs.js.map +1 -1
  57. package/dist/lite/index.node.mjs +21 -9
  58. package/dist/lite/index.node.mjs.map +1 -1
  59. package/dist/lite/index.rn.esm2017.js +34 -24
  60. package/dist/lite/index.rn.esm2017.js.map +1 -1
  61. package/dist/lite/packages/firestore/src/index/index_entry.d.ts +6 -0
  62. package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +4 -5
  63. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +2 -2
  64. package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +4 -5
  65. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +48 -1
  66. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +34 -553
  67. package/dist/lite/packages/firestore/src/local/indexeddb_sentinels.d.ts +238 -0
  68. package/dist/lite/packages/firestore/src/local/indexeddb_target_cache.d.ts +2 -1
  69. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +2 -1
  70. package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +4 -5
  71. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +1 -1
  72. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +11 -0
  73. package/dist/lite/packages/firestore/src/model/collections.d.ts +7 -0
  74. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -0
  75. package/dist/lite/packages/firestore/src/model/type_order.d.ts +2 -1
  76. package/dist/lite/packages/firestore/src/platform/node/base64.d.ts +1 -0
  77. package/dist/lite/packages/firestore/src/util/obj_map.d.ts +3 -0
  78. package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +4 -4
  79. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +1 -1
  80. package/dist/packages/firestore/dist/index.esm2017.d.ts +191 -191
  81. package/dist/packages/firestore/src/index/index_entry.d.ts +6 -0
  82. package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +4 -5
  83. package/dist/packages/firestore/src/local/index_manager.d.ts +2 -2
  84. package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +4 -5
  85. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +48 -1
  86. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +34 -553
  87. package/dist/packages/firestore/src/local/indexeddb_sentinels.d.ts +238 -0
  88. package/dist/packages/firestore/src/local/indexeddb_target_cache.d.ts +2 -1
  89. package/dist/packages/firestore/src/local/local_serializer.d.ts +2 -1
  90. package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +4 -5
  91. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +1 -1
  92. package/dist/packages/firestore/src/local/simple_db.d.ts +11 -0
  93. package/dist/packages/firestore/src/model/collections.d.ts +7 -0
  94. package/dist/packages/firestore/src/model/field_index.d.ts +2 -0
  95. package/dist/packages/firestore/src/model/type_order.d.ts +2 -1
  96. package/dist/packages/firestore/src/platform/node/base64.d.ts +1 -0
  97. package/dist/packages/firestore/src/util/obj_map.d.ts +3 -0
  98. package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +4 -4
  99. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +1 -1
  100. package/dist/private.d.ts +12 -5
  101. package/package.json +7 -7
@@ -33,7 +33,7 @@ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
33
33
  var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
34
34
 
35
35
  const name = "@firebase/firestore";
36
- const version$1 = "3.4.5";
36
+ const version$1 = "3.4.6-2022216223411";
37
37
 
38
38
  /**
39
39
  * @license
@@ -86,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
86
86
  User.FIRST_PARTY = new User('first-party-uid');
87
87
  User.MOCK_USER = new User('mock-user');
88
88
 
89
- const version = "9.6.7";
89
+ const version = "9.6.9-2022216223411";
90
90
 
91
91
  /**
92
92
  * @license
@@ -1224,55 +1224,23 @@ function decodeResourcePath(path) {
1224
1224
  }
1225
1225
  return new ResourcePath(segments);
1226
1226
  }
1227
+
1227
1228
  /**
1228
- * Schema Version for the Web client:
1229
- * 1. Initial version including Mutation Queue, Query Cache, and Remote
1230
- * Document Cache
1231
- * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No
1232
- * longer required because migration 3 unconditionally clears it.
1233
- * 3. Dropped and re-created Query Cache to deal with cache corruption related
1234
- * to limbo resolution. Addresses
1235
- * https://github.com/firebase/firebase-ios-sdk/issues/1548
1236
- * 4. Multi-Tab Support.
1237
- * 5. Removal of held write acks.
1238
- * 6. Create document global for tracking document cache size.
1239
- * 7. Ensure every cached document has a sentinel row with a sequence number.
1240
- * 8. Add collection-parent index for Collection Group queries.
1241
- * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than
1242
- * an auto-incrementing ID. This is required for Index-Free queries.
1243
- * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1244
- * 11. Add bundles and named_queries for bundle support.
1245
- * 12. Add document overlays.
1246
- * 13. Add indexing support.
1247
- */
1248
- const SCHEMA_VERSION = 12;
1249
- /**
1250
- * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
1251
- */
1252
- class DbTimestamp {
1253
- constructor(seconds, nanoseconds) {
1254
- this.seconds = seconds;
1255
- this.nanoseconds = nanoseconds;
1256
- }
1257
- }
1258
- /**
1259
- * A singleton object to be stored in the 'owner' store in IndexedDb.
1229
+ * @license
1230
+ * Copyright 2022 Google LLC
1231
+ *
1232
+ * Licensed under the Apache License, Version 2.0 (the "License");
1233
+ * you may not use this file except in compliance with the License.
1234
+ * You may obtain a copy of the License at
1260
1235
  *
1261
- * A given database can have a single primary tab assigned at a given time. That
1262
- * tab must validate that it is still holding the primary lease before every
1263
- * operation that requires locked access. The primary tab should regularly
1264
- * write an updated timestamp to this lease to prevent other tabs from
1265
- * "stealing" the primary lease
1236
+ * http://www.apache.org/licenses/LICENSE-2.0
1237
+ *
1238
+ * Unless required by applicable law or agreed to in writing, software
1239
+ * distributed under the License is distributed on an "AS IS" BASIS,
1240
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1241
+ * See the License for the specific language governing permissions and
1242
+ * limitations under the License.
1266
1243
  */
1267
- class DbPrimaryClient {
1268
- constructor(ownerId,
1269
- /** Whether to allow shared access from multiple tabs. */
1270
- allowTabSynchronization, leaseTimestampMs) {
1271
- this.ownerId = ownerId;
1272
- this.allowTabSynchronization = allowTabSynchronization;
1273
- this.leaseTimestampMs = leaseTimestampMs;
1274
- }
1275
- }
1276
1244
  /**
1277
1245
  * Name of the IndexedDb object store.
1278
1246
  *
@@ -1280,239 +1248,62 @@ class DbPrimaryClient {
1280
1248
  * older clients that only supported single locked access to the persistence
1281
1249
  * layer.
1282
1250
  */
1283
- DbPrimaryClient.store = 'owner';
1251
+ const DbPrimaryClientStore = 'owner';
1284
1252
  /**
1285
1253
  * The key string used for the single object that exists in the
1286
1254
  * DbPrimaryClient store.
1287
1255
  */
1288
- DbPrimaryClient.key = 'owner';
1289
- /**
1290
- * An object to be stored in the 'mutationQueues' store in IndexedDb.
1291
- *
1292
- * Each user gets a single queue of MutationBatches to apply to the server.
1293
- * DbMutationQueue tracks the metadata about the queue.
1294
- */
1295
- class DbMutationQueue {
1296
- constructor(
1297
- /**
1298
- * The normalized user ID to which this queue belongs.
1299
- */
1300
- userId,
1301
- /**
1302
- * An identifier for the highest numbered batch that has been acknowledged
1303
- * by the server. All MutationBatches in this queue with batchIds less
1304
- * than or equal to this value are considered to have been acknowledged by
1305
- * the server.
1306
- *
1307
- * NOTE: this is deprecated and no longer used by the code.
1308
- */
1309
- lastAcknowledgedBatchId,
1310
- /**
1311
- * A stream token that was previously sent by the server.
1312
- *
1313
- * See StreamingWriteRequest in datastore.proto for more details about
1314
- * usage.
1315
- *
1316
- * After sending this token, earlier tokens may not be used anymore so
1317
- * only a single stream token is retained.
1318
- *
1319
- * NOTE: this is deprecated and no longer used by the code.
1320
- */
1321
- lastStreamToken) {
1322
- this.userId = userId;
1323
- this.lastAcknowledgedBatchId = lastAcknowledgedBatchId;
1324
- this.lastStreamToken = lastStreamToken;
1325
- }
1326
- }
1256
+ const DbPrimaryClientKey = 'owner';
1327
1257
  /** Name of the IndexedDb object store. */
1328
- DbMutationQueue.store = 'mutationQueues';
1258
+ const DbMutationQueueStore = 'mutationQueues';
1329
1259
  /** Keys are automatically assigned via the userId property. */
1330
- DbMutationQueue.keyPath = 'userId';
1331
- /**
1332
- * An object to be stored in the 'mutations' store in IndexedDb.
1333
- *
1334
- * Represents a batch of user-level mutations intended to be sent to the server
1335
- * in a single write. Each user-level batch gets a separate DbMutationBatch
1336
- * with a new batchId.
1337
- */
1338
- class DbMutationBatch {
1339
- constructor(
1340
- /**
1341
- * The normalized user ID to which this batch belongs.
1342
- */
1343
- userId,
1344
- /**
1345
- * An identifier for this batch, allocated using an auto-generated key.
1346
- */
1347
- batchId,
1348
- /**
1349
- * The local write time of the batch, stored as milliseconds since the
1350
- * epoch.
1351
- */
1352
- localWriteTimeMs,
1353
- /**
1354
- * A list of "mutations" that represent a partial base state from when this
1355
- * write batch was initially created. During local application of the write
1356
- * batch, these baseMutations are applied prior to the real writes in order
1357
- * to override certain document fields from the remote document cache. This
1358
- * is necessary in the case of non-idempotent writes (e.g. `increment()`
1359
- * transforms) to make sure that the local view of the modified documents
1360
- * doesn't flicker if the remote document cache receives the result of the
1361
- * non-idempotent write before the write is removed from the queue.
1362
- *
1363
- * These mutations are never sent to the backend.
1364
- */
1365
- baseMutations,
1366
- /**
1367
- * A list of mutations to apply. All mutations will be applied atomically.
1368
- *
1369
- * Mutations are serialized via toMutation().
1370
- */
1371
- mutations) {
1372
- this.userId = userId;
1373
- this.batchId = batchId;
1374
- this.localWriteTimeMs = localWriteTimeMs;
1375
- this.baseMutations = baseMutations;
1376
- this.mutations = mutations;
1377
- }
1378
- }
1260
+ const DbMutationQueueKeyPath = 'userId';
1379
1261
  /** Name of the IndexedDb object store. */
1380
- DbMutationBatch.store = 'mutations';
1262
+ const DbMutationBatchStore = 'mutations';
1381
1263
  /** Keys are automatically assigned via the userId, batchId properties. */
1382
- DbMutationBatch.keyPath = 'batchId';
1264
+ const DbMutationBatchKeyPath = 'batchId';
1383
1265
  /** The index name for lookup of mutations by user. */
1384
- DbMutationBatch.userMutationsIndex = 'userMutationsIndex';
1266
+ const DbMutationBatchUserMutationsIndex = 'userMutationsIndex';
1385
1267
  /** The user mutations index is keyed by [userId, batchId] pairs. */
1386
- DbMutationBatch.userMutationsKeyPath = ['userId', 'batchId'];
1268
+ const DbMutationBatchUserMutationsKeyPath = ['userId', 'batchId'];
1387
1269
  /**
1388
- * An object to be stored in the 'documentMutations' store in IndexedDb.
1389
- *
1390
- * A manually maintained index of all the mutation batches that affect a given
1391
- * document key. The rows in this table are references based on the contents of
1392
- * DbMutationBatch.mutations.
1270
+ * Creates a [userId] key for use in the DbDocumentMutations index to iterate
1271
+ * over all of a user's document mutations.
1393
1272
  */
1394
- class DbDocumentMutation {
1395
- constructor() { }
1396
- /**
1397
- * Creates a [userId] key for use in the DbDocumentMutations index to iterate
1398
- * over all of a user's document mutations.
1399
- */
1400
- static prefixForUser(userId) {
1401
- return [userId];
1402
- }
1403
- /**
1404
- * Creates a [userId, encodedPath] key for use in the DbDocumentMutations
1405
- * index to iterate over all at document mutations for a given path or lower.
1406
- */
1407
- static prefixForPath(userId, path) {
1408
- return [userId, encodeResourcePath(path)];
1409
- }
1410
- /**
1411
- * Creates a full index key of [userId, encodedPath, batchId] for inserting
1412
- * and deleting into the DbDocumentMutations index.
1413
- */
1414
- static key(userId, path, batchId) {
1415
- return [userId, encodeResourcePath(path), batchId];
1416
- }
1273
+ function newDbDocumentMutationPrefixForUser(userId) {
1274
+ return [userId];
1417
1275
  }
1418
- DbDocumentMutation.store = 'documentMutations';
1419
1276
  /**
1420
- * Because we store all the useful information for this store in the key,
1421
- * there is no useful information to store as the value. The raw (unencoded)
1422
- * path cannot be stored because IndexedDb doesn't store prototype
1423
- * information.
1424
- */
1425
- DbDocumentMutation.PLACEHOLDER = new DbDocumentMutation();
1426
- /**
1427
- * Represents the known absence of a document at a particular version.
1428
- * Stored in IndexedDb as part of a DbRemoteDocument object.
1277
+ * Creates a [userId, encodedPath] key for use in the DbDocumentMutations
1278
+ * index to iterate over all at document mutations for a given path or lower.
1429
1279
  */
1430
- class DbNoDocument {
1431
- constructor(path, readTime) {
1432
- this.path = path;
1433
- this.readTime = readTime;
1434
- }
1280
+ function newDbDocumentMutationPrefixForPath(userId, path) {
1281
+ return [userId, encodeResourcePath(path)];
1435
1282
  }
1436
1283
  /**
1437
- * Represents a document that is known to exist but whose data is unknown.
1438
- * Stored in IndexedDb as part of a DbRemoteDocument object.
1284
+ * Creates a full index key of [userId, encodedPath, batchId] for inserting
1285
+ * and deleting into the DbDocumentMutations index.
1439
1286
  */
1440
- class DbUnknownDocument {
1441
- constructor(path, version) {
1442
- this.path = path;
1443
- this.version = version;
1444
- }
1287
+ function newDbDocumentMutationKey(userId, path, batchId) {
1288
+ return [userId, encodeResourcePath(path), batchId];
1445
1289
  }
1446
1290
  /**
1447
- * An object to be stored in the 'remoteDocuments' store in IndexedDb.
1448
- * It represents either:
1449
- *
1450
- * - A complete document.
1451
- * - A "no document" representing a document that is known not to exist (at
1452
- * some version).
1453
- * - An "unknown document" representing a document that is known to exist (at
1454
- * some version) but whose contents are unknown.
1455
- *
1456
- * Note: This is the persisted equivalent of a MaybeDocument and could perhaps
1457
- * be made more general if necessary.
1291
+ * Because we store all the useful information for this store in the key,
1292
+ * there is no useful information to store as the value. The raw (unencoded)
1293
+ * path cannot be stored because IndexedDb doesn't store prototype
1294
+ * information.
1458
1295
  */
1459
- class DbRemoteDocument {
1460
- // TODO: We are currently storing full document keys almost three times
1461
- // (once as part of the primary key, once - partly - as `parentPath` and once
1462
- // inside the encoded documents). During our next migration, we should
1463
- // rewrite the primary key as parentPath + document ID which would allow us
1464
- // to drop one value.
1465
- constructor(
1466
- /**
1467
- * Set to an instance of DbUnknownDocument if the data for a document is
1468
- * not known, but it is known that a document exists at the specified
1469
- * version (e.g. it had a successful update applied to it)
1470
- */
1471
- unknownDocument,
1472
- /**
1473
- * Set to an instance of a DbNoDocument if it is known that no document
1474
- * exists.
1475
- */
1476
- noDocument,
1477
- /**
1478
- * Set to an instance of a Document if there's a cached version of the
1479
- * document.
1480
- */
1481
- document,
1482
- /**
1483
- * Documents that were written to the remote document store based on
1484
- * a write acknowledgment are marked with `hasCommittedMutations`. These
1485
- * documents are potentially inconsistent with the backend's copy and use
1486
- * the write's commit version as their document version.
1487
- */
1488
- hasCommittedMutations,
1489
- /**
1490
- * When the document was read from the backend. Undefined for data written
1491
- * prior to schema version 9.
1492
- */
1493
- readTime,
1494
- /**
1495
- * The path of the collection this document is part of. Undefined for data
1496
- * written prior to schema version 9.
1497
- */
1498
- parentPath) {
1499
- this.unknownDocument = unknownDocument;
1500
- this.noDocument = noDocument;
1501
- this.document = document;
1502
- this.hasCommittedMutations = hasCommittedMutations;
1503
- this.readTime = readTime;
1504
- this.parentPath = parentPath;
1505
- }
1506
- }
1507
- DbRemoteDocument.store = 'remoteDocuments';
1296
+ const DbDocumentMutationPlaceholder = {};
1297
+ const DbDocumentMutationStore = 'documentMutations';
1298
+ const DbRemoteDocumentStore = 'remoteDocuments';
1508
1299
  /**
1509
1300
  * An index that provides access to all entries sorted by read time (which
1510
1301
  * corresponds to the last modification time of each row).
1511
1302
  *
1512
1303
  * This index is used to provide a changelog for Multi-Tab.
1513
1304
  */
1514
- DbRemoteDocument.readTimeIndex = 'readTimeIndex';
1515
- DbRemoteDocument.readTimeIndexPath = 'readTime';
1305
+ const DbRemoteDocumentReadTimeIndex = 'readTimeIndex';
1306
+ const DbRemoteDocumentReadTimeIndexPath = 'readTime';
1516
1307
  /**
1517
1308
  * An index that provides access to documents in a collection sorted by read
1518
1309
  * time.
@@ -1520,361 +1311,68 @@ DbRemoteDocument.readTimeIndexPath = 'readTime';
1520
1311
  * This index is used to allow the RemoteDocumentCache to fetch newly changed
1521
1312
  * documents in a collection.
1522
1313
  */
1523
- DbRemoteDocument.collectionReadTimeIndex = 'collectionReadTimeIndex';
1524
- DbRemoteDocument.collectionReadTimeIndexPath = ['parentPath', 'readTime'];
1525
- /**
1526
- * Contains a single entry that has metadata about the remote document cache.
1527
- */
1528
- class DbRemoteDocumentGlobal {
1529
- /**
1530
- * @param byteSize - Approximately the total size in bytes of all the
1531
- * documents in the document cache.
1532
- */
1533
- constructor(byteSize) {
1534
- this.byteSize = byteSize;
1535
- }
1536
- }
1537
- DbRemoteDocumentGlobal.store = 'remoteDocumentGlobal';
1538
- DbRemoteDocumentGlobal.key = 'remoteDocumentGlobalKey';
1539
- /**
1540
- * An object to be stored in the 'targets' store in IndexedDb.
1541
- *
1542
- * This is based on and should be kept in sync with the proto used in the iOS
1543
- * client.
1544
- *
1545
- * Each query the client listens to against the server is tracked on disk so
1546
- * that the query can be efficiently resumed on restart.
1547
- */
1548
- class DbTarget {
1549
- constructor(
1550
- /**
1551
- * An auto-generated sequential numeric identifier for the query.
1552
- *
1553
- * Queries are stored using their canonicalId as the key, but these
1554
- * canonicalIds can be quite long so we additionally assign a unique
1555
- * queryId which can be used by referenced data structures (e.g.
1556
- * indexes) to minimize the on-disk cost.
1557
- */
1558
- targetId,
1559
- /**
1560
- * The canonical string representing this query. This is not unique.
1561
- */
1562
- canonicalId,
1563
- /**
1564
- * The last readTime received from the Watch Service for this query.
1565
- *
1566
- * This is the same value as TargetChange.read_time in the protos.
1567
- */
1568
- readTime,
1569
- /**
1570
- * An opaque, server-assigned token that allows watching a query to be
1571
- * resumed after disconnecting without retransmitting all the data
1572
- * that matches the query. The resume token essentially identifies a
1573
- * point in time from which the server should resume sending results.
1574
- *
1575
- * This is related to the snapshotVersion in that the resumeToken
1576
- * effectively also encodes that value, but the resumeToken is opaque
1577
- * and sometimes encodes additional information.
1578
- *
1579
- * A consequence of this is that the resumeToken should be used when
1580
- * asking the server to reason about where this client is in the watch
1581
- * stream, but the client should use the snapshotVersion for its own
1582
- * purposes.
1583
- *
1584
- * This is the same value as TargetChange.resume_token in the protos.
1585
- */
1586
- resumeToken,
1587
- /**
1588
- * A sequence number representing the last time this query was
1589
- * listened to, used for garbage collection purposes.
1590
- *
1591
- * Conventionally this would be a timestamp value, but device-local
1592
- * clocks are unreliable and they must be able to create new listens
1593
- * even while disconnected. Instead this should be a monotonically
1594
- * increasing number that's incremented on each listen call.
1595
- *
1596
- * This is different from the queryId since the queryId is an
1597
- * immutable identifier assigned to the Query on first use while
1598
- * lastListenSequenceNumber is updated every time the query is
1599
- * listened to.
1600
- */
1601
- lastListenSequenceNumber,
1602
- /**
1603
- * Denotes the maximum snapshot version at which the associated query view
1604
- * contained no limbo documents. Undefined for data written prior to
1605
- * schema version 9.
1606
- */
1607
- lastLimboFreeSnapshotVersion,
1608
- /**
1609
- * The query for this target.
1610
- *
1611
- * Because canonical ids are not unique we must store the actual query. We
1612
- * use the proto to have an object we can persist without having to
1613
- * duplicate translation logic to and from a `Query` object.
1614
- */
1615
- query) {
1616
- this.targetId = targetId;
1617
- this.canonicalId = canonicalId;
1618
- this.readTime = readTime;
1619
- this.resumeToken = resumeToken;
1620
- this.lastListenSequenceNumber = lastListenSequenceNumber;
1621
- this.lastLimboFreeSnapshotVersion = lastLimboFreeSnapshotVersion;
1622
- this.query = query;
1623
- }
1624
- }
1625
- DbTarget.store = 'targets';
1314
+ const DbRemoteDocumentCollectionReadTimeIndex = 'collectionReadTimeIndex';
1315
+ const DbRemoteDocumentCollectionReadTimeIndexPath = [
1316
+ 'parentPath',
1317
+ 'readTime'
1318
+ ];
1319
+ const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
1320
+ const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
1321
+ const DbTargetStore = 'targets';
1626
1322
  /** Keys are automatically assigned via the targetId property. */
1627
- DbTarget.keyPath = 'targetId';
1323
+ const DbTargetKeyPath = 'targetId';
1628
1324
  /** The name of the queryTargets index. */
1629
- DbTarget.queryTargetsIndexName = 'queryTargetsIndex';
1325
+ const DbTargetQueryTargetsIndexName = 'queryTargetsIndex';
1630
1326
  /**
1631
1327
  * The index of all canonicalIds to the targets that they match. This is not
1632
1328
  * a unique mapping because canonicalId does not promise a unique name for all
1633
1329
  * possible queries, so we append the targetId to make the mapping unique.
1634
1330
  */
1635
- DbTarget.queryTargetsKeyPath = ['canonicalId', 'targetId'];
1636
- /**
1637
- * An object representing an association between a target and a document, or a
1638
- * sentinel row marking the last sequence number at which a document was used.
1639
- * Each document cached must have a corresponding sentinel row before lru
1640
- * garbage collection is enabled.
1641
- *
1642
- * The target associations and sentinel rows are co-located so that orphaned
1643
- * documents and their sequence numbers can be identified efficiently via a scan
1644
- * of this store.
1645
- */
1646
- class DbTargetDocument {
1647
- constructor(
1648
- /**
1649
- * The targetId identifying a target or 0 for a sentinel row.
1650
- */
1651
- targetId,
1652
- /**
1653
- * The path to the document, as encoded in the key.
1654
- */
1655
- path,
1656
- /**
1657
- * If this is a sentinel row, this should be the sequence number of the last
1658
- * time the document specified by `path` was used. Otherwise, it should be
1659
- * `undefined`.
1660
- */
1661
- sequenceNumber) {
1662
- this.targetId = targetId;
1663
- this.path = path;
1664
- this.sequenceNumber = sequenceNumber;
1665
- }
1666
- }
1331
+ const DbTargetQueryTargetsKeyPath = ['canonicalId', 'targetId'];
1667
1332
  /** Name of the IndexedDb object store. */
1668
- DbTargetDocument.store = 'targetDocuments';
1333
+ const DbTargetDocumentStore = 'targetDocuments';
1669
1334
  /** Keys are automatically assigned via the targetId, path properties. */
1670
- DbTargetDocument.keyPath = ['targetId', 'path'];
1335
+ const DbTargetDocumentKeyPath = ['targetId', 'path'];
1671
1336
  /** The index name for the reverse index. */
1672
- DbTargetDocument.documentTargetsIndex = 'documentTargetsIndex';
1337
+ const DbTargetDocumentDocumentTargetsIndex = 'documentTargetsIndex';
1673
1338
  /** We also need to create the reverse index for these properties. */
1674
- DbTargetDocument.documentTargetsKeyPath = ['path', 'targetId'];
1675
- /**
1676
- * A record of global state tracked across all Targets, tracked separately
1677
- * to avoid the need for extra indexes.
1678
- *
1679
- * This should be kept in-sync with the proto used in the iOS client.
1680
- */
1681
- class DbTargetGlobal {
1682
- constructor(
1683
- /**
1684
- * The highest numbered target id across all targets.
1685
- *
1686
- * See DbTarget.targetId.
1687
- */
1688
- highestTargetId,
1689
- /**
1690
- * The highest numbered lastListenSequenceNumber across all targets.
1691
- *
1692
- * See DbTarget.lastListenSequenceNumber.
1693
- */
1694
- highestListenSequenceNumber,
1695
- /**
1696
- * A global snapshot version representing the last consistent snapshot we
1697
- * received from the backend. This is monotonically increasing and any
1698
- * snapshots received from the backend prior to this version (e.g. for
1699
- * targets resumed with a resumeToken) should be suppressed (buffered)
1700
- * until the backend has caught up to this snapshot version again. This
1701
- * prevents our cache from ever going backwards in time.
1702
- */
1703
- lastRemoteSnapshotVersion,
1704
- /**
1705
- * The number of targets persisted.
1706
- */
1707
- targetCount) {
1708
- this.highestTargetId = highestTargetId;
1709
- this.highestListenSequenceNumber = highestListenSequenceNumber;
1710
- this.lastRemoteSnapshotVersion = lastRemoteSnapshotVersion;
1711
- this.targetCount = targetCount;
1712
- }
1713
- }
1339
+ const DbTargetDocumentDocumentTargetsKeyPath = ['path', 'targetId'];
1714
1340
  /**
1715
1341
  * The key string used for the single object that exists in the
1716
1342
  * DbTargetGlobal store.
1717
1343
  */
1718
- DbTargetGlobal.key = 'targetGlobalKey';
1719
- DbTargetGlobal.store = 'targetGlobal';
1720
- /**
1721
- * An object representing an association between a Collection id (e.g. 'messages')
1722
- * to a parent path (e.g. '/chats/123') that contains it as a (sub)collection.
1723
- * This is used to efficiently find all collections to query when performing
1724
- * a Collection Group query.
1725
- */
1726
- class DbCollectionParent {
1727
- constructor(
1728
- /**
1729
- * The collectionId (e.g. 'messages')
1730
- */
1731
- collectionId,
1732
- /**
1733
- * The path to the parent (either a document location or an empty path for
1734
- * a root-level collection).
1735
- */
1736
- parent) {
1737
- this.collectionId = collectionId;
1738
- this.parent = parent;
1739
- }
1740
- }
1344
+ const DbTargetGlobalKey = 'targetGlobalKey';
1345
+ const DbTargetGlobalStore = 'targetGlobal';
1741
1346
  /** Name of the IndexedDb object store. */
1742
- DbCollectionParent.store = 'collectionParents';
1347
+ const DbCollectionParentStore = 'collectionParents';
1743
1348
  /** Keys are automatically assigned via the collectionId, parent properties. */
1744
- DbCollectionParent.keyPath = ['collectionId', 'parent'];
1745
- /**
1746
- * A record of the metadata state of each client.
1747
- *
1748
- * PORTING NOTE: This is used to synchronize multi-tab state and does not need
1749
- * to be ported to iOS or Android.
1750
- */
1751
- class DbClientMetadata {
1752
- constructor(
1753
- // Note: Previous schema versions included a field
1754
- // "lastProcessedDocumentChangeId". Don't use anymore.
1755
- /** The auto-generated client id assigned at client startup. */
1756
- clientId,
1757
- /** The last time this state was updated. */
1758
- updateTimeMs,
1759
- /** Whether the client's network connection is enabled. */
1760
- networkEnabled,
1761
- /** Whether this client is running in a foreground tab. */
1762
- inForeground) {
1763
- this.clientId = clientId;
1764
- this.updateTimeMs = updateTimeMs;
1765
- this.networkEnabled = networkEnabled;
1766
- this.inForeground = inForeground;
1767
- }
1768
- }
1349
+ const DbCollectionParentKeyPath = ['collectionId', 'parent'];
1769
1350
  /** Name of the IndexedDb object store. */
1770
- DbClientMetadata.store = 'clientMetadata';
1351
+ const DbClientMetadataStore = 'clientMetadata';
1771
1352
  /** Keys are automatically assigned via the clientId properties. */
1772
- DbClientMetadata.keyPath = 'clientId';
1773
- /** An object representing a bundle loaded by the SDK. */
1774
- class DbBundle {
1775
- constructor(
1776
- /** The ID of the loaded bundle. */
1777
- bundleId,
1778
- /** The create time of the loaded bundle. */
1779
- createTime,
1780
- /** The schema version of the loaded bundle. */
1781
- version) {
1782
- this.bundleId = bundleId;
1783
- this.createTime = createTime;
1784
- this.version = version;
1785
- }
1786
- }
1353
+ const DbClientMetadataKeyPath = 'clientId';
1787
1354
  /** Name of the IndexedDb object store. */
1788
- DbBundle.store = 'bundles';
1789
- DbBundle.keyPath = 'bundleId';
1790
- /** An object representing a named query loaded by the SDK via a bundle. */
1791
- class DbNamedQuery {
1792
- constructor(
1793
- /** The name of the query. */
1794
- name,
1795
- /** The read time of the results saved in the bundle from the named query. */
1796
- readTime,
1797
- /** The query saved in the bundle. */
1798
- bundledQuery) {
1799
- this.name = name;
1800
- this.readTime = readTime;
1801
- this.bundledQuery = bundledQuery;
1802
- }
1803
- }
1355
+ const DbBundleStore = 'bundles';
1356
+ const DbBundleKeyPath = 'bundleId';
1804
1357
  /** Name of the IndexedDb object store. */
1805
- DbNamedQuery.store = 'namedQueries';
1806
- DbNamedQuery.keyPath = 'name';
1807
- /** An object representing the global configuration for a field index. */
1808
- class DbIndexConfiguration {
1809
- constructor(
1810
- /**
1811
- * The index id for this entry. Undefined for indexes that are not yet
1812
- * persisted.
1813
- */
1814
- indexId,
1815
- /** The collection group this index belongs to. */
1816
- collectionGroup,
1817
- /** The fields to index for this index. */
1818
- fields) {
1819
- this.indexId = indexId;
1820
- this.collectionGroup = collectionGroup;
1821
- this.fields = fields;
1822
- }
1823
- }
1358
+ const DbNamedQueryStore = 'namedQueries';
1359
+ const DbNamedQueryKeyPath = 'name';
1824
1360
  /** Name of the IndexedDb object store. */
1825
- DbIndexConfiguration.store = 'indexConfiguration';
1826
- DbIndexConfiguration.keyPath = 'indexId';
1361
+ const DbIndexConfigurationStore = 'indexConfiguration';
1362
+ const DbIndexConfigurationKeyPath = 'indexId';
1827
1363
  /**
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';
1837
- /**
1838
- * An object describing how up-to-date the index backfill is for each user and
1839
- * index.
1840
- */
1841
- class DbIndexState {
1842
- constructor(
1843
- /** The index id for this entry. */
1844
- indexId,
1845
- /** The user id for this entry. */
1846
- uid,
1847
- /**
1848
- * A number that indicates when the index was last updated (relative to
1849
- * other indexes).
1850
- */
1851
- sequenceNumber,
1852
- /**
1853
- * The latest read time that has been indexed by Firestore for this field
1854
- * index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
1855
- */
1856
- readTime,
1857
- /**
1858
- * The last document that has been indexed for this field index. Empty if
1859
- * no documents have been indexed.
1860
- */
1861
- documentKey,
1862
- /**
1863
- * The largest mutation batch id that has been processed for this index. -1
1864
- * if no mutations have been indexed.
1865
- */
1866
- largestBatchId) {
1867
- this.indexId = indexId;
1868
- this.uid = uid;
1869
- this.sequenceNumber = sequenceNumber;
1870
- this.readTime = readTime;
1871
- this.documentKey = documentKey;
1872
- this.largestBatchId = largestBatchId;
1873
- }
1874
- }
1364
+ * An index that provides access to the index configurations by collection
1365
+ * group.
1366
+ *
1367
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1368
+ * not possible here as the Web client supports concurrent access to
1369
+ * persistence via multi-tab.
1370
+ */
1371
+ const DbIndexConfigurationCollectionGroupIndex = 'collectionGroupIndex';
1372
+ const DbIndexConfigurationCollectionGroupIndexPath = 'collectionGroup';
1875
1373
  /** Name of the IndexedDb object store. */
1876
- DbIndexState.store = 'indexState';
1877
- DbIndexState.keyPath = ['indexId', 'uid'];
1374
+ const DbIndexStateStore = 'indexState';
1375
+ const DbIndexStateKeyPath = ['indexId', 'uid'];
1878
1376
  /**
1879
1377
  * An index that provides access to documents in a collection sorted by last
1880
1378
  * update time. Used by the backfiller.
@@ -1883,108 +1381,66 @@ DbIndexState.keyPath = ['indexId', 'uid'];
1883
1381
  * not possible here as the Web client supports concurrent access to
1884
1382
  * persistence via multi-tab.
1885
1383
  */
1886
- DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1887
- DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1888
- /** An object that stores the encoded entries for all documents and fields. */
1889
- class DbIndexEntry {
1890
- constructor(
1891
- /** The index id for this entry. */
1892
- indexId,
1893
- /** The user id for this entry. */
1894
- uid,
1895
- /** The encoded array index value for this entry. */
1896
- arrayValue,
1897
- /** The encoded directional value for equality and inequality filters. */
1898
- directionalValue,
1899
- /** The document key this entry points to. */
1900
- documentKey) {
1901
- this.indexId = indexId;
1902
- this.uid = uid;
1903
- this.arrayValue = arrayValue;
1904
- this.directionalValue = directionalValue;
1905
- this.documentKey = documentKey;
1906
- }
1907
- }
1384
+ const DbIndexStateSequenceNumberIndex = 'sequenceNumberIndex';
1385
+ const DbIndexStateSequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1908
1386
  /** Name of the IndexedDb object store. */
1909
- DbIndexEntry.store = 'indexEntries';
1910
- DbIndexEntry.keyPath = [
1387
+ const DbIndexEntryStore = 'indexEntries';
1388
+ const DbIndexEntryKeyPath = [
1911
1389
  'indexId',
1912
1390
  'uid',
1913
1391
  'arrayValue',
1914
1392
  'directionalValue',
1915
1393
  'documentKey'
1916
1394
  ];
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
- }
1395
+ const DbIndexEntryDocumentKeyIndex = 'documentKeyIndex';
1396
+ const DbIndexEntryDocumentKeyIndexPath = [
1397
+ 'indexId',
1398
+ 'uid',
1399
+ 'documentKey'
1400
+ ];
1944
1401
  /** Name of the IndexedDb object store. */
1945
- DbDocumentOverlay.store = 'documentOverlays';
1946
- DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
1947
- DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
1948
- DbDocumentOverlay.collectionPathOverlayIndexPath = [
1402
+ const DbDocumentOverlayStore = 'documentOverlays';
1403
+ const DbDocumentOverlayKeyPath = [
1404
+ 'userId',
1405
+ 'collectionPath',
1406
+ 'documentId'
1407
+ ];
1408
+ const DbDocumentOverlayCollectionPathOverlayIndex = 'collectionPathOverlayIndex';
1409
+ const DbDocumentOverlayCollectionPathOverlayIndexPath = [
1949
1410
  'userId',
1950
1411
  'collectionPath',
1951
1412
  'largestBatchId'
1952
1413
  ];
1953
- DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1954
- DbDocumentOverlay.collectionGroupOverlayIndexPath = [
1414
+ const DbDocumentOverlayCollectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
1415
+ const DbDocumentOverlayCollectionGroupOverlayIndexPath = [
1955
1416
  'userId',
1956
1417
  'collectionGroup',
1957
1418
  'largestBatchId'
1958
1419
  ];
1959
1420
  // Visible for testing
1960
1421
  const V1_STORES = [
1961
- DbMutationQueue.store,
1962
- DbMutationBatch.store,
1963
- DbDocumentMutation.store,
1964
- DbRemoteDocument.store,
1965
- DbTarget.store,
1966
- DbPrimaryClient.store,
1967
- DbTargetGlobal.store,
1968
- DbTargetDocument.store
1422
+ DbMutationQueueStore,
1423
+ DbMutationBatchStore,
1424
+ DbDocumentMutationStore,
1425
+ DbRemoteDocumentStore,
1426
+ DbTargetStore,
1427
+ DbPrimaryClientStore,
1428
+ DbTargetGlobalStore,
1429
+ DbTargetDocumentStore
1969
1430
  ];
1970
- // V2 is no longer usable (see comment at top of file)
1971
1431
  // Visible for testing
1972
1432
  const V3_STORES = V1_STORES;
1973
1433
  // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
1974
- const V4_STORES = [...V3_STORES, DbClientMetadata.store];
1975
- // V5 does not change the set of stores.
1976
- const V6_STORES = [...V4_STORES, DbRemoteDocumentGlobal.store];
1977
- // V7 does not change the set of stores.
1978
- const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1979
- // V9 does not change the set of stores.
1980
- // V10 does not change the set of stores.
1981
- const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1982
- const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
1434
+ const V4_STORES = [...V3_STORES, DbClientMetadataStore];
1435
+ const V6_STORES = [...V4_STORES, DbRemoteDocumentGlobalStore];
1436
+ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
1437
+ const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
1438
+ const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
1983
1439
  const V13_STORES = [
1984
1440
  ...V12_STORES,
1985
- DbIndexConfiguration.store,
1986
- DbIndexState.store,
1987
- DbIndexEntry.store
1441
+ DbIndexConfigurationStore,
1442
+ DbIndexStateStore,
1443
+ DbIndexEntryStore
1988
1444
  ];
1989
1445
  /** Returns the object stores for the provided schema. */
1990
1446
  function getObjectStores(schemaVersion) {
@@ -2702,6 +2158,21 @@ class SimpleDbStore {
2702
2158
  });
2703
2159
  }
2704
2160
  }
2161
+ /**
2162
+ * Loads the first `count` elements from the provided index range. Loads all
2163
+ * elements if no limit is provided.
2164
+ */
2165
+ loadFirst(range, count) {
2166
+ const request = this.store.getAll(range, count === null ? undefined : count);
2167
+ return new PersistencePromise((resolve, reject) => {
2168
+ request.onerror = (event) => {
2169
+ reject(event.target.error);
2170
+ };
2171
+ request.onsuccess = (event) => {
2172
+ resolve(event.target.result);
2173
+ };
2174
+ });
2175
+ }
2705
2176
  deleteAll(indexOrRange, range) {
2706
2177
  logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
2707
2178
  const options = this.options(indexOrRange, range);
@@ -3312,17 +2783,15 @@ class FieldMask {
3312
2783
  * See the License for the specific language governing permissions and
3313
2784
  * limitations under the License.
3314
2785
  */
2786
+ /** Converts a Base64 encoded string to a binary string. */
3315
2787
  function decodeBase64(encoded) {
3316
- // Node actually doesn't validate base64 strings.
3317
- // A quick sanity check that is not a fool-proof validation
3318
- if (/[^-A-Za-z0-9+/=]/.test(encoded)) {
3319
- throw new FirestoreError(Code.INVALID_ARGUMENT, 'Not a valid Base64 string: ' + encoded);
3320
- }
3321
- return new Buffer(encoded, 'base64').toString('binary');
2788
+ // Note: We used to validate the base64 string here via a regular expression.
2789
+ // This was removed to improve the performance of indexing.
2790
+ return Buffer.from(encoded, 'base64').toString('binary');
3322
2791
  }
3323
2792
  /** Converts a binary string to a Base64 encoded string. */
3324
2793
  function encodeBase64(raw) {
3325
- return new Buffer(raw, 'binary').toString('base64');
2794
+ return Buffer.from(raw, 'binary').toString('base64');
3326
2795
  }
3327
2796
  /** True if and only if the Base64 conversion functions are available. */
3328
2797
  function isBase64Available() {
@@ -3363,6 +2832,8 @@ class ByteString {
3363
2832
  return new ByteString(binaryString);
3364
2833
  }
3365
2834
  static fromUint8Array(array) {
2835
+ // TODO(indexing); Remove the copy of the byte string here as this method
2836
+ // is frequently called during indexing.
3366
2837
  const binaryString = binaryStringFromUint8Array(array);
3367
2838
  return new ByteString(binaryString);
3368
2839
  }
@@ -3781,13 +3252,17 @@ class DocumentKey {
3781
3252
  * See the License for the specific language governing permissions and
3782
3253
  * limitations under the License.
3783
3254
  */
3255
+ const MAX_VALUE_TYPE = '__max__';
3784
3256
  const MAX_VALUE = {
3785
3257
  mapValue: {
3786
3258
  fields: {
3787
- '__type__': { stringValue: '__max___' }
3259
+ '__type__': { stringValue: MAX_VALUE_TYPE }
3788
3260
  }
3789
3261
  }
3790
3262
  };
3263
+ const MIN_VALUE = {
3264
+ nullValue: 'NULL_VALUE'
3265
+ };
3791
3266
  /** Extracts the backend's type order for the provided value. */
3792
3267
  function typeOrder(value) {
3793
3268
  if ('nullValue' in value) {
@@ -3821,6 +3296,9 @@ function typeOrder(value) {
3821
3296
  if (isServerTimestamp(value)) {
3822
3297
  return 4 /* ServerTimestampValue */;
3823
3298
  }
3299
+ else if (isMaxValue(value)) {
3300
+ return 9 /* ArrayValue */;
3301
+ }
3824
3302
  return 10 /* ObjectValue */;
3825
3303
  }
3826
3304
  else {
@@ -3860,6 +3338,8 @@ function valueEquals(left, right) {
3860
3338
  return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
3861
3339
  case 10 /* ObjectValue */:
3862
3340
  return objectEquals(left, right);
3341
+ case 9007199254740991 /* MaxValue */:
3342
+ return true;
3863
3343
  default:
3864
3344
  return fail();
3865
3345
  }
@@ -3932,6 +3412,7 @@ function valueCompare(left, right) {
3932
3412
  }
3933
3413
  switch (leftType) {
3934
3414
  case 0 /* NullValue */:
3415
+ case 9007199254740991 /* MaxValue */:
3935
3416
  return 0;
3936
3417
  case 1 /* BooleanValue */:
3937
3418
  return primitiveComparator(left.booleanValue, right.booleanValue);
@@ -4200,7 +3681,102 @@ function deepClone(source) {
4200
3681
  }
4201
3682
  /** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
4202
3683
  function isMaxValue(value) {
4203
- return valueEquals(value, MAX_VALUE);
3684
+ return ((((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue ===
3685
+ MAX_VALUE_TYPE);
3686
+ }
3687
+ /** Returns the lowest value for the given value type (inclusive). */
3688
+ function valuesGetLowerBound(value) {
3689
+ if ('nullValue' in value) {
3690
+ return MIN_VALUE;
3691
+ }
3692
+ else if ('booleanValue' in value) {
3693
+ return { booleanValue: false };
3694
+ }
3695
+ else if ('integerValue' in value || 'doubleValue' in value) {
3696
+ return { doubleValue: NaN };
3697
+ }
3698
+ else if ('timestampValue' in value) {
3699
+ return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } };
3700
+ }
3701
+ else if ('stringValue' in value) {
3702
+ return { stringValue: '' };
3703
+ }
3704
+ else if ('bytesValue' in value) {
3705
+ return { bytesValue: '' };
3706
+ }
3707
+ else if ('referenceValue' in value) {
3708
+ return refValue(DatabaseId.empty(), DocumentKey.empty());
3709
+ }
3710
+ else if ('geoPointValue' in value) {
3711
+ return { geoPointValue: { latitude: -90, longitude: -180 } };
3712
+ }
3713
+ else if ('arrayValue' in value) {
3714
+ return { arrayValue: {} };
3715
+ }
3716
+ else if ('mapValue' in value) {
3717
+ return { mapValue: {} };
3718
+ }
3719
+ else {
3720
+ return fail();
3721
+ }
3722
+ }
3723
+ /** Returns the largest value for the given value type (exclusive). */
3724
+ function valuesGetUpperBound(value) {
3725
+ if ('nullValue' in value) {
3726
+ return { booleanValue: false };
3727
+ }
3728
+ else if ('booleanValue' in value) {
3729
+ return { doubleValue: NaN };
3730
+ }
3731
+ else if ('integerValue' in value || 'doubleValue' in value) {
3732
+ return { timestampValue: { seconds: Number.MIN_SAFE_INTEGER } };
3733
+ }
3734
+ else if ('timestampValue' in value) {
3735
+ return { stringValue: '' };
3736
+ }
3737
+ else if ('stringValue' in value) {
3738
+ return { bytesValue: '' };
3739
+ }
3740
+ else if ('bytesValue' in value) {
3741
+ return refValue(DatabaseId.empty(), DocumentKey.empty());
3742
+ }
3743
+ else if ('referenceValue' in value) {
3744
+ return { geoPointValue: { latitude: -90, longitude: -180 } };
3745
+ }
3746
+ else if ('geoPointValue' in value) {
3747
+ return { arrayValue: {} };
3748
+ }
3749
+ else if ('arrayValue' in value) {
3750
+ return { mapValue: {} };
3751
+ }
3752
+ else if ('mapValue' in value) {
3753
+ return MAX_VALUE;
3754
+ }
3755
+ else {
3756
+ return fail();
3757
+ }
3758
+ }
3759
+ function valuesMax(left, right) {
3760
+ if (left === undefined) {
3761
+ return right;
3762
+ }
3763
+ else if (right === undefined) {
3764
+ return left;
3765
+ }
3766
+ else {
3767
+ return valueCompare(left, right) > 0 ? left : right;
3768
+ }
3769
+ }
3770
+ function valuesMin(left, right) {
3771
+ if (left === undefined) {
3772
+ return right;
3773
+ }
3774
+ else if (right === undefined) {
3775
+ return left;
3776
+ }
3777
+ else {
3778
+ return valueCompare(left, right) < 0 ? left : right;
3779
+ }
4204
3780
  }
4205
3781
 
4206
3782
  /**
@@ -4595,6 +4171,10 @@ function fieldIndexGetArraySegment(fieldIndex) {
4595
4171
  function fieldIndexGetDirectionalSegments(fieldIndex) {
4596
4172
  return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
4597
4173
  }
4174
+ /** Returns a debug representation of the field index */
4175
+ function fieldIndexToString(fieldIndex) {
4176
+ return `id=${fieldIndex.indexId}|cg=${fieldIndex.collectionGroup}|f=${fieldIndex.fields.map(f => `${f.fieldPath}:${f.kind}`).join(',')}`;
4177
+ }
4598
4178
  /** An index component consisting of field path and index type. */
4599
4179
  class IndexSegment {
4600
4180
  constructor(
@@ -4791,6 +4371,191 @@ function targetIsDocumentTarget(target) {
4791
4371
  target.collectionGroup === null &&
4792
4372
  target.filters.length === 0);
4793
4373
  }
4374
+ /** Returns the field filters that target the given field path. */
4375
+ function targetGetFieldFiltersForPath(target, path) {
4376
+ return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
4377
+ }
4378
+ /**
4379
+ * Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
4380
+ * filters. Returns `null` if there are no such filters.
4381
+ */
4382
+ function targetGetArrayValues(target, fieldIndex) {
4383
+ const segment = fieldIndexGetArraySegment(fieldIndex);
4384
+ if (segment === undefined) {
4385
+ return null;
4386
+ }
4387
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
4388
+ switch (fieldFilter.op) {
4389
+ case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
4390
+ return fieldFilter.value.arrayValue.values || [];
4391
+ case "array-contains" /* ARRAY_CONTAINS */:
4392
+ return [fieldFilter.value];
4393
+ // Remaining filters are not array filters.
4394
+ }
4395
+ }
4396
+ return null;
4397
+ }
4398
+ /**
4399
+ * Returns the list of values that are used in != or NOT_IN filters. Returns
4400
+ * `null` if there are no such filters.
4401
+ */
4402
+ function targetGetNotInValues(target, fieldIndex) {
4403
+ const values = new Map();
4404
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
4405
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
4406
+ switch (fieldFilter.op) {
4407
+ case "==" /* EQUAL */:
4408
+ case "in" /* IN */:
4409
+ // Encode equality prefix, which is encoded in the index value before
4410
+ // the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
4411
+ // `value != 'ab'`).
4412
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
4413
+ break;
4414
+ case "not-in" /* NOT_IN */:
4415
+ case "!=" /* NOT_EQUAL */:
4416
+ // NotIn/NotEqual is always a suffix. There cannot be any remaining
4417
+ // segments and hence we can return early here.
4418
+ values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
4419
+ return Array.from(values.values());
4420
+ // Remaining filters cannot be used as notIn bounds.
4421
+ }
4422
+ }
4423
+ }
4424
+ return null;
4425
+ }
4426
+ /**
4427
+ * Returns a lower bound of field values that can be used as a starting point to
4428
+ * scan the index defined by `fieldIndex`. Returns `null` if no lower bound
4429
+ * exists.
4430
+ */
4431
+ function targetGetLowerBound(target, fieldIndex) {
4432
+ const values = [];
4433
+ let inclusive = true;
4434
+ // For each segment, retrieve a lower bound if there is a suitable filter or
4435
+ // startAt.
4436
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
4437
+ let segmentValue = undefined;
4438
+ let segmentInclusive = true;
4439
+ // Process all filters to find a value for the current field segment
4440
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
4441
+ let filterValue = undefined;
4442
+ let filterInclusive = true;
4443
+ switch (fieldFilter.op) {
4444
+ case "<" /* LESS_THAN */:
4445
+ case "<=" /* LESS_THAN_OR_EQUAL */:
4446
+ filterValue = valuesGetLowerBound(fieldFilter.value);
4447
+ break;
4448
+ case "==" /* EQUAL */:
4449
+ case "in" /* IN */:
4450
+ case ">=" /* GREATER_THAN_OR_EQUAL */:
4451
+ filterValue = fieldFilter.value;
4452
+ break;
4453
+ case ">" /* GREATER_THAN */:
4454
+ filterValue = fieldFilter.value;
4455
+ filterInclusive = false;
4456
+ break;
4457
+ case "!=" /* NOT_EQUAL */:
4458
+ case "not-in" /* NOT_IN */:
4459
+ filterValue = MIN_VALUE;
4460
+ break;
4461
+ // Remaining filters cannot be used as lower bounds.
4462
+ }
4463
+ if (valuesMax(segmentValue, filterValue) === filterValue) {
4464
+ segmentValue = filterValue;
4465
+ segmentInclusive = filterInclusive;
4466
+ }
4467
+ }
4468
+ // If there is a startAt bound, compare the values against the existing
4469
+ // boundary to see if we can narrow the scope.
4470
+ if (target.startAt !== null) {
4471
+ for (let i = 0; i < target.orderBy.length; ++i) {
4472
+ const orderBy = target.orderBy[i];
4473
+ if (orderBy.field.isEqual(segment.fieldPath)) {
4474
+ const cursorValue = target.startAt.position[i];
4475
+ if (valuesMax(segmentValue, cursorValue) === cursorValue) {
4476
+ segmentValue = cursorValue;
4477
+ segmentInclusive = target.startAt.inclusive;
4478
+ }
4479
+ break;
4480
+ }
4481
+ }
4482
+ }
4483
+ if (segmentValue === undefined) {
4484
+ // No lower bound exists
4485
+ return null;
4486
+ }
4487
+ values.push(segmentValue);
4488
+ inclusive && (inclusive = segmentInclusive);
4489
+ }
4490
+ return new Bound(values, inclusive);
4491
+ }
4492
+ /**
4493
+ * Returns an upper bound of field values that can be used as an ending point
4494
+ * when scanning the index defined by `fieldIndex`. Returns `null` if no
4495
+ * upper bound exists.
4496
+ */
4497
+ function targetGetUpperBound(target, fieldIndex) {
4498
+ const values = [];
4499
+ let inclusive = true;
4500
+ // For each segment, retrieve an upper bound if there is a suitable filter or
4501
+ // endAt.
4502
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
4503
+ let segmentValue = undefined;
4504
+ let segmentInclusive = true;
4505
+ // Process all filters to find a value for the current field segment
4506
+ for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
4507
+ let filterValue = undefined;
4508
+ let filterInclusive = true;
4509
+ switch (fieldFilter.op) {
4510
+ case ">=" /* GREATER_THAN_OR_EQUAL */:
4511
+ case ">" /* GREATER_THAN */:
4512
+ filterValue = valuesGetUpperBound(fieldFilter.value);
4513
+ filterInclusive = false;
4514
+ break;
4515
+ case "==" /* EQUAL */:
4516
+ case "in" /* IN */:
4517
+ case "<=" /* LESS_THAN_OR_EQUAL */:
4518
+ filterValue = fieldFilter.value;
4519
+ break;
4520
+ case "<" /* LESS_THAN */:
4521
+ filterValue = fieldFilter.value;
4522
+ filterInclusive = false;
4523
+ break;
4524
+ case "!=" /* NOT_EQUAL */:
4525
+ case "not-in" /* NOT_IN */:
4526
+ filterValue = MAX_VALUE;
4527
+ break;
4528
+ // Remaining filters cannot be used as upper bounds.
4529
+ }
4530
+ if (valuesMin(segmentValue, filterValue) === filterValue) {
4531
+ segmentValue = filterValue;
4532
+ segmentInclusive = filterInclusive;
4533
+ }
4534
+ }
4535
+ // If there is a endAt bound, compare the values against the existing
4536
+ // boundary to see if we can narrow the scope.
4537
+ if (target.endAt !== null) {
4538
+ for (let i = 0; i < target.orderBy.length; ++i) {
4539
+ const orderBy = target.orderBy[i];
4540
+ if (orderBy.field.isEqual(segment.fieldPath)) {
4541
+ const cursorValue = target.endAt.position[i];
4542
+ if (valuesMin(segmentValue, cursorValue) === cursorValue) {
4543
+ segmentValue = cursorValue;
4544
+ segmentInclusive = target.endAt.inclusive;
4545
+ }
4546
+ break;
4547
+ }
4548
+ }
4549
+ }
4550
+ if (segmentValue === undefined) {
4551
+ // No upper bound exists
4552
+ return null;
4553
+ }
4554
+ values.push(segmentValue);
4555
+ inclusive && (inclusive = segmentInclusive);
4556
+ }
4557
+ return new Bound(values, inclusive);
4558
+ }
4794
4559
  class Filter {
4795
4560
  }
4796
4561
  class FieldFilter extends Filter {
@@ -5392,6 +5157,116 @@ function compareDocs(orderBy, d1, d2) {
5392
5157
  }
5393
5158
  }
5394
5159
 
5160
+ /**
5161
+ * @license
5162
+ * Copyright 2017 Google LLC
5163
+ *
5164
+ * Licensed under the Apache License, Version 2.0 (the "License");
5165
+ * you may not use this file except in compliance with the License.
5166
+ * You may obtain a copy of the License at
5167
+ *
5168
+ * http://www.apache.org/licenses/LICENSE-2.0
5169
+ *
5170
+ * Unless required by applicable law or agreed to in writing, software
5171
+ * distributed under the License is distributed on an "AS IS" BASIS,
5172
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5173
+ * See the License for the specific language governing permissions and
5174
+ * limitations under the License.
5175
+ */
5176
+ /**
5177
+ * A map implementation that uses objects as keys. Objects must have an
5178
+ * associated equals function and must be immutable. Entries in the map are
5179
+ * stored together with the key being produced from the mapKeyFn. This map
5180
+ * automatically handles collisions of keys.
5181
+ */
5182
+ class ObjectMap {
5183
+ constructor(mapKeyFn, equalsFn) {
5184
+ this.mapKeyFn = mapKeyFn;
5185
+ this.equalsFn = equalsFn;
5186
+ /**
5187
+ * The inner map for a key/value pair. Due to the possibility of collisions we
5188
+ * keep a list of entries that we do a linear search through to find an actual
5189
+ * match. Note that collisions should be rare, so we still expect near
5190
+ * constant time lookups in practice.
5191
+ */
5192
+ this.inner = {};
5193
+ /** The number of entries stored in the map */
5194
+ this.innerSize = 0;
5195
+ }
5196
+ /** Get a value for this key, or undefined if it does not exist. */
5197
+ get(key) {
5198
+ const id = this.mapKeyFn(key);
5199
+ const matches = this.inner[id];
5200
+ if (matches === undefined) {
5201
+ return undefined;
5202
+ }
5203
+ for (const [otherKey, value] of matches) {
5204
+ if (this.equalsFn(otherKey, key)) {
5205
+ return value;
5206
+ }
5207
+ }
5208
+ return undefined;
5209
+ }
5210
+ has(key) {
5211
+ return this.get(key) !== undefined;
5212
+ }
5213
+ /** Put this key and value in the map. */
5214
+ set(key, value) {
5215
+ const id = this.mapKeyFn(key);
5216
+ const matches = this.inner[id];
5217
+ if (matches === undefined) {
5218
+ this.inner[id] = [[key, value]];
5219
+ this.innerSize++;
5220
+ return;
5221
+ }
5222
+ for (let i = 0; i < matches.length; i++) {
5223
+ if (this.equalsFn(matches[i][0], key)) {
5224
+ // This is updating an existing entry and does not increase `innerSize`.
5225
+ matches[i] = [key, value];
5226
+ return;
5227
+ }
5228
+ }
5229
+ matches.push([key, value]);
5230
+ this.innerSize++;
5231
+ }
5232
+ /**
5233
+ * Remove this key from the map. Returns a boolean if anything was deleted.
5234
+ */
5235
+ delete(key) {
5236
+ const id = this.mapKeyFn(key);
5237
+ const matches = this.inner[id];
5238
+ if (matches === undefined) {
5239
+ return false;
5240
+ }
5241
+ for (let i = 0; i < matches.length; i++) {
5242
+ if (this.equalsFn(matches[i][0], key)) {
5243
+ if (matches.length === 1) {
5244
+ delete this.inner[id];
5245
+ }
5246
+ else {
5247
+ matches.splice(i, 1);
5248
+ }
5249
+ this.innerSize--;
5250
+ return true;
5251
+ }
5252
+ }
5253
+ return false;
5254
+ }
5255
+ forEach(fn) {
5256
+ forEach(this.inner, (_, entries) => {
5257
+ for (const [k, v] of entries) {
5258
+ fn(k, v);
5259
+ }
5260
+ });
5261
+ }
5262
+ isEmpty() {
5263
+ return isEmpty(this.inner);
5264
+ }
5265
+ size() {
5266
+ return this.innerSize;
5267
+ }
5268
+ }
5269
+
5395
5270
  /**
5396
5271
  * @license
5397
5272
  * Copyright 2017 Google LLC
@@ -5534,7 +5409,7 @@ class SortedMapIterator {
5534
5409
  while (!node.isEmpty()) {
5535
5410
  cmp = startKey ? comparator(node.key, startKey) : 1;
5536
5411
  // flip the comparison if we're going in reverse
5537
- if (isReverse) {
5412
+ if (startKey && isReverse) {
5538
5413
  cmp *= -1;
5539
5414
  }
5540
5415
  if (cmp < 0) {
@@ -6103,6 +5978,9 @@ const EMPTY_DOCUMENT_MAP = new SortedMap(DocumentKey.comparator);
6103
5978
  function documentMap() {
6104
5979
  return EMPTY_DOCUMENT_MAP;
6105
5980
  }
5981
+ function newOverlayMap() {
5982
+ return new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
5983
+ }
6106
5984
  const EMPTY_DOCUMENT_VERSION_MAP = new SortedMap(DocumentKey.comparator);
6107
5985
  function documentVersionMap() {
6108
5986
  return EMPTY_DOCUMENT_VERSION_MAP;
@@ -8781,30 +8659,36 @@ function fromDbRemoteDocument(localSerializer, remoteDoc) {
8781
8659
  }
8782
8660
  /** Encodes a document for storage locally. */
8783
8661
  function toDbRemoteDocument(localSerializer, document) {
8784
- const dbReadTime = toDbTimestampKey(document.readTime);
8785
8662
  const parentPath = document.key.path.popLast().toArray();
8663
+ const readTime = toDbTimestampKey(document.readTime);
8786
8664
  if (document.isFoundDocument()) {
8787
8665
  const doc = toDocument(localSerializer.remoteSerializer, document);
8788
8666
  const hasCommittedMutations = document.hasCommittedMutations;
8789
- return new DbRemoteDocument(
8790
- /* unknownDocument= */ null,
8791
- /* noDocument= */ null, doc, hasCommittedMutations, dbReadTime, parentPath);
8667
+ return {
8668
+ document: doc,
8669
+ hasCommittedMutations,
8670
+ readTime,
8671
+ parentPath
8672
+ };
8792
8673
  }
8793
8674
  else if (document.isNoDocument()) {
8794
8675
  const path = document.key.path.toArray();
8795
- const version = toDbTimestamp(document.version);
8796
8676
  const hasCommittedMutations = document.hasCommittedMutations;
8797
- return new DbRemoteDocument(
8798
- /* unknownDocument= */ null, new DbNoDocument(path, version),
8799
- /* document= */ null, hasCommittedMutations, dbReadTime, parentPath);
8677
+ return {
8678
+ noDocument: { path, readTime: toDbTimestamp(document.version) },
8679
+ hasCommittedMutations,
8680
+ readTime,
8681
+ parentPath
8682
+ };
8800
8683
  }
8801
8684
  else if (document.isUnknownDocument()) {
8802
8685
  const path = document.key.path.toArray();
8803
- const readTime = toDbTimestamp(document.version);
8804
- return new DbRemoteDocument(new DbUnknownDocument(path, readTime),
8805
- /* noDocument= */ null,
8806
- /* document= */ null,
8807
- /* hasCommittedMutations= */ true, dbReadTime, parentPath);
8686
+ return {
8687
+ unknownDocument: { path, version: toDbTimestamp(document.version) },
8688
+ hasCommittedMutations: true,
8689
+ readTime,
8690
+ parentPath
8691
+ };
8808
8692
  }
8809
8693
  else {
8810
8694
  return fail();
@@ -8820,7 +8704,7 @@ function fromDbTimestampKey(dbTimestampKey) {
8820
8704
  }
8821
8705
  function toDbTimestamp(snapshotVersion) {
8822
8706
  const timestamp = snapshotVersion.toTimestamp();
8823
- return new DbTimestamp(timestamp.seconds, timestamp.nanoseconds);
8707
+ return { seconds: timestamp.seconds, nanoseconds: timestamp.nanoseconds };
8824
8708
  }
8825
8709
  function fromDbTimestamp(dbTimestamp) {
8826
8710
  const timestamp = new Timestamp(dbTimestamp.seconds, dbTimestamp.nanoseconds);
@@ -8830,7 +8714,13 @@ function fromDbTimestamp(dbTimestamp) {
8830
8714
  function toDbMutationBatch(localSerializer, userId, batch) {
8831
8715
  const serializedBaseMutations = batch.baseMutations.map(m => toMutation(localSerializer.remoteSerializer, m));
8832
8716
  const serializedMutations = batch.mutations.map(m => toMutation(localSerializer.remoteSerializer, m));
8833
- return new DbMutationBatch(userId, batch.batchId, batch.localWriteTime.toMillis(), serializedBaseMutations, serializedMutations);
8717
+ return {
8718
+ userId,
8719
+ batchId: batch.batchId,
8720
+ localWriteTimeMs: batch.localWriteTime.toMillis(),
8721
+ baseMutations: serializedBaseMutations,
8722
+ mutations: serializedMutations
8723
+ };
8834
8724
  }
8835
8725
  /** Decodes a DbMutationBatch into a MutationBatch */
8836
8726
  function fromDbMutationBatch(localSerializer, dbBatch) {
@@ -8886,7 +8776,15 @@ function toDbTarget(localSerializer, targetData) {
8886
8776
  // convert it to a base64 string for storage.
8887
8777
  const resumeToken = targetData.resumeToken.toBase64();
8888
8778
  // lastListenSequenceNumber is always 0 until we do real GC.
8889
- return new DbTarget(targetData.targetId, canonifyTarget(targetData.target), dbTimestamp, resumeToken, targetData.sequenceNumber, dbLastLimboFreeTimestamp, queryProto);
8779
+ return {
8780
+ targetId: targetData.targetId,
8781
+ canonicalId: canonifyTarget(targetData.target),
8782
+ readTime: dbTimestamp,
8783
+ resumeToken,
8784
+ lastListenSequenceNumber: targetData.sequenceNumber,
8785
+ lastLimboFreeSnapshotVersion: dbLastLimboFreeTimestamp,
8786
+ query: queryProto
8787
+ };
8890
8788
  }
8891
8789
  /**
8892
8790
  * A helper function for figuring out what kind of query has been stored.
@@ -8965,7 +8863,14 @@ function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
8965
8863
  /** Decodes an Overlay model object into a DbDocumentOverlay object. */
8966
8864
  function toDbDocumentOverlay(localSerializer, userId, overlay) {
8967
8865
  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));
8866
+ return {
8867
+ userId,
8868
+ collectionPath,
8869
+ documentId,
8870
+ collectionGroup: overlay.mutation.key.getCollectionGroup(),
8871
+ largestBatchId: overlay.largestBatchId,
8872
+ overlayMutation: toMutation(localSerializer.remoteSerializer, overlay.mutation)
8873
+ };
8969
8874
  }
8970
8875
  /**
8971
8876
  * Returns the DbDocumentOverlayKey corresponding to the given user and
@@ -8977,7 +8882,11 @@ function toDbDocumentOverlayKey(userId, docKey) {
8977
8882
  return [userId, collectionPath, docId];
8978
8883
  }
8979
8884
  function toDbIndexConfiguration(index) {
8980
- return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8885
+ return {
8886
+ indexId: index.indexId,
8887
+ collectionGroup: index.collectionGroup,
8888
+ fields: index.fields.map(s => [s.fieldPath.canonicalString(), s.kind])
8889
+ };
8981
8890
  }
8982
8891
  function fromDbIndexConfiguration(index, state) {
8983
8892
  const decodedState = state
@@ -8987,7 +8896,14 @@ function fromDbIndexConfiguration(index, state) {
8987
8896
  return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8988
8897
  }
8989
8898
  function toDbIndexState(indexId, user, sequenceNumber, offset) {
8990
- return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8899
+ return {
8900
+ indexId,
8901
+ uid: user.uid || '',
8902
+ sequenceNumber,
8903
+ readTime: toDbTimestamp(offset.readTime),
8904
+ documentKey: encodeResourcePath(offset.documentKey.path),
8905
+ largestBatchId: offset.largestBatchId
8906
+ };
8991
8907
  }
8992
8908
 
8993
8909
  /**
@@ -9038,13 +8954,13 @@ class IndexedDbBundleCache {
9038
8954
  * Helper to get a typed SimpleDbStore for the bundles object store.
9039
8955
  */
9040
8956
  function bundlesStore(txn) {
9041
- return getStore(txn, DbBundle.store);
8957
+ return getStore(txn, DbBundleStore);
9042
8958
  }
9043
8959
  /**
9044
8960
  * Helper to get a typed SimpleDbStore for the namedQueries object store.
9045
8961
  */
9046
8962
  function namedQueriesStore(txn) {
9047
- return getStore(txn, DbNamedQuery.store);
8963
+ return getStore(txn, DbNamedQueryStore);
9048
8964
  }
9049
8965
 
9050
8966
  /**
@@ -9091,7 +9007,7 @@ class IndexedDbDocumentOverlayCache {
9091
9007
  }
9092
9008
  saveOverlays(transaction, largestBatchId, overlays) {
9093
9009
  const promises = [];
9094
- overlays.forEach(mutation => {
9010
+ overlays.forEach((_, mutation) => {
9095
9011
  const overlay = new Overlay(largestBatchId, mutation);
9096
9012
  promises.push(this.saveOverlay(transaction, overlay));
9097
9013
  });
@@ -9106,19 +9022,19 @@ class IndexedDbDocumentOverlayCache {
9106
9022
  const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
9107
9023
  /*lowerOpen=*/ false,
9108
9024
  /*upperOpen=*/ true);
9109
- promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
9025
+ promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlayCollectionPathOverlayIndex, range));
9110
9026
  });
9111
9027
  return PersistencePromise.waitFor(promises);
9112
9028
  }
9113
9029
  getOverlaysForCollection(transaction, collection, sinceBatchId) {
9114
- const result = new Map();
9030
+ const result = newOverlayMap();
9115
9031
  const collectionPath = encodeResourcePath(collection);
9116
9032
  // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9117
9033
  // is not inclusive.
9118
9034
  const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
9119
9035
  /*lowerOpen=*/ true);
9120
9036
  return documentOverlayStore(transaction)
9121
- .loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
9037
+ .loadAll(DbDocumentOverlayCollectionPathOverlayIndex, range)
9122
9038
  .next(dbOverlays => {
9123
9039
  for (const dbOverlay of dbOverlays) {
9124
9040
  const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
@@ -9128,7 +9044,7 @@ class IndexedDbDocumentOverlayCache {
9128
9044
  });
9129
9045
  }
9130
9046
  getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
9131
- const result = new Map();
9047
+ const result = newOverlayMap();
9132
9048
  let currentBatchId = undefined;
9133
9049
  // We want batch IDs larger than `sinceBatchId`, and so the lower bound
9134
9050
  // is not inclusive.
@@ -9136,7 +9052,7 @@ class IndexedDbDocumentOverlayCache {
9136
9052
  /*lowerOpen=*/ true);
9137
9053
  return documentOverlayStore(transaction)
9138
9054
  .iterate({
9139
- index: DbDocumentOverlay.collectionGroupOverlayIndex,
9055
+ index: DbDocumentOverlayCollectionGroupOverlayIndex,
9140
9056
  range
9141
9057
  }, (_, dbOverlay, control) => {
9142
9058
  // We do not want to return partial batch overlays, even if the size
@@ -9144,7 +9060,7 @@ class IndexedDbDocumentOverlayCache {
9144
9060
  // continue to aggregate results even after the result size exceeds
9145
9061
  // `count` if there are more overlays from the `currentBatchId`.
9146
9062
  const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
9147
- if (result.size < count ||
9063
+ if (result.size() < count ||
9148
9064
  overlay.largestBatchId === currentBatchId) {
9149
9065
  result.set(overlay.getKey(), overlay);
9150
9066
  currentBatchId = overlay.largestBatchId;
@@ -9163,7 +9079,7 @@ class IndexedDbDocumentOverlayCache {
9163
9079
  * Helper to get a typed SimpleDbStore for the document overlay object store.
9164
9080
  */
9165
9081
  function documentOverlayStore(txn) {
9166
- return getStore(txn, DbDocumentOverlay.store);
9082
+ return getStore(txn, DbDocumentOverlayStore);
9167
9083
  }
9168
9084
 
9169
9085
  /**
@@ -9719,21 +9635,40 @@ class IndexEntry {
9719
9635
  this.arrayValue = arrayValue;
9720
9636
  this.directionalValue = directionalValue;
9721
9637
  }
9638
+ /**
9639
+ * Returns an IndexEntry entry that sorts immediately after the current
9640
+ * directional value.
9641
+ */
9642
+ successor() {
9643
+ const currentLength = this.directionalValue.length;
9644
+ const newLength = currentLength === 0 || this.directionalValue[currentLength - 1] === 255
9645
+ ? currentLength + 1
9646
+ : currentLength;
9647
+ const successor = new Uint8Array(newLength);
9648
+ successor.set(this.directionalValue, 0);
9649
+ if (newLength !== currentLength) {
9650
+ successor.set([0], this.directionalValue.length);
9651
+ }
9652
+ else {
9653
+ ++successor[successor.length - 1];
9654
+ }
9655
+ return new IndexEntry(this.indexId, this.documentKey, this.arrayValue, successor);
9656
+ }
9722
9657
  }
9723
9658
  function indexEntryComparator(left, right) {
9724
9659
  let cmp = left.indexId - right.indexId;
9725
9660
  if (cmp !== 0) {
9726
9661
  return cmp;
9727
9662
  }
9728
- cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
9663
+ cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9729
9664
  if (cmp !== 0) {
9730
9665
  return cmp;
9731
9666
  }
9732
- cmp = compareByteArrays(left.arrayValue, right.arrayValue);
9667
+ cmp = compareByteArrays(left.directionalValue, right.directionalValue);
9733
9668
  if (cmp !== 0) {
9734
9669
  return cmp;
9735
9670
  }
9736
- return compareByteArrays(left.directionalValue, right.directionalValue);
9671
+ return DocumentKey.comparator(left.documentKey, right.documentKey);
9737
9672
  }
9738
9673
  function compareByteArrays(left, right) {
9739
9674
  for (let i = 0; i < left.length && i < right.length; ++i) {
@@ -9745,6 +9680,160 @@ function compareByteArrays(left, right) {
9745
9680
  return left.length - right.length;
9746
9681
  }
9747
9682
 
9683
+ /**
9684
+ * @license
9685
+ * Copyright 2022 Google LLC
9686
+ *
9687
+ * Licensed under the Apache License, Version 2.0 (the "License");
9688
+ * you may not use this file except in compliance with the License.
9689
+ * You may obtain a copy of the License at
9690
+ *
9691
+ * http://www.apache.org/licenses/LICENSE-2.0
9692
+ *
9693
+ * Unless required by applicable law or agreed to in writing, software
9694
+ * distributed under the License is distributed on an "AS IS" BASIS,
9695
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9696
+ * See the License for the specific language governing permissions and
9697
+ * limitations under the License.
9698
+ */
9699
+ /**
9700
+ * A light query planner for Firestore.
9701
+ *
9702
+ * This class matches a `FieldIndex` against a Firestore Query `Target`. It
9703
+ * determines whether a given index can be used to serve the specified target.
9704
+ *
9705
+ * The following table showcases some possible index configurations:
9706
+ *
9707
+ * Query | Index
9708
+ * -----------------------------------------------------------------------------
9709
+ * where('a', '==', 'a').where('b', '==', 'b') | a ASC, b DESC
9710
+ * where('a', '==', 'a').where('b', '==', 'b') | a ASC
9711
+ * where('a', '==', 'a').where('b', '==', 'b') | b DESC
9712
+ * where('a', '>=', 'a').orderBy('a') | a ASC
9713
+ * where('a', '>=', 'a').orderBy('a', 'desc') | a DESC
9714
+ * where('a', '>=', 'a').orderBy('a').orderBy('b') | a ASC, b ASC
9715
+ * where('a', '>=', 'a').orderBy('a').orderBy('b') | a ASC
9716
+ * where('a', 'array-contains', 'a').orderBy('b') | a CONTAINS, b ASCENDING
9717
+ * where('a', 'array-contains', 'a').orderBy('b') | a CONTAINS
9718
+ */
9719
+ class TargetIndexMatcher {
9720
+ constructor(target) {
9721
+ this.collectionId =
9722
+ target.collectionGroup != null
9723
+ ? target.collectionGroup
9724
+ : target.path.lastSegment();
9725
+ this.orderBys = target.orderBy;
9726
+ this.equalityFilters = [];
9727
+ for (const filter of target.filters) {
9728
+ const fieldFilter = filter;
9729
+ if (fieldFilter.isInequality()) {
9730
+ this.inequalityFilter = fieldFilter;
9731
+ }
9732
+ else {
9733
+ this.equalityFilters.push(fieldFilter);
9734
+ }
9735
+ }
9736
+ }
9737
+ /**
9738
+ * Returns whether the index can be used to serve the TargetIndexMatcher's
9739
+ * target.
9740
+ *
9741
+ * An index is considered capable of serving the target when:
9742
+ * - The target uses all index segments for its filters and orderBy clauses.
9743
+ * The target can have additional filter and orderBy clauses, but not
9744
+ * fewer.
9745
+ * - If an ArrayContains/ArrayContainsAnyfilter is used, the index must also
9746
+ * have a corresponding `CONTAINS` segment.
9747
+ * - All directional index segments can be mapped to the target as a series of
9748
+ * equality filters, a single inequality filter and a series of orderBy
9749
+ * clauses.
9750
+ * - The segments that represent the equality filters may appear out of order.
9751
+ * - The optional segment for the inequality filter must appear after all
9752
+ * equality segments.
9753
+ * - The segments that represent that orderBy clause of the target must appear
9754
+ * in order after all equality and inequality segments. Single orderBy
9755
+ * clauses cannot be skipped, but a continuous orderBy suffix may be
9756
+ * omitted.
9757
+ */
9758
+ servedByIndex(index) {
9759
+ // If there is an array element, find a matching filter.
9760
+ const arraySegment = fieldIndexGetArraySegment(index);
9761
+ if (arraySegment !== undefined &&
9762
+ !this.hasMatchingEqualityFilter(arraySegment)) {
9763
+ return false;
9764
+ }
9765
+ const segments = fieldIndexGetDirectionalSegments(index);
9766
+ let segmentIndex = 0;
9767
+ let orderBysIndex = 0;
9768
+ // Process all equalities first. Equalities can appear out of order.
9769
+ for (; segmentIndex < segments.length; ++segmentIndex) {
9770
+ // We attempt to greedily match all segments to equality filters. If a
9771
+ // filter matches an index segment, we can mark the segment as used.
9772
+ // Since it is not possible to use the same field path in both an equality
9773
+ // and inequality/oderBy clause, we do not have to consider the possibility
9774
+ // that a matching equality segment should instead be used to map to an
9775
+ // inequality filter or orderBy clause.
9776
+ if (!this.hasMatchingEqualityFilter(segments[segmentIndex])) {
9777
+ // If we cannot find a matching filter, we need to verify whether the
9778
+ // remaining segments map to the target's inequality and its orderBy
9779
+ // clauses.
9780
+ break;
9781
+ }
9782
+ }
9783
+ // If we already have processed all segments, all segments are used to serve
9784
+ // the equality filters and we do not need to map any segments to the
9785
+ // target's inequality and orderBy clauses.
9786
+ if (segmentIndex === segments.length) {
9787
+ return true;
9788
+ }
9789
+ // If there is an inequality filter, the next segment must match both the
9790
+ // filter and the first orderBy clause.
9791
+ if (this.inequalityFilter !== undefined) {
9792
+ const segment = segments[segmentIndex];
9793
+ if (!this.matchesFilter(this.inequalityFilter, segment) ||
9794
+ !this.matchesOrderBy(this.orderBys[orderBysIndex++], segment)) {
9795
+ return false;
9796
+ }
9797
+ ++segmentIndex;
9798
+ }
9799
+ // All remaining segments need to represent the prefix of the target's
9800
+ // orderBy.
9801
+ for (; segmentIndex < segments.length; ++segmentIndex) {
9802
+ const segment = segments[segmentIndex];
9803
+ if (orderBysIndex >= this.orderBys.length ||
9804
+ !this.matchesOrderBy(this.orderBys[orderBysIndex++], segment)) {
9805
+ return false;
9806
+ }
9807
+ }
9808
+ return true;
9809
+ }
9810
+ hasMatchingEqualityFilter(segment) {
9811
+ for (const filter of this.equalityFilters) {
9812
+ if (this.matchesFilter(filter, segment)) {
9813
+ return true;
9814
+ }
9815
+ }
9816
+ return false;
9817
+ }
9818
+ matchesFilter(filter, segment) {
9819
+ if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
9820
+ return false;
9821
+ }
9822
+ const isArrayOperator = filter.op === "array-contains" /* ARRAY_CONTAINS */ ||
9823
+ filter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */;
9824
+ return (segment.kind === 2 /* CONTAINS */) === isArrayOperator;
9825
+ }
9826
+ matchesOrderBy(orderBy, segment) {
9827
+ if (!orderBy.field.isEqual(segment.fieldPath)) {
9828
+ return false;
9829
+ }
9830
+ return ((segment.kind === 0 /* ASCENDING */ &&
9831
+ orderBy.dir === "asc" /* ASCENDING */) ||
9832
+ (segment.kind === 1 /* DESCENDING */ &&
9833
+ orderBy.dir === "desc" /* DESCENDING */));
9834
+ }
9835
+ }
9836
+
9748
9837
  /**
9749
9838
  * @license
9750
9839
  * Copyright 2019 Google LLC
@@ -9783,9 +9872,9 @@ class MemoryIndexManager {
9783
9872
  // Field indices are not supported with memory persistence.
9784
9873
  return PersistencePromise.resolve();
9785
9874
  }
9786
- getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9875
+ getDocumentsMatchingTarget(transaction, target) {
9787
9876
  // Field indices are not supported with memory persistence.
9788
- return PersistencePromise.resolve(documentKeySet());
9877
+ return PersistencePromise.resolve(null);
9789
9878
  }
9790
9879
  getFieldIndex(transaction, target) {
9791
9880
  // Field indices are not supported with memory persistence.
@@ -9857,6 +9946,7 @@ class MemoryCollectionParentIndex {
9857
9946
  * limitations under the License.
9858
9947
  */
9859
9948
  const LOG_TAG$f = 'IndexedDbIndexManager';
9949
+ const EMPTY_VALUE = new Uint8Array(0);
9860
9950
  /**
9861
9951
  * A persisted implementation of IndexManager.
9862
9952
  *
@@ -9874,6 +9964,11 @@ class IndexedDbIndexManager {
9874
9964
  * used to satisfy reads.
9875
9965
  */
9876
9966
  this.collectionParentsCache = new MemoryCollectionParentIndex();
9967
+ /**
9968
+ * Maps from a target to its equivalent list of sub-targets. Each sub-target
9969
+ * contains only one term from the target's disjunctive normal form (DNF).
9970
+ */
9971
+ this.targetToDnfSubTargets = new ObjectMap(t => canonifyTarget(t), (l, r) => targetEquals(l, r));
9877
9972
  this.uid = user.uid || '';
9878
9973
  }
9879
9974
  /**
@@ -9942,13 +10037,111 @@ class IndexedDbIndexManager {
9942
10037
  /*lowerOpen=*/ false,
9943
10038
  /*upperOpen=*/ true)));
9944
10039
  }
9945
- getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9946
- // TODO(indexing): Implement
9947
- return PersistencePromise.resolve(documentKeySet());
10040
+ getDocumentsMatchingTarget(transaction, target) {
10041
+ const indexEntries = indexEntriesStore(transaction);
10042
+ let canServeTarget = true;
10043
+ const indexes = new Map();
10044
+ return PersistencePromise.forEach(this.getSubTargets(target), (subTarget) => {
10045
+ return this.getFieldIndex(transaction, subTarget).next(index => {
10046
+ canServeTarget && (canServeTarget = !!index);
10047
+ indexes.set(subTarget, index);
10048
+ });
10049
+ }).next(() => {
10050
+ if (!canServeTarget) {
10051
+ return PersistencePromise.resolve(null);
10052
+ }
10053
+ else {
10054
+ let result = documentKeySet();
10055
+ return PersistencePromise.forEach(indexes, (index, subTarget) => {
10056
+ logDebug(LOG_TAG$f, `Using index ${fieldIndexToString(index)} to execute ${canonifyTarget(target)}`);
10057
+ const arrayValues = targetGetArrayValues(subTarget, index);
10058
+ const notInValues = targetGetNotInValues(subTarget, index);
10059
+ const lowerBound = targetGetLowerBound(subTarget, index);
10060
+ const upperBound = targetGetUpperBound(subTarget, index);
10061
+ const lowerBoundEncoded = this.encodeBound(index, subTarget, lowerBound);
10062
+ const upperBoundEncoded = this.encodeBound(index, subTarget, upperBound);
10063
+ const notInEncoded = this.encodeValues(index, subTarget, notInValues);
10064
+ const indexRanges = this.generateIndexRanges(index.indexId, arrayValues, lowerBoundEncoded, !!lowerBound && lowerBound.inclusive, upperBoundEncoded, !!upperBound && upperBound.inclusive, notInEncoded);
10065
+ return PersistencePromise.forEach(indexRanges, (indexRange) => {
10066
+ return indexEntries
10067
+ .loadFirst(indexRange, target.limit)
10068
+ .next(entries => {
10069
+ entries.forEach(entry => {
10070
+ result = result.add(new DocumentKey(decodeResourcePath(entry.documentKey)));
10071
+ });
10072
+ });
10073
+ });
10074
+ }).next(() => result);
10075
+ }
10076
+ });
10077
+ }
10078
+ getSubTargets(target) {
10079
+ let subTargets = this.targetToDnfSubTargets.get(target);
10080
+ if (subTargets) {
10081
+ return subTargets;
10082
+ }
10083
+ // TODO(orquery): Implement DNF transform
10084
+ subTargets = [target];
10085
+ this.targetToDnfSubTargets.set(target, subTargets);
10086
+ return subTargets;
10087
+ }
10088
+ /**
10089
+ * Constructs a key range query on `DbIndexEntryStore` that unions all
10090
+ * bounds.
10091
+ */
10092
+ generateIndexRanges(indexId, arrayValues, lowerBounds, lowerBoundInclusive, upperBounds, upperBoundInclusive, notInValues) {
10093
+ // The number of total index scans we union together. This is similar to a
10094
+ // distributed normal form, but adapted for array values. We create a single
10095
+ // index range per value in an ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filter
10096
+ // combined with the values from the query bounds.
10097
+ const totalScans = (arrayValues != null ? arrayValues.length : 1) *
10098
+ Math.max(lowerBounds != null ? lowerBounds.length : 1, upperBounds != null ? upperBounds.length : 1);
10099
+ const scansPerArrayElement = totalScans / (arrayValues != null ? arrayValues.length : 1);
10100
+ const indexRanges = [];
10101
+ for (let i = 0; i < totalScans; ++i) {
10102
+ const arrayValue = arrayValues
10103
+ ? this.encodeSingleElement(arrayValues[i / scansPerArrayElement])
10104
+ : EMPTY_VALUE;
10105
+ const lowerBound = lowerBounds
10106
+ ? this.generateLowerBound(indexId, arrayValue, lowerBounds[i % scansPerArrayElement], lowerBoundInclusive)
10107
+ : this.generateEmptyBound(indexId);
10108
+ const upperBound = upperBounds
10109
+ ? this.generateUpperBound(indexId, arrayValue, upperBounds[i % scansPerArrayElement], upperBoundInclusive)
10110
+ : this.generateEmptyBound(indexId + 1);
10111
+ indexRanges.push(...this.createRange(lowerBound, upperBound, notInValues.map((notIn // make non-nullable
10112
+ ) => this.generateLowerBound(indexId, arrayValue, notIn,
10113
+ /* inclusive= */ true))));
10114
+ }
10115
+ return indexRanges;
10116
+ }
10117
+ /** Generates the lower bound for `arrayValue` and `directionalValue`. */
10118
+ generateLowerBound(indexId, arrayValue, directionalValue, inclusive) {
10119
+ const entry = new IndexEntry(indexId, DocumentKey.empty(), arrayValue, directionalValue);
10120
+ return inclusive ? entry : entry.successor();
10121
+ }
10122
+ /** Generates the upper bound for `arrayValue` and `directionalValue`. */
10123
+ generateUpperBound(indexId, arrayValue, directionalValue, inclusive) {
10124
+ const entry = new IndexEntry(indexId, DocumentKey.empty(), arrayValue, directionalValue);
10125
+ return inclusive ? entry.successor() : entry;
10126
+ }
10127
+ /**
10128
+ * Generates an empty bound that scopes the index scan to the current index
10129
+ * and user.
10130
+ */
10131
+ generateEmptyBound(indexId) {
10132
+ return new IndexEntry(indexId, DocumentKey.empty(), EMPTY_VALUE, EMPTY_VALUE);
9948
10133
  }
9949
10134
  getFieldIndex(transaction, target) {
9950
- // TODO(indexing): Implement
9951
- return PersistencePromise.resolve(null);
10135
+ const targetIndexMatcher = new TargetIndexMatcher(target);
10136
+ const collectionGroup = target.collectionGroup != null
10137
+ ? target.collectionGroup
10138
+ : target.path.lastSegment();
10139
+ return this.getFieldIndexes(transaction, collectionGroup).next(indexes => {
10140
+ const matchingIndexes = indexes.filter(i => targetIndexMatcher.servedByIndex(i));
10141
+ // Return the index that matches the most number of segments.
10142
+ matchingIndexes.sort((l, r) => r.fields.length - l.fields.length);
10143
+ return matchingIndexes.length > 0 ? matchingIndexes[0] : null;
10144
+ });
9952
10145
  }
9953
10146
  /**
9954
10147
  * Returns the byte encoded form of the directional values in the field index.
@@ -9973,11 +10166,79 @@ class IndexedDbIndexManager {
9973
10166
  FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
9974
10167
  return encoder.encodedBytes();
9975
10168
  }
10169
+ /**
10170
+ * Encodes the given field values according to the specification in `target`.
10171
+ * For IN queries, a list of possible values is returned.
10172
+ */
10173
+ encodeValues(fieldIndex, target, bound) {
10174
+ if (bound === null) {
10175
+ return [];
10176
+ }
10177
+ let encoders = [];
10178
+ encoders.push(new IndexByteEncoder());
10179
+ let boundIdx = 0;
10180
+ for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
10181
+ const value = bound[boundIdx++];
10182
+ for (const encoder of encoders) {
10183
+ if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
10184
+ encoders = this.expandIndexValues(encoders, segment, value);
10185
+ }
10186
+ else {
10187
+ const directionalEncoder = encoder.forKind(segment.kind);
10188
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, directionalEncoder);
10189
+ }
10190
+ }
10191
+ }
10192
+ return this.getEncodedBytes(encoders);
10193
+ }
10194
+ /**
10195
+ * Encodes the given bounds according to the specification in `target`. For IN
10196
+ * queries, a list of possible values is returned.
10197
+ */
10198
+ encodeBound(fieldIndex, target, bound) {
10199
+ if (bound == null) {
10200
+ return null;
10201
+ }
10202
+ return this.encodeValues(fieldIndex, target, bound.position);
10203
+ }
10204
+ /** Returns the byte representation for the provided encoders. */
10205
+ getEncodedBytes(encoders) {
10206
+ const result = [];
10207
+ for (let i = 0; i < encoders.length; ++i) {
10208
+ result[i] = encoders[i].encodedBytes();
10209
+ }
10210
+ return result;
10211
+ }
10212
+ /**
10213
+ * Creates a separate encoder for each element of an array.
10214
+ *
10215
+ * The method appends each value to all existing encoders (e.g. filter("a",
10216
+ * "==", "a1").filter("b", "in", ["b1", "b2"]) becomes ["a1,b1", "a1,b2"]). A
10217
+ * list of new encoders is returned.
10218
+ */
10219
+ expandIndexValues(encoders, segment, value) {
10220
+ const prefixes = [...encoders];
10221
+ const results = [];
10222
+ for (const arrayElement of value.arrayValue.values || []) {
10223
+ for (const prefix of prefixes) {
10224
+ const clonedEncoder = new IndexByteEncoder();
10225
+ clonedEncoder.seed(prefix.encodedBytes());
10226
+ FirestoreIndexValueWriter.INSTANCE.writeIndexValue(arrayElement, clonedEncoder.forKind(segment.kind));
10227
+ results.push(clonedEncoder);
10228
+ }
10229
+ }
10230
+ return results;
10231
+ }
10232
+ isInFilter(target, fieldPath) {
10233
+ return !!target.filters.find(f => f instanceof FieldFilter &&
10234
+ f.field.isEqual(fieldPath) &&
10235
+ (f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
10236
+ }
9976
10237
  getFieldIndexes(transaction, collectionGroup) {
9977
10238
  const indexes = indexConfigurationStore(transaction);
9978
10239
  const states = indexStateStore(transaction);
9979
10240
  return (collectionGroup
9980
- ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
10241
+ ? indexes.loadAll(DbIndexConfigurationCollectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9981
10242
  : indexes.loadAll()).next(indexConfigs => {
9982
10243
  const result = [];
9983
10244
  return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
@@ -9994,7 +10255,12 @@ class IndexedDbIndexManager {
9994
10255
  if (indexes.length === 0) {
9995
10256
  return null;
9996
10257
  }
9997
- indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
10258
+ indexes.sort((l, r) => {
10259
+ const cmp = l.indexState.sequenceNumber - r.indexState.sequenceNumber;
10260
+ return cmp !== 0
10261
+ ? cmp
10262
+ : primitiveComparator(l.collectionGroup, r.collectionGroup);
10263
+ });
9998
10264
  return indexes[0].collectionGroup;
9999
10265
  });
10000
10266
  }
@@ -10002,7 +10268,7 @@ class IndexedDbIndexManager {
10002
10268
  const indexes = indexConfigurationStore(transaction);
10003
10269
  const states = indexStateStore(transaction);
10004
10270
  return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
10005
- .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
10271
+ .loadAll(DbIndexConfigurationCollectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
10006
10272
  .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
10007
10273
  }
10008
10274
  updateIndexEntries(transaction, documents) {
@@ -10033,7 +10299,13 @@ class IndexedDbIndexManager {
10033
10299
  }
10034
10300
  addIndexEntry(transaction, document, indexEntry) {
10035
10301
  const indexEntries = indexEntriesStore(transaction);
10036
- return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
10302
+ return indexEntries.put({
10303
+ indexId: indexEntry.indexId,
10304
+ uid: this.uid,
10305
+ arrayValue: indexEntry.arrayValue,
10306
+ directionalValue: indexEntry.directionalValue,
10307
+ documentKey: encodeResourcePath(document.key.path)
10308
+ });
10037
10309
  }
10038
10310
  deleteIndexEntry(transaction, document, indexEntry) {
10039
10311
  const indexEntries = indexEntriesStore(transaction);
@@ -10050,7 +10322,7 @@ class IndexedDbIndexManager {
10050
10322
  let results = new SortedSet(indexEntryComparator);
10051
10323
  return indexEntries
10052
10324
  .iterate({
10053
- index: DbIndexEntry.documentKeyIndex,
10325
+ index: DbIndexEntryDocumentKeyIndex,
10054
10326
  range: IDBKeyRange.only([
10055
10327
  fieldIndex.indexId,
10056
10328
  this.uid,
@@ -10078,7 +10350,7 @@ class IndexedDbIndexManager {
10078
10350
  }
10079
10351
  }
10080
10352
  else {
10081
- results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
10353
+ results = results.add(new IndexEntry(fieldIndex.indexId, document.key, EMPTY_VALUE, directionalValue));
10082
10354
  }
10083
10355
  return results;
10084
10356
  }
@@ -10104,7 +10376,7 @@ class IndexedDbIndexManager {
10104
10376
  const states = indexStateStore(transaction);
10105
10377
  return states
10106
10378
  .iterate({
10107
- index: DbIndexState.sequenceNumberIndex,
10379
+ index: DbIndexStateSequenceNumberIndex,
10108
10380
  reverse: true,
10109
10381
  range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
10110
10382
  }, (_, state, controller) => {
@@ -10113,31 +10385,81 @@ class IndexedDbIndexManager {
10113
10385
  })
10114
10386
  .next(() => nextSequenceNumber);
10115
10387
  }
10388
+ /**
10389
+ * Returns a new set of IDB ranges that splits the existing range and excludes
10390
+ * any values that match the `notInValue` from these ranges. As an example,
10391
+ * '[foo > 2 && foo != 3]` becomes `[foo > 2 && < 3, foo > 3]`.
10392
+ */
10393
+ createRange(lower, upper, notInValues) {
10394
+ // The notIb values need to be sorted and unique so that we can return a
10395
+ // sorted set of non-overlapping ranges.
10396
+ notInValues = notInValues
10397
+ .sort((l, r) => indexEntryComparator(l, r))
10398
+ .filter((el, i, values) => !i || indexEntryComparator(el, values[i - 1]) !== 0);
10399
+ const bounds = [];
10400
+ bounds.push(lower);
10401
+ for (const notInValue of notInValues) {
10402
+ const cmpToLower = indexEntryComparator(notInValue, lower);
10403
+ const cmpToUpper = indexEntryComparator(notInValue, upper);
10404
+ if (cmpToLower === 0) {
10405
+ // `notInValue` is the lower bound. We therefore need to raise the bound
10406
+ // to the next value.
10407
+ bounds[0] = lower.successor();
10408
+ }
10409
+ else if (cmpToLower > 0 && cmpToUpper < 0) {
10410
+ // `notInValue` is in the middle of the range
10411
+ bounds.push(notInValue);
10412
+ bounds.push(notInValue.successor());
10413
+ }
10414
+ else if (cmpToUpper > 0) {
10415
+ // `notInValue` (and all following values) are out of the range
10416
+ break;
10417
+ }
10418
+ }
10419
+ bounds.push(upper);
10420
+ const ranges = [];
10421
+ for (let i = 0; i < bounds.length; i += 2) {
10422
+ ranges.push(IDBKeyRange.bound([
10423
+ bounds[i].indexId,
10424
+ this.uid,
10425
+ bounds[i].arrayValue,
10426
+ bounds[i].directionalValue,
10427
+ ''
10428
+ ], [
10429
+ bounds[i + 1].indexId,
10430
+ this.uid,
10431
+ bounds[i + 1].arrayValue,
10432
+ bounds[i + 1].directionalValue,
10433
+ ''
10434
+ ]));
10435
+ }
10436
+ return ranges;
10437
+ }
10116
10438
  }
10117
10439
  /**
10118
10440
  * Helper to get a typed SimpleDbStore for the collectionParents
10119
10441
  * document store.
10120
10442
  */
10121
10443
  function collectionParentsStore(txn) {
10122
- return getStore(txn, DbCollectionParent.store);
10444
+ return getStore(txn, DbCollectionParentStore);
10123
10445
  }
10124
10446
  /**
10125
10447
  * Helper to get a typed SimpleDbStore for the index entry object store.
10126
10448
  */
10127
10449
  function indexEntriesStore(txn) {
10128
- return getStore(txn, DbIndexEntry.store);
10450
+ return getStore(txn, DbIndexEntryStore);
10129
10451
  }
10130
10452
  /**
10131
10453
  * Helper to get a typed SimpleDbStore for the index configuration object store.
10132
10454
  */
10133
10455
  function indexConfigurationStore(txn) {
10134
- return getStore(txn, DbIndexConfiguration.store);
10456
+ return getStore(txn, DbIndexConfigurationStore);
10135
10457
  }
10136
10458
  /**
10137
10459
  * Helper to get a typed SimpleDbStore for the index state object store.
10138
10460
  */
10139
10461
  function indexStateStore(txn) {
10140
- return getStore(txn, DbIndexState.store);
10462
+ return getStore(txn, DbIndexStateStore);
10141
10463
  }
10142
10464
 
10143
10465
  /**
@@ -10161,8 +10483,8 @@ function indexStateStore(txn) {
10161
10483
  * @returns A PersistencePromise of the document mutations that were removed.
10162
10484
  */
10163
10485
  function removeMutationBatch(txn, userId, batch) {
10164
- const mutationStore = txn.store(DbMutationBatch.store);
10165
- const indexTxn = txn.store(DbDocumentMutation.store);
10486
+ const mutationStore = txn.store(DbMutationBatchStore);
10487
+ const indexTxn = txn.store(DbDocumentMutationStore);
10166
10488
  const promises = [];
10167
10489
  const range = IDBKeyRange.only(batch.batchId);
10168
10490
  let numDeleted = 0;
@@ -10175,7 +10497,7 @@ function removeMutationBatch(txn, userId, batch) {
10175
10497
  }));
10176
10498
  const removedDocuments = [];
10177
10499
  for (const mutation of batch.mutations) {
10178
- const indexKey = DbDocumentMutation.key(userId, mutation.key.path, batch.batchId);
10500
+ const indexKey = newDbDocumentMutationKey(userId, mutation.key.path, batch.batchId);
10179
10501
  promises.push(indexTxn.delete(indexKey));
10180
10502
  removedDocuments.push(mutation.key);
10181
10503
  }
@@ -10264,7 +10586,7 @@ class IndexedDbMutationQueue {
10264
10586
  let empty = true;
10265
10587
  const range = IDBKeyRange.bound([this.userId, Number.NEGATIVE_INFINITY], [this.userId, Number.POSITIVE_INFINITY]);
10266
10588
  return mutationsStore(transaction)
10267
- .iterate({ index: DbMutationBatch.userMutationsIndex, range }, (key, value, control) => {
10589
+ .iterate({ index: DbMutationBatchUserMutationsIndex, range }, (key, value, control) => {
10268
10590
  empty = false;
10269
10591
  control.done();
10270
10592
  })
@@ -10289,10 +10611,10 @@ class IndexedDbMutationQueue {
10289
10611
  const promises = [];
10290
10612
  let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
10291
10613
  for (const mutation of mutations) {
10292
- const indexKey = DbDocumentMutation.key(this.userId, mutation.key.path, batchId);
10614
+ const indexKey = newDbDocumentMutationKey(this.userId, mutation.key.path, batchId);
10293
10615
  collectionParents = collectionParents.add(mutation.key.path.popLast());
10294
10616
  promises.push(mutationStore.put(dbBatch));
10295
- promises.push(documentStore.put(indexKey, DbDocumentMutation.PLACEHOLDER));
10617
+ promises.push(documentStore.put(indexKey, DbDocumentMutationPlaceholder));
10296
10618
  }
10297
10619
  collectionParents.forEach(parent => {
10298
10620
  promises.push(this.indexManager.addToCollectionParentIndex(transaction, parent));
@@ -10343,7 +10665,7 @@ class IndexedDbMutationQueue {
10343
10665
  const range = IDBKeyRange.lowerBound([this.userId, nextBatchId]);
10344
10666
  let foundBatch = null;
10345
10667
  return mutationsStore(transaction)
10346
- .iterate({ index: DbMutationBatch.userMutationsIndex, range }, (key, dbBatch, control) => {
10668
+ .iterate({ index: DbMutationBatchUserMutationsIndex, range }, (key, dbBatch, control) => {
10347
10669
  if (dbBatch.userId === this.userId) {
10348
10670
  hardAssert(dbBatch.batchId >= nextBatchId);
10349
10671
  foundBatch = fromDbMutationBatch(this.serializer, dbBatch);
@@ -10359,7 +10681,7 @@ class IndexedDbMutationQueue {
10359
10681
  ]);
10360
10682
  let batchId = BATCHID_UNKNOWN;
10361
10683
  return mutationsStore(transaction)
10362
- .iterate({ index: DbMutationBatch.userMutationsIndex, range, reverse: true }, (key, dbBatch, control) => {
10684
+ .iterate({ index: DbMutationBatchUserMutationsIndex, range, reverse: true }, (key, dbBatch, control) => {
10363
10685
  batchId = dbBatch.batchId;
10364
10686
  control.done();
10365
10687
  })
@@ -10368,13 +10690,13 @@ class IndexedDbMutationQueue {
10368
10690
  getAllMutationBatches(transaction) {
10369
10691
  const range = IDBKeyRange.bound([this.userId, BATCHID_UNKNOWN], [this.userId, Number.POSITIVE_INFINITY]);
10370
10692
  return mutationsStore(transaction)
10371
- .loadAll(DbMutationBatch.userMutationsIndex, range)
10693
+ .loadAll(DbMutationBatchUserMutationsIndex, range)
10372
10694
  .next(dbBatches => dbBatches.map(dbBatch => fromDbMutationBatch(this.serializer, dbBatch)));
10373
10695
  }
10374
10696
  getAllMutationBatchesAffectingDocumentKey(transaction, documentKey) {
10375
10697
  // Scan the document-mutation index starting with a prefix starting with
10376
10698
  // the given documentKey.
10377
- const indexPrefix = DbDocumentMutation.prefixForPath(this.userId, documentKey.path);
10699
+ const indexPrefix = newDbDocumentMutationPrefixForPath(this.userId, documentKey.path);
10378
10700
  const indexStart = IDBKeyRange.lowerBound(indexPrefix);
10379
10701
  const results = [];
10380
10702
  return documentMutationsStore(transaction)
@@ -10409,7 +10731,7 @@ class IndexedDbMutationQueue {
10409
10731
  let uniqueBatchIDs = new SortedSet(primitiveComparator);
10410
10732
  const promises = [];
10411
10733
  documentKeys.forEach(documentKey => {
10412
- const indexStart = DbDocumentMutation.prefixForPath(this.userId, documentKey.path);
10734
+ const indexStart = newDbDocumentMutationPrefixForPath(this.userId, documentKey.path);
10413
10735
  const range = IDBKeyRange.lowerBound(indexStart);
10414
10736
  const promise = documentMutationsStore(transaction).iterate({ range }, (indexKey, _, control) => {
10415
10737
  const [userID, encodedPath, batchID] = indexKey;
@@ -10445,7 +10767,7 @@ class IndexedDbMutationQueue {
10445
10767
  // Since we don't yet index the actual properties in the mutations, our
10446
10768
  // current approach is to just return all mutation batches that affect
10447
10769
  // documents in the collection being queried.
10448
- const indexPrefix = DbDocumentMutation.prefixForPath(this.userId, queryPath);
10770
+ const indexPrefix = newDbDocumentMutationPrefixForPath(this.userId, queryPath);
10449
10771
  const indexStart = IDBKeyRange.lowerBound(indexPrefix);
10450
10772
  // Collect up unique batchIDs encountered during a scan of the index. Use a
10451
10773
  // SortedSet to accumulate batch IDs so they can be traversed in order in a
@@ -10517,7 +10839,7 @@ class IndexedDbMutationQueue {
10517
10839
  }
10518
10840
  // Verify that there are no entries in the documentMutations index if
10519
10841
  // the queue is empty.
10520
- const startRange = IDBKeyRange.lowerBound(DbDocumentMutation.prefixForUser(this.userId));
10842
+ const startRange = IDBKeyRange.lowerBound(newDbDocumentMutationPrefixForUser(this.userId));
10521
10843
  const danglingMutationReferences = [];
10522
10844
  return documentMutationsStore(txn)
10523
10845
  .iterate({ range: startRange }, (key, _, control) => {
@@ -10545,9 +10867,11 @@ class IndexedDbMutationQueue {
10545
10867
  return mutationQueuesStore(transaction)
10546
10868
  .get(this.userId)
10547
10869
  .next((metadata) => {
10548
- return (metadata ||
10549
- new DbMutationQueue(this.userId, BATCHID_UNKNOWN,
10550
- /*lastStreamToken=*/ ''));
10870
+ return (metadata || {
10871
+ userId: this.userId,
10872
+ lastAcknowledgedBatchId: BATCHID_UNKNOWN,
10873
+ lastStreamToken: ''
10874
+ });
10551
10875
  });
10552
10876
  }
10553
10877
  }
@@ -10556,7 +10880,7 @@ class IndexedDbMutationQueue {
10556
10880
  * mutation for the given key.
10557
10881
  */
10558
10882
  function mutationQueueContainsKey(txn, userId, key) {
10559
- const indexKey = DbDocumentMutation.prefixForPath(userId, key.path);
10883
+ const indexKey = newDbDocumentMutationPrefixForPath(userId, key.path);
10560
10884
  const encodedPath = indexKey[1];
10561
10885
  const startRange = IDBKeyRange.lowerBound(indexKey);
10562
10886
  let containsKey = false;
@@ -10588,19 +10912,19 @@ function mutationQueuesContainKey(txn, docKey) {
10588
10912
  * Helper to get a typed SimpleDbStore for the mutations object store.
10589
10913
  */
10590
10914
  function mutationsStore(txn) {
10591
- return getStore(txn, DbMutationBatch.store);
10915
+ return getStore(txn, DbMutationBatchStore);
10592
10916
  }
10593
10917
  /**
10594
10918
  * Helper to get a typed SimpleDbStore for the mutationQueues object store.
10595
10919
  */
10596
10920
  function documentMutationsStore(txn) {
10597
- return getStore(txn, DbDocumentMutation.store);
10921
+ return getStore(txn, DbDocumentMutationStore);
10598
10922
  }
10599
10923
  /**
10600
10924
  * Helper to get a typed SimpleDbStore for the mutationQueues object store.
10601
10925
  */
10602
10926
  function mutationQueuesStore(txn) {
10603
- return getStore(txn, DbMutationQueue.store);
10927
+ return getStore(txn, DbMutationQueueStore);
10604
10928
  }
10605
10929
 
10606
10930
  /**
@@ -10764,14 +11088,14 @@ class IndexedDbTargetCache {
10764
11088
  }
10765
11089
  retrieveMetadata(transaction) {
10766
11090
  return globalTargetStore(transaction)
10767
- .get(DbTargetGlobal.key)
11091
+ .get(DbTargetGlobalKey)
10768
11092
  .next(metadata => {
10769
11093
  hardAssert(metadata !== null);
10770
11094
  return metadata;
10771
11095
  });
10772
11096
  }
10773
11097
  saveMetadata(transaction, metadata) {
10774
- return globalTargetStore(transaction).put(DbTargetGlobal.key, metadata);
11098
+ return globalTargetStore(transaction).put(DbTargetGlobalKey, metadata);
10775
11099
  }
10776
11100
  saveTargetData(transaction, targetData) {
10777
11101
  return targetsStore(transaction).put(toDbTarget(this.serializer, targetData));
@@ -10804,7 +11128,7 @@ class IndexedDbTargetCache {
10804
11128
  const range = IDBKeyRange.bound([canonicalId, Number.NEGATIVE_INFINITY], [canonicalId, Number.POSITIVE_INFINITY]);
10805
11129
  let result = null;
10806
11130
  return targetsStore(transaction)
10807
- .iterate({ range, index: DbTarget.queryTargetsIndexName }, (key, value, control) => {
11131
+ .iterate({ range, index: DbTargetQueryTargetsIndexName }, (key, value, control) => {
10808
11132
  const found = fromDbTarget(value);
10809
11133
  // After finding a potential match, check that the target is
10810
11134
  // actually equal to the requested target.
@@ -10822,7 +11146,7 @@ class IndexedDbTargetCache {
10822
11146
  const store = documentTargetStore(txn);
10823
11147
  keys.forEach(key => {
10824
11148
  const path = encodeResourcePath(key.path);
10825
- promises.push(store.put(new DbTargetDocument(targetId, path)));
11149
+ promises.push(store.put({ targetId, path }));
10826
11150
  promises.push(this.referenceDelegate.addReference(txn, targetId, key));
10827
11151
  });
10828
11152
  return PersistencePromise.waitFor(promises);
@@ -10868,7 +11192,7 @@ class IndexedDbTargetCache {
10868
11192
  let count = 0;
10869
11193
  return documentTargetStore(txn)
10870
11194
  .iterate({
10871
- index: DbTargetDocument.documentTargetsIndex,
11195
+ index: DbTargetDocumentDocumentTargetsIndex,
10872
11196
  keysOnly: true,
10873
11197
  range
10874
11198
  }, ([targetId, path], _, control) => {
@@ -10907,19 +11231,19 @@ class IndexedDbTargetCache {
10907
11231
  * Helper to get a typed SimpleDbStore for the queries object store.
10908
11232
  */
10909
11233
  function targetsStore(txn) {
10910
- return getStore(txn, DbTarget.store);
11234
+ return getStore(txn, DbTargetStore);
10911
11235
  }
10912
11236
  /**
10913
11237
  * Helper to get a typed SimpleDbStore for the target globals object store.
10914
11238
  */
10915
11239
  function globalTargetStore(txn) {
10916
- return getStore(txn, DbTargetGlobal.store);
11240
+ return getStore(txn, DbTargetGlobalStore);
10917
11241
  }
10918
11242
  /**
10919
11243
  * Helper to get a typed SimpleDbStore for the document target object store.
10920
11244
  */
10921
11245
  function documentTargetStore(txn) {
10922
- return getStore(txn, DbTargetDocument.store);
11246
+ return getStore(txn, DbTargetDocumentStore);
10923
11247
  }
10924
11248
 
10925
11249
  /**
@@ -11333,7 +11657,7 @@ class IndexedDbLruDelegateImpl {
11333
11657
  let nextPath;
11334
11658
  return store
11335
11659
  .iterate({
11336
- index: DbTargetDocument.documentTargetsIndex
11660
+ index: DbTargetDocumentDocumentTargetsIndex
11337
11661
  }, ([targetId, docKey], { path, sequenceNumber }) => {
11338
11662
  if (targetId === 0) {
11339
11663
  // if nextToReport is valid, report it, this is a new key so the
@@ -11375,113 +11699,12 @@ function sentinelKey$1(key) {
11375
11699
  * store.
11376
11700
  */
11377
11701
  function sentinelRow(key, sequenceNumber) {
11378
- return new DbTargetDocument(0, encodeResourcePath(key.path), sequenceNumber);
11702
+ return { targetId: 0, path: encodeResourcePath(key.path), sequenceNumber };
11379
11703
  }
11380
11704
  function writeSentinelKey(txn, key) {
11381
11705
  return documentTargetStore(txn).put(sentinelRow(key, txn.currentSequenceNumber));
11382
11706
  }
11383
11707
 
11384
- /**
11385
- * @license
11386
- * Copyright 2017 Google LLC
11387
- *
11388
- * Licensed under the Apache License, Version 2.0 (the "License");
11389
- * you may not use this file except in compliance with the License.
11390
- * You may obtain a copy of the License at
11391
- *
11392
- * http://www.apache.org/licenses/LICENSE-2.0
11393
- *
11394
- * Unless required by applicable law or agreed to in writing, software
11395
- * distributed under the License is distributed on an "AS IS" BASIS,
11396
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11397
- * See the License for the specific language governing permissions and
11398
- * limitations under the License.
11399
- */
11400
- /**
11401
- * A map implementation that uses objects as keys. Objects must have an
11402
- * associated equals function and must be immutable. Entries in the map are
11403
- * stored together with the key being produced from the mapKeyFn. This map
11404
- * automatically handles collisions of keys.
11405
- */
11406
- class ObjectMap {
11407
- constructor(mapKeyFn, equalsFn) {
11408
- this.mapKeyFn = mapKeyFn;
11409
- this.equalsFn = equalsFn;
11410
- /**
11411
- * The inner map for a key/value pair. Due to the possibility of collisions we
11412
- * keep a list of entries that we do a linear search through to find an actual
11413
- * match. Note that collisions should be rare, so we still expect near
11414
- * constant time lookups in practice.
11415
- */
11416
- this.inner = {};
11417
- }
11418
- /** Get a value for this key, or undefined if it does not exist. */
11419
- get(key) {
11420
- const id = this.mapKeyFn(key);
11421
- const matches = this.inner[id];
11422
- if (matches === undefined) {
11423
- return undefined;
11424
- }
11425
- for (const [otherKey, value] of matches) {
11426
- if (this.equalsFn(otherKey, key)) {
11427
- return value;
11428
- }
11429
- }
11430
- return undefined;
11431
- }
11432
- has(key) {
11433
- return this.get(key) !== undefined;
11434
- }
11435
- /** Put this key and value in the map. */
11436
- set(key, value) {
11437
- const id = this.mapKeyFn(key);
11438
- const matches = this.inner[id];
11439
- if (matches === undefined) {
11440
- this.inner[id] = [[key, value]];
11441
- return;
11442
- }
11443
- for (let i = 0; i < matches.length; i++) {
11444
- if (this.equalsFn(matches[i][0], key)) {
11445
- matches[i] = [key, value];
11446
- return;
11447
- }
11448
- }
11449
- matches.push([key, value]);
11450
- }
11451
- /**
11452
- * Remove this key from the map. Returns a boolean if anything was deleted.
11453
- */
11454
- delete(key) {
11455
- const id = this.mapKeyFn(key);
11456
- const matches = this.inner[id];
11457
- if (matches === undefined) {
11458
- return false;
11459
- }
11460
- for (let i = 0; i < matches.length; i++) {
11461
- if (this.equalsFn(matches[i][0], key)) {
11462
- if (matches.length === 1) {
11463
- delete this.inner[id];
11464
- }
11465
- else {
11466
- matches.splice(i, 1);
11467
- }
11468
- return true;
11469
- }
11470
- }
11471
- return false;
11472
- }
11473
- forEach(fn) {
11474
- forEach(this.inner, (_, entries) => {
11475
- for (const [k, v] of entries) {
11476
- fn(k, v);
11477
- }
11478
- });
11479
- }
11480
- isEmpty() {
11481
- return isEmpty(this.inner);
11482
- }
11483
- }
11484
-
11485
11708
  /**
11486
11709
  * @license
11487
11710
  * Copyright 2017 Google LLC
@@ -11750,7 +11973,7 @@ class IndexedDbRemoteDocumentCacheImpl {
11750
11973
  const readTimeKey = toDbTimestampKey(sinceReadTime);
11751
11974
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
11752
11975
  /* open= */ true);
11753
- iterationOptions.index = DbRemoteDocument.collectionReadTimeIndex;
11976
+ iterationOptions.index = DbRemoteDocumentCollectionReadTimeIndex;
11754
11977
  }
11755
11978
  return remoteDocumentsStore(transaction)
11756
11979
  .iterate(iterationOptions, (key, dbRemoteDoc, control) => {
@@ -11780,14 +12003,14 @@ class IndexedDbRemoteDocumentCacheImpl {
11780
12003
  }
11781
12004
  getMetadata(txn) {
11782
12005
  return documentGlobalStore(txn)
11783
- .get(DbRemoteDocumentGlobal.key)
12006
+ .get(DbRemoteDocumentGlobalKey)
11784
12007
  .next(metadata => {
11785
12008
  hardAssert(!!metadata);
11786
12009
  return metadata;
11787
12010
  });
11788
12011
  }
11789
12012
  setMetadata(txn, metadata) {
11790
- return documentGlobalStore(txn).put(DbRemoteDocumentGlobal.key, metadata);
12013
+ return documentGlobalStore(txn).put(DbRemoteDocumentGlobalKey, metadata);
11791
12014
  }
11792
12015
  /**
11793
12016
  * Decodes `dbRemoteDoc` and returns the document (or an invalid document if
@@ -11822,7 +12045,7 @@ function remoteDocumentCacheGetNewDocumentChanges(remoteDocumentCache, transacti
11822
12045
  const documentsStore = remoteDocumentsStore(transaction);
11823
12046
  const range = IDBKeyRange.lowerBound(lastReadTime, true);
11824
12047
  return documentsStore
11825
- .iterate({ index: DbRemoteDocument.readTimeIndex, range }, (_, dbRemoteDoc) => {
12048
+ .iterate({ index: DbRemoteDocumentReadTimeIndex, range }, (_, dbRemoteDoc) => {
11826
12049
  // Unlike `getEntry()` and others, `getNewDocumentChanges()` parses
11827
12050
  // the documents directly since we want to keep sentinel deletes.
11828
12051
  const doc = fromDbRemoteDocument(remoteDocumentCacheImpl.serializer, dbRemoteDoc);
@@ -11846,7 +12069,7 @@ function remoteDocumentCacheGetLastReadTime(transaction) {
11846
12069
  // If there are no existing entries, we return SnapshotVersion.min().
11847
12070
  let readTime = SnapshotVersion.min();
11848
12071
  return documentsStore
11849
- .iterate({ index: DbRemoteDocument.readTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
12072
+ .iterate({ index: DbRemoteDocumentReadTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
11850
12073
  if (dbRemoteDoc.readTime) {
11851
12074
  readTime = fromDbTimestampKey(dbRemoteDoc.readTime);
11852
12075
  }
@@ -11934,17 +12157,39 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
11934
12157
  }
11935
12158
  }
11936
12159
  function documentGlobalStore(txn) {
11937
- return getStore(txn, DbRemoteDocumentGlobal.store);
12160
+ return getStore(txn, DbRemoteDocumentGlobalStore);
11938
12161
  }
11939
12162
  /**
11940
12163
  * Helper to get a typed SimpleDbStore for the remoteDocuments object store.
11941
12164
  */
11942
12165
  function remoteDocumentsStore(txn) {
11943
- return getStore(txn, DbRemoteDocument.store);
12166
+ return getStore(txn, DbRemoteDocumentStore);
11944
12167
  }
11945
12168
  function dbKey(docKey) {
11946
12169
  return docKey.path.toArray();
11947
12170
  }
12171
+ /**
12172
+ * Schema Version for the Web client:
12173
+ * 1. Initial version including Mutation Queue, Query Cache, and Remote
12174
+ * Document Cache
12175
+ * 2. Used to ensure a targetGlobal object exists and add targetCount to it. No
12176
+ * longer required because migration 3 unconditionally clears it.
12177
+ * 3. Dropped and re-created Query Cache to deal with cache corruption related
12178
+ * to limbo resolution. Addresses
12179
+ * https://github.com/firebase/firebase-ios-sdk/issues/1548
12180
+ * 4. Multi-Tab Support.
12181
+ * 5. Removal of held write acks.
12182
+ * 6. Create document global for tracking document cache size.
12183
+ * 7. Ensure every cached document has a sentinel row with a sequence number.
12184
+ * 8. Add collection-parent index for Collection Group queries.
12185
+ * 9. Change RemoteDocumentChanges store to be keyed by readTime rather than
12186
+ * an auto-incrementing ID. This is required for Index-Free queries.
12187
+ * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
12188
+ * 11. Add bundles and named_queries for bundle support.
12189
+ * 12. Add document overlays.
12190
+ * 13. Add indexing support.
12191
+ */
12192
+ const SCHEMA_VERSION = 12;
11948
12193
 
11949
12194
  /**
11950
12195
  * @license
@@ -12054,27 +12299,27 @@ class SchemaConverter {
12054
12299
  return p;
12055
12300
  }
12056
12301
  addDocumentGlobal(txn) {
12057
- let byteCount = 0;
12302
+ let byteSize = 0;
12058
12303
  return txn
12059
- .store(DbRemoteDocument.store)
12304
+ .store(DbRemoteDocumentStore)
12060
12305
  .iterate((_, doc) => {
12061
- byteCount += dbDocumentSize(doc);
12306
+ byteSize += dbDocumentSize(doc);
12062
12307
  })
12063
12308
  .next(() => {
12064
- const metadata = new DbRemoteDocumentGlobal(byteCount);
12309
+ const metadata = { byteSize };
12065
12310
  return txn
12066
- .store(DbRemoteDocumentGlobal.store)
12067
- .put(DbRemoteDocumentGlobal.key, metadata);
12311
+ .store(DbRemoteDocumentGlobalStore)
12312
+ .put(DbRemoteDocumentGlobalKey, metadata);
12068
12313
  });
12069
12314
  }
12070
12315
  removeAcknowledgedMutations(txn) {
12071
- const queuesStore = txn.store(DbMutationQueue.store);
12072
- const mutationsStore = txn.store(DbMutationBatch.store);
12316
+ const queuesStore = txn.store(DbMutationQueueStore);
12317
+ const mutationsStore = txn.store(DbMutationBatchStore);
12073
12318
  return queuesStore.loadAll().next(queues => {
12074
12319
  return PersistencePromise.forEach(queues, (queue) => {
12075
12320
  const range = IDBKeyRange.bound([queue.userId, BATCHID_UNKNOWN], [queue.userId, queue.lastAcknowledgedBatchId]);
12076
12321
  return mutationsStore
12077
- .loadAll(DbMutationBatch.userMutationsIndex, range)
12322
+ .loadAll(DbMutationBatchUserMutationsIndex, range)
12078
12323
  .next(dbBatches => {
12079
12324
  return PersistencePromise.forEach(dbBatches, (dbBatch) => {
12080
12325
  hardAssert(dbBatch.userId === queue.userId);
@@ -12090,12 +12335,16 @@ class SchemaConverter {
12090
12335
  * with a sequence number. Missing rows are given the most recently used sequence number.
12091
12336
  */
12092
12337
  ensureSequenceNumbers(txn) {
12093
- const documentTargetStore = txn.store(DbTargetDocument.store);
12094
- const documentsStore = txn.store(DbRemoteDocument.store);
12095
- const globalTargetStore = txn.store(DbTargetGlobal.store);
12096
- return globalTargetStore.get(DbTargetGlobal.key).next(metadata => {
12338
+ const documentTargetStore = txn.store(DbTargetDocumentStore);
12339
+ const documentsStore = txn.store(DbRemoteDocumentStore);
12340
+ const globalTargetStore = txn.store(DbTargetGlobalStore);
12341
+ return globalTargetStore.get(DbTargetGlobalKey).next(metadata => {
12097
12342
  const writeSentinelKey = (path) => {
12098
- return documentTargetStore.put(new DbTargetDocument(0, encodeResourcePath(path), metadata.highestListenSequenceNumber));
12343
+ return documentTargetStore.put({
12344
+ targetId: 0,
12345
+ path: encodeResourcePath(path),
12346
+ sequenceNumber: metadata.highestListenSequenceNumber
12347
+ });
12099
12348
  };
12100
12349
  const promises = [];
12101
12350
  return documentsStore
@@ -12116,10 +12365,10 @@ class SchemaConverter {
12116
12365
  }
12117
12366
  createCollectionParentIndex(db, txn) {
12118
12367
  // Create the index.
12119
- db.createObjectStore(DbCollectionParent.store, {
12120
- keyPath: DbCollectionParent.keyPath
12368
+ db.createObjectStore(DbCollectionParentStore, {
12369
+ keyPath: DbCollectionParentKeyPath
12121
12370
  });
12122
- const collectionParentsStore = txn.store(DbCollectionParent.store);
12371
+ const collectionParentsStore = txn.store(DbCollectionParentStore);
12123
12372
  // Helper to add an index entry iff we haven't already written it.
12124
12373
  const cache = new MemoryCollectionParentIndex();
12125
12374
  const addEntry = (collectionPath) => {
@@ -12134,7 +12383,7 @@ class SchemaConverter {
12134
12383
  };
12135
12384
  // Index existing remote documents.
12136
12385
  return txn
12137
- .store(DbRemoteDocument.store)
12386
+ .store(DbRemoteDocumentStore)
12138
12387
  .iterate({ keysOnly: true }, (pathSegments, _) => {
12139
12388
  const path = new ResourcePath(pathSegments);
12140
12389
  return addEntry(path.popLast());
@@ -12142,7 +12391,7 @@ class SchemaConverter {
12142
12391
  .next(() => {
12143
12392
  // Index existing mutations.
12144
12393
  return txn
12145
- .store(DbDocumentMutation.store)
12394
+ .store(DbDocumentMutationStore)
12146
12395
  .iterate({ keysOnly: true }, ([userID, encodedPath, batchId], _) => {
12147
12396
  const path = decodeResourcePath(encodedPath);
12148
12397
  return addEntry(path.popLast());
@@ -12150,7 +12399,7 @@ class SchemaConverter {
12150
12399
  });
12151
12400
  }
12152
12401
  rewriteCanonicalIds(txn) {
12153
- const targetStore = txn.store(DbTarget.store);
12402
+ const targetStore = txn.store(DbTargetStore);
12154
12403
  return targetStore.iterate((key, originalDbTarget) => {
12155
12404
  const originalTargetData = fromDbTarget(originalDbTarget);
12156
12405
  const updatedDbTarget = toDbTarget(this.serializer, originalTargetData);
@@ -12162,59 +12411,59 @@ function sentinelKey(path) {
12162
12411
  return [0, encodeResourcePath(path)];
12163
12412
  }
12164
12413
  function createPrimaryClientStore(db) {
12165
- db.createObjectStore(DbPrimaryClient.store);
12414
+ db.createObjectStore(DbPrimaryClientStore);
12166
12415
  }
12167
12416
  function createMutationQueue(db) {
12168
- db.createObjectStore(DbMutationQueue.store, {
12169
- keyPath: DbMutationQueue.keyPath
12417
+ db.createObjectStore(DbMutationQueueStore, {
12418
+ keyPath: DbMutationQueueKeyPath
12170
12419
  });
12171
- const mutationBatchesStore = db.createObjectStore(DbMutationBatch.store, {
12172
- keyPath: DbMutationBatch.keyPath,
12420
+ const mutationBatchesStore = db.createObjectStore(DbMutationBatchStore, {
12421
+ keyPath: DbMutationBatchKeyPath,
12173
12422
  autoIncrement: true
12174
12423
  });
12175
- mutationBatchesStore.createIndex(DbMutationBatch.userMutationsIndex, DbMutationBatch.userMutationsKeyPath, { unique: true });
12176
- db.createObjectStore(DbDocumentMutation.store);
12424
+ mutationBatchesStore.createIndex(DbMutationBatchUserMutationsIndex, DbMutationBatchUserMutationsKeyPath, { unique: true });
12425
+ db.createObjectStore(DbDocumentMutationStore);
12177
12426
  }
12178
12427
  /**
12179
12428
  * Upgrade function to migrate the 'mutations' store from V1 to V3. Loads
12180
12429
  * and rewrites all data.
12181
12430
  */
12182
12431
  function upgradeMutationBatchSchemaAndMigrateData(db, txn) {
12183
- const v1MutationsStore = txn.store(DbMutationBatch.store);
12432
+ const v1MutationsStore = txn.store(DbMutationBatchStore);
12184
12433
  return v1MutationsStore.loadAll().next(existingMutations => {
12185
- db.deleteObjectStore(DbMutationBatch.store);
12186
- const mutationsStore = db.createObjectStore(DbMutationBatch.store, {
12187
- keyPath: DbMutationBatch.keyPath,
12434
+ db.deleteObjectStore(DbMutationBatchStore);
12435
+ const mutationsStore = db.createObjectStore(DbMutationBatchStore, {
12436
+ keyPath: DbMutationBatchKeyPath,
12188
12437
  autoIncrement: true
12189
12438
  });
12190
- mutationsStore.createIndex(DbMutationBatch.userMutationsIndex, DbMutationBatch.userMutationsKeyPath, { unique: true });
12191
- const v3MutationsStore = txn.store(DbMutationBatch.store);
12439
+ mutationsStore.createIndex(DbMutationBatchUserMutationsIndex, DbMutationBatchUserMutationsKeyPath, { unique: true });
12440
+ const v3MutationsStore = txn.store(DbMutationBatchStore);
12192
12441
  const writeAll = existingMutations.map(mutation => v3MutationsStore.put(mutation));
12193
12442
  return PersistencePromise.waitFor(writeAll);
12194
12443
  });
12195
12444
  }
12196
12445
  function createRemoteDocumentCache(db) {
12197
- db.createObjectStore(DbRemoteDocument.store);
12446
+ db.createObjectStore(DbRemoteDocumentStore);
12198
12447
  }
12199
12448
  function createDocumentGlobalStore(db) {
12200
- db.createObjectStore(DbRemoteDocumentGlobal.store);
12449
+ db.createObjectStore(DbRemoteDocumentGlobalStore);
12201
12450
  }
12202
12451
  function createQueryCache(db) {
12203
- const targetDocumentsStore = db.createObjectStore(DbTargetDocument.store, {
12204
- keyPath: DbTargetDocument.keyPath
12452
+ const targetDocumentsStore = db.createObjectStore(DbTargetDocumentStore, {
12453
+ keyPath: DbTargetDocumentKeyPath
12205
12454
  });
12206
- targetDocumentsStore.createIndex(DbTargetDocument.documentTargetsIndex, DbTargetDocument.documentTargetsKeyPath, { unique: true });
12207
- const targetStore = db.createObjectStore(DbTarget.store, {
12208
- keyPath: DbTarget.keyPath
12455
+ targetDocumentsStore.createIndex(DbTargetDocumentDocumentTargetsIndex, DbTargetDocumentDocumentTargetsKeyPath, { unique: true });
12456
+ const targetStore = db.createObjectStore(DbTargetStore, {
12457
+ keyPath: DbTargetKeyPath
12209
12458
  });
12210
12459
  // NOTE: This is unique only because the TargetId is the suffix.
12211
- targetStore.createIndex(DbTarget.queryTargetsIndexName, DbTarget.queryTargetsKeyPath, { unique: true });
12212
- db.createObjectStore(DbTargetGlobal.store);
12460
+ targetStore.createIndex(DbTargetQueryTargetsIndexName, DbTargetQueryTargetsKeyPath, { unique: true });
12461
+ db.createObjectStore(DbTargetGlobalStore);
12213
12462
  }
12214
12463
  function dropQueryCache(db) {
12215
- db.deleteObjectStore(DbTargetDocument.store);
12216
- db.deleteObjectStore(DbTarget.store);
12217
- db.deleteObjectStore(DbTargetGlobal.store);
12464
+ db.deleteObjectStore(DbTargetDocumentStore);
12465
+ db.deleteObjectStore(DbTargetStore);
12466
+ db.deleteObjectStore(DbTargetGlobalStore);
12218
12467
  }
12219
12468
  function dropRemoteDocumentChangesStore(db) {
12220
12469
  if (db.objectStoreNames.contains('remoteDocumentChanges')) {
@@ -12227,58 +12476,60 @@ function dropRemoteDocumentChangesStore(db) {
12227
12476
  * @param txn - The version upgrade transaction for indexeddb
12228
12477
  */
12229
12478
  function writeEmptyTargetGlobalEntry(txn) {
12230
- const globalStore = txn.store(DbTargetGlobal.store);
12231
- const metadata = new DbTargetGlobal(
12232
- /*highestTargetId=*/ 0,
12233
- /*lastListenSequenceNumber=*/ 0, SnapshotVersion.min().toTimestamp(),
12234
- /*targetCount=*/ 0);
12235
- return globalStore.put(DbTargetGlobal.key, metadata);
12479
+ const globalStore = txn.store(DbTargetGlobalStore);
12480
+ const metadata = {
12481
+ highestTargetId: 0,
12482
+ highestListenSequenceNumber: 0,
12483
+ lastRemoteSnapshotVersion: SnapshotVersion.min().toTimestamp(),
12484
+ targetCount: 0
12485
+ };
12486
+ return globalStore.put(DbTargetGlobalKey, metadata);
12236
12487
  }
12237
12488
  /**
12238
12489
  * Creates indices on the RemoteDocuments store used for both multi-tab
12239
12490
  * and Index-Free queries.
12240
12491
  */
12241
12492
  function createRemoteDocumentReadTimeIndex(txn) {
12242
- const remoteDocumentStore = txn.objectStore(DbRemoteDocument.store);
12243
- remoteDocumentStore.createIndex(DbRemoteDocument.readTimeIndex, DbRemoteDocument.readTimeIndexPath, { unique: false });
12244
- remoteDocumentStore.createIndex(DbRemoteDocument.collectionReadTimeIndex, DbRemoteDocument.collectionReadTimeIndexPath, { unique: false });
12493
+ const remoteDocumentStore = txn.objectStore(DbRemoteDocumentStore);
12494
+ remoteDocumentStore.createIndex(DbRemoteDocumentReadTimeIndex, DbRemoteDocumentReadTimeIndexPath, { unique: false });
12495
+ remoteDocumentStore.createIndex(DbRemoteDocumentCollectionReadTimeIndex, DbRemoteDocumentCollectionReadTimeIndexPath, { unique: false });
12245
12496
  }
12246
12497
  function createClientMetadataStore(db) {
12247
- db.createObjectStore(DbClientMetadata.store, {
12248
- keyPath: DbClientMetadata.keyPath
12498
+ db.createObjectStore(DbClientMetadataStore, {
12499
+ keyPath: DbClientMetadataKeyPath
12249
12500
  });
12250
12501
  }
12251
12502
  function createBundlesStore(db) {
12252
- db.createObjectStore(DbBundle.store, {
12253
- keyPath: DbBundle.keyPath
12503
+ db.createObjectStore(DbBundleStore, {
12504
+ keyPath: DbBundleKeyPath
12254
12505
  });
12255
12506
  }
12256
12507
  function createNamedQueriesStore(db) {
12257
- db.createObjectStore(DbNamedQuery.store, {
12258
- keyPath: DbNamedQuery.keyPath
12508
+ db.createObjectStore(DbNamedQueryStore, {
12509
+ keyPath: DbNamedQueryKeyPath
12259
12510
  });
12260
12511
  }
12261
12512
  function createFieldIndex(db) {
12262
- const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
12263
- keyPath: DbIndexConfiguration.keyPath,
12513
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfigurationStore, {
12514
+ keyPath: DbIndexConfigurationKeyPath,
12264
12515
  autoIncrement: true
12265
12516
  });
12266
- indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
12267
- const indexStateStore = db.createObjectStore(DbIndexState.store, {
12268
- keyPath: DbIndexState.keyPath
12517
+ indexConfiguratioStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
12518
+ const indexStateStore = db.createObjectStore(DbIndexStateStore, {
12519
+ keyPath: DbIndexStateKeyPath
12269
12520
  });
12270
- indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
12271
- const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
12272
- keyPath: DbIndexEntry.keyPath
12521
+ indexStateStore.createIndex(DbIndexStateSequenceNumberIndex, DbIndexStateSequenceNumberIndexPath, { unique: false });
12522
+ const indexEntryStore = db.createObjectStore(DbIndexEntryStore, {
12523
+ keyPath: DbIndexEntryKeyPath
12273
12524
  });
12274
- indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
12525
+ indexEntryStore.createIndex(DbIndexEntryDocumentKeyIndex, DbIndexEntryDocumentKeyIndexPath, { unique: false });
12275
12526
  }
12276
12527
  function createDocumentOverlayStore(db) {
12277
- const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
12278
- keyPath: DbDocumentOverlay.keyPath
12528
+ const documentOverlayStore = db.createObjectStore(DbDocumentOverlayStore, {
12529
+ keyPath: DbDocumentOverlayKeyPath
12279
12530
  });
12280
- documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
12281
- documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
12531
+ documentOverlayStore.createIndex(DbDocumentOverlayCollectionPathOverlayIndex, DbDocumentOverlayCollectionPathOverlayIndexPath, { unique: false });
12532
+ documentOverlayStore.createIndex(DbDocumentOverlayCollectionGroupOverlayIndex, DbDocumentOverlayCollectionGroupOverlayIndexPath, { unique: false });
12282
12533
  }
12283
12534
 
12284
12535
  /**
@@ -12526,7 +12777,12 @@ class IndexedDbPersistence {
12526
12777
  return this.runTransaction('updateClientMetadataAndTryBecomePrimary', 'readwrite', txn => {
12527
12778
  const metadataStore = clientMetadataStore(txn);
12528
12779
  return metadataStore
12529
- .put(new DbClientMetadata(this.clientId, Date.now(), this.networkEnabled, this.inForeground))
12780
+ .put({
12781
+ clientId: this.clientId,
12782
+ updateTimeMs: Date.now(),
12783
+ networkEnabled: this.networkEnabled,
12784
+ inForeground: this.inForeground
12785
+ })
12530
12786
  .next(() => {
12531
12787
  if (this.isPrimary) {
12532
12788
  return this.verifyPrimaryLease(txn).next(success => {
@@ -12572,7 +12828,7 @@ class IndexedDbPersistence {
12572
12828
  }
12573
12829
  verifyPrimaryLease(txn) {
12574
12830
  const store = primaryClientStore(txn);
12575
- return store.get(DbPrimaryClient.key).next(primaryClient => {
12831
+ return store.get(DbPrimaryClientKey).next(primaryClient => {
12576
12832
  return PersistencePromise.resolve(this.isLocalClient(primaryClient));
12577
12833
  });
12578
12834
  }
@@ -12590,7 +12846,7 @@ class IndexedDbPersistence {
12590
12846
  !this.isWithinAge(this.lastGarbageCollectionTime, MAX_CLIENT_AGE_MS)) {
12591
12847
  this.lastGarbageCollectionTime = Date.now();
12592
12848
  const inactiveClients = await this.runTransaction('maybeGarbageCollectMultiClientState', 'readwrite-primary', txn => {
12593
- const metadataStore = getStore(txn, DbClientMetadata.store);
12849
+ const metadataStore = getStore(txn, DbClientMetadataStore);
12594
12850
  return metadataStore.loadAll().next(existingClients => {
12595
12851
  const active = this.filterActiveClients(existingClients, MAX_CLIENT_AGE_MS);
12596
12852
  const inactive = existingClients.filter(client => active.indexOf(client) === -1);
@@ -12644,7 +12900,7 @@ class IndexedDbPersistence {
12644
12900
  }
12645
12901
  const store = primaryClientStore(txn);
12646
12902
  return store
12647
- .get(DbPrimaryClient.key)
12903
+ .get(DbPrimaryClientKey)
12648
12904
  .next(currentPrimary => {
12649
12905
  const currentLeaseIsValid = currentPrimary !== null &&
12650
12906
  this.isWithinAge(currentPrimary.leaseTimestampMs, MAX_PRIMARY_ELIGIBLE_AGE_MS) &&
@@ -12725,7 +12981,7 @@ class IndexedDbPersistence {
12725
12981
  this.detachWindowUnloadHook();
12726
12982
  // Use `SimpleDb.runTransaction` directly to avoid failing if another tab
12727
12983
  // has obtained the primary lease.
12728
- await this.simpleDb.runTransaction('shutdown', 'readwrite', [DbPrimaryClient.store, DbClientMetadata.store], simpleDbTxn => {
12984
+ await this.simpleDb.runTransaction('shutdown', 'readwrite', [DbPrimaryClientStore, DbClientMetadataStore], simpleDbTxn => {
12729
12985
  const persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, ListenSequence.INVALID);
12730
12986
  return this.releasePrimaryLeaseIfHeld(persistenceTransaction).next(() => this.removeClientMetadata(persistenceTransaction));
12731
12987
  });
@@ -12832,7 +13088,7 @@ class IndexedDbPersistence {
12832
13088
  // be turned off.
12833
13089
  verifyAllowTabSynchronization(txn) {
12834
13090
  const store = primaryClientStore(txn);
12835
- return store.get(DbPrimaryClient.key).next(currentPrimary => {
13091
+ return store.get(DbPrimaryClientKey).next(currentPrimary => {
12836
13092
  const currentLeaseIsValid = currentPrimary !== null &&
12837
13093
  this.isWithinAge(currentPrimary.leaseTimestampMs, MAX_PRIMARY_ELIGIBLE_AGE_MS) &&
12838
13094
  !this.isClientZombied(currentPrimary.ownerId);
@@ -12850,8 +13106,12 @@ class IndexedDbPersistence {
12850
13106
  * method does not verify that the client is eligible for this lease.
12851
13107
  */
12852
13108
  acquireOrExtendPrimaryLease(txn) {
12853
- const newPrimary = new DbPrimaryClient(this.clientId, this.allowTabSynchronization, Date.now());
12854
- return primaryClientStore(txn).put(DbPrimaryClient.key, newPrimary);
13109
+ const newPrimary = {
13110
+ ownerId: this.clientId,
13111
+ allowTabSynchronization: this.allowTabSynchronization,
13112
+ leaseTimestampMs: Date.now()
13113
+ };
13114
+ return primaryClientStore(txn).put(DbPrimaryClientKey, newPrimary);
12855
13115
  }
12856
13116
  static isAvailable() {
12857
13117
  return SimpleDb.isAvailable();
@@ -12859,10 +13119,10 @@ class IndexedDbPersistence {
12859
13119
  /** Checks the primary lease and removes it if we are the current primary. */
12860
13120
  releasePrimaryLeaseIfHeld(txn) {
12861
13121
  const store = primaryClientStore(txn);
12862
- return store.get(DbPrimaryClient.key).next(primaryClient => {
13122
+ return store.get(DbPrimaryClientKey).next(primaryClient => {
12863
13123
  if (this.isLocalClient(primaryClient)) {
12864
13124
  logDebug(LOG_TAG$d, 'Releasing primary lease.');
12865
- return store.delete(DbPrimaryClient.key);
13125
+ return store.delete(DbPrimaryClientKey);
12866
13126
  }
12867
13127
  else {
12868
13128
  return PersistencePromise.resolve();
@@ -12996,13 +13256,13 @@ class IndexedDbPersistence {
12996
13256
  * Helper to get a typed SimpleDbStore for the primary client object store.
12997
13257
  */
12998
13258
  function primaryClientStore(txn) {
12999
- return getStore(txn, DbPrimaryClient.store);
13259
+ return getStore(txn, DbPrimaryClientStore);
13000
13260
  }
13001
13261
  /**
13002
13262
  * Helper to get a typed SimpleDbStore for the client metadata object store.
13003
13263
  */
13004
13264
  function clientMetadataStore(txn) {
13005
- return getStore(txn, DbClientMetadata.store);
13265
+ return getStore(txn, DbClientMetadataStore);
13006
13266
  }
13007
13267
  /**
13008
13268
  * Generates a string used as a prefix when storing data in IndexedDB and
@@ -14065,7 +14325,7 @@ class MemoryDocumentOverlayCache {
14065
14325
  return PersistencePromise.resolve(this.overlays.get(key));
14066
14326
  }
14067
14327
  saveOverlays(transaction, largestBatchId, overlays) {
14068
- overlays.forEach(mutation => {
14328
+ overlays.forEach((_, mutation) => {
14069
14329
  this.saveOverlay(transaction, largestBatchId, mutation);
14070
14330
  });
14071
14331
  return PersistencePromise.resolve();
@@ -14079,7 +14339,7 @@ class MemoryDocumentOverlayCache {
14079
14339
  return PersistencePromise.resolve();
14080
14340
  }
14081
14341
  getOverlaysForCollection(transaction, collection, sinceBatchId) {
14082
- const result = new Map();
14342
+ const result = newOverlayMap();
14083
14343
  const immediateChildrenPathLength = collection.length + 1;
14084
14344
  const prefix = new DocumentKey(collection.child(''));
14085
14345
  const iter = this.overlays.getIteratorFrom(prefix);
@@ -14113,19 +14373,19 @@ class MemoryDocumentOverlayCache {
14113
14373
  if (overlay.largestBatchId > sinceBatchId) {
14114
14374
  let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
14115
14375
  if (overlaysForBatchId === null) {
14116
- overlaysForBatchId = new Map();
14376
+ overlaysForBatchId = newOverlayMap();
14117
14377
  batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
14118
14378
  }
14119
14379
  overlaysForBatchId.set(overlay.getKey(), overlay);
14120
14380
  }
14121
14381
  }
14122
- const result = new Map();
14382
+ const result = newOverlayMap();
14123
14383
  const batchIter = batchIdToOverlays.getIterator();
14124
14384
  while (batchIter.hasNext()) {
14125
14385
  const entry = batchIter.getNext();
14126
14386
  const overlays = entry.value;
14127
- overlays.forEach((overlay, key) => result.set(key, overlay));
14128
- if (result.size >= count) {
14387
+ overlays.forEach((key, overlay) => result.set(key, overlay));
14388
+ if (result.size() >= count) {
14129
14389
  break;
14130
14390
  }
14131
14391
  }
@@ -14138,16 +14398,19 @@ class MemoryDocumentOverlayCache {
14138
14398
  // Remove the association of the overlay to its batch id.
14139
14399
  const existing = this.overlays.get(mutation.key);
14140
14400
  if (existing !== null) {
14141
- this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
14401
+ const newSet = this.overlayByBatchId
14402
+ .get(existing.largestBatchId)
14403
+ .delete(mutation.key);
14404
+ this.overlayByBatchId.set(existing.largestBatchId, newSet);
14142
14405
  }
14143
14406
  this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
14144
14407
  // Create the association of this overlay to the given largestBatchId.
14145
14408
  let batch = this.overlayByBatchId.get(largestBatchId);
14146
14409
  if (batch === undefined) {
14147
- batch = new Set();
14410
+ batch = documentKeySet();
14148
14411
  this.overlayByBatchId.set(largestBatchId, batch);
14149
14412
  }
14150
- batch.add(mutation.key);
14413
+ this.overlayByBatchId.set(largestBatchId, batch.add(mutation.key));
14151
14414
  }
14152
14415
  }
14153
14416
 
@@ -15998,9 +16261,13 @@ function createMetadata(databasePath, authToken, appCheckToken, appId) {
15998
16261
  metadata.set('X-Firebase-GMPID', appId);
15999
16262
  }
16000
16263
  metadata.set('X-Goog-Api-Client', X_GOOG_API_CLIENT_VALUE);
16001
- // This header is used to improve routing and project isolation by the
16264
+ // These headers are used to improve routing and project isolation by the
16002
16265
  // backend.
16266
+ // TODO(b/199767712): We are keeping 'Google-Cloud-Resource-Prefix' until Emulators can be
16267
+ // released with cl/428820046. Currently blocked because Emulators are now built with Java
16268
+ // 11 from Google3.
16003
16269
  metadata.set('Google-Cloud-Resource-Prefix', databasePath);
16270
+ metadata.set('x-goog-request-params', databasePath);
16004
16271
  return metadata;
16005
16272
  }
16006
16273
  /**