@firebase/firestore 3.7.3 → 3.8.0-canary.06dc1364d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/dist/firestore/lite/index.d.ts +1 -1
- package/dist/firestore/src/api/filter.d.ts +1 -1
- package/dist/firestore/src/api/reference_impl.d.ts +2 -1
- package/dist/firestore/src/api.d.ts +1 -1
- package/dist/firestore/src/core/bound.d.ts +49 -0
- package/dist/firestore/src/core/filter.d.ts +134 -0
- package/dist/firestore/src/core/order_by.d.ts +35 -0
- package/dist/firestore/src/core/query.d.ts +5 -6
- package/dist/firestore/src/core/target.d.ts +4 -120
- package/dist/firestore/src/lite-api/query.d.ts +275 -66
- package/dist/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
- package/dist/firestore/src/local/local_documents_view.d.ts +0 -2
- package/dist/firestore/src/model/document.d.ts +8 -1
- package/dist/firestore/src/protos/firestore_proto_api.d.ts +1 -1
- package/dist/firestore/src/remote/serializer.d.ts +10 -3
- package/dist/firestore/src/util/logic_utils.d.ts +52 -0
- package/dist/firestore/src/util/types.d.ts +1 -1
- package/dist/firestore/test/lite/helpers.d.ts +1 -1
- package/dist/firestore/test/unit/core/filter.test.d.ts +17 -0
- package/dist/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
- package/dist/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
- package/dist/firestore/test/util/helpers.d.ts +6 -2
- package/dist/firestore/test/util/test_platform.d.ts +2 -2
- package/dist/index.d.ts +140 -63
- package/dist/index.esm2017.js +4307 -3743
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4994 -4410
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1670 -890
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1663 -891
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4532 -3968
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +332 -69
- package/dist/lite/firestore/lite/index.d.ts +1 -1
- package/dist/lite/firestore/src/api/filter.d.ts +1 -1
- package/dist/lite/firestore/src/api/reference_impl.d.ts +2 -1
- package/dist/lite/firestore/src/api.d.ts +1 -1
- package/dist/lite/firestore/src/core/bound.d.ts +49 -0
- package/dist/lite/firestore/src/core/filter.d.ts +134 -0
- package/dist/lite/firestore/src/core/order_by.d.ts +35 -0
- package/dist/lite/firestore/src/core/query.d.ts +5 -6
- package/dist/lite/firestore/src/core/target.d.ts +4 -120
- package/dist/lite/firestore/src/lite-api/query.d.ts +275 -66
- package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
- package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -2
- package/dist/lite/firestore/src/model/document.d.ts +8 -1
- package/dist/lite/firestore/src/protos/firestore_proto_api.d.ts +1 -1
- package/dist/lite/firestore/src/remote/serializer.d.ts +10 -3
- package/dist/lite/firestore/src/util/logic_utils.d.ts +52 -0
- package/dist/lite/firestore/src/util/types.d.ts +1 -1
- package/dist/lite/firestore/test/lite/helpers.d.ts +1 -1
- package/dist/lite/firestore/test/unit/core/filter.test.d.ts +17 -0
- package/dist/lite/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
- package/dist/lite/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
- package/dist/lite/firestore/test/util/helpers.d.ts +6 -2
- package/dist/lite/firestore/test/util/test_platform.d.ts +2 -2
- package/dist/lite/index.browser.esm2017.js +2093 -1793
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +2241 -2000
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.d.ts +138 -62
- package/dist/lite/index.node.cjs.js +2214 -1802
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +2207 -1803
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +2079 -1778
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +330 -66
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +324 -174
- package/dist/lite/packages/firestore/lite/index.d.ts +1 -1
- package/dist/lite/packages/firestore/rollup.config.debug.d.ts +1 -1
- package/dist/lite/packages/firestore/src/api/filter.d.ts +1 -1
- package/dist/lite/packages/firestore/src/api/reference_impl.d.ts +2 -1
- package/dist/lite/packages/firestore/src/api.d.ts +1 -1
- package/dist/lite/packages/firestore/src/core/bound.d.ts +49 -0
- package/dist/lite/packages/firestore/src/core/filter.d.ts +134 -0
- package/dist/lite/packages/firestore/src/core/order_by.d.ts +35 -0
- package/dist/lite/packages/firestore/src/core/query.d.ts +5 -6
- package/dist/lite/packages/firestore/src/core/target.d.ts +4 -120
- package/dist/lite/packages/firestore/src/lite-api/query.d.ts +275 -66
- package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
- package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -2
- package/dist/lite/packages/firestore/src/model/document.d.ts +8 -1
- package/dist/lite/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
- package/dist/lite/packages/firestore/src/remote/serializer.d.ts +10 -3
- package/dist/lite/packages/firestore/src/util/logic_utils.d.ts +52 -0
- package/dist/lite/packages/firestore/src/util/types.d.ts +1 -1
- package/dist/lite/packages/firestore/test/lite/helpers.d.ts +1 -1
- package/dist/lite/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
- package/dist/lite/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
- package/dist/lite/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
- package/dist/lite/packages/firestore/test/util/helpers.d.ts +6 -2
- package/dist/lite/packages/firestore/test/util/test_platform.d.ts +2 -2
- package/dist/lite/private.d.ts +240 -66
- package/dist/packages/firestore/dist/index.esm2017.d.ts +373 -210
- package/dist/packages/firestore/lite/index.d.ts +1 -1
- package/dist/packages/firestore/rollup.config.debug.d.ts +1 -1
- package/dist/packages/firestore/src/api/filter.d.ts +1 -1
- package/dist/packages/firestore/src/api/reference_impl.d.ts +2 -1
- package/dist/packages/firestore/src/api.d.ts +1 -1
- package/dist/packages/firestore/src/core/bound.d.ts +49 -0
- package/dist/packages/firestore/src/core/filter.d.ts +134 -0
- package/dist/packages/firestore/src/core/order_by.d.ts +35 -0
- package/dist/packages/firestore/src/core/query.d.ts +5 -6
- package/dist/packages/firestore/src/core/target.d.ts +4 -120
- package/dist/packages/firestore/src/lite-api/query.d.ts +275 -66
- package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +2 -0
- package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -2
- package/dist/packages/firestore/src/model/document.d.ts +8 -1
- package/dist/packages/firestore/src/protos/firestore_proto_api.d.ts +1 -1
- package/dist/packages/firestore/src/remote/serializer.d.ts +10 -3
- package/dist/packages/firestore/src/util/logic_utils.d.ts +52 -0
- package/dist/packages/firestore/src/util/types.d.ts +1 -1
- package/dist/packages/firestore/test/lite/helpers.d.ts +1 -1
- package/dist/packages/firestore/test/unit/core/filter.test.d.ts +17 -0
- package/dist/packages/firestore/test/unit/specs/spec_test_runner.d.ts +2 -1
- package/dist/packages/firestore/test/unit/util/logic_utils.test.d.ts +17 -0
- package/dist/packages/firestore/test/util/helpers.d.ts +6 -2
- package/dist/packages/firestore/test/util/test_platform.d.ts +2 -2
- package/dist/private.d.ts +242 -69
- package/package.json +14 -12
package/dist/index.node.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import * as grpc from '@grpc/grpc-js';
|
|
|
8
8
|
import * as protoLoader from '@grpc/proto-loader';
|
|
9
9
|
|
|
10
10
|
const name = "@firebase/firestore";
|
|
11
|
-
const version$1 = "3.
|
|
11
|
+
const version$1 = "3.8.0-canary.06dc1364d";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
61
61
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
62
62
|
User.MOCK_USER = new User('mock-user');
|
|
63
63
|
|
|
64
|
-
const version = "9.
|
|
64
|
+
const version = "9.15.0-canary.06dc1364d";
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @license
|
|
@@ -1499,11 +1499,11 @@ class FieldIndex {
|
|
|
1499
1499
|
FieldIndex.UNKNOWN_ID = -1;
|
|
1500
1500
|
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
1501
1501
|
function fieldIndexGetArraySegment(fieldIndex) {
|
|
1502
|
-
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
1502
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* IndexKind.CONTAINS */);
|
|
1503
1503
|
}
|
|
1504
1504
|
/** Returns all directional (ascending/descending) segments for this index. */
|
|
1505
1505
|
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
1506
|
-
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
1506
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* IndexKind.CONTAINS */);
|
|
1507
1507
|
}
|
|
1508
1508
|
/**
|
|
1509
1509
|
* Returns the order of the document key component for the given index.
|
|
@@ -1513,7 +1513,7 @@ function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
|
1513
1513
|
function fieldIndexGetKeyOrder(fieldIndex) {
|
|
1514
1514
|
const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
|
|
1515
1515
|
return directionalSegments.length === 0
|
|
1516
|
-
? 0 /* ASCENDING */
|
|
1516
|
+
? 0 /* IndexKind.ASCENDING */
|
|
1517
1517
|
: directionalSegments[directionalSegments.length - 1].kind;
|
|
1518
1518
|
}
|
|
1519
1519
|
/**
|
|
@@ -2627,7 +2627,7 @@ class IndexBackfillerScheduler {
|
|
|
2627
2627
|
}
|
|
2628
2628
|
schedule(delay) {
|
|
2629
2629
|
logDebug(LOG_TAG$h, `Scheduled in ${delay}ms`);
|
|
2630
|
-
this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* IndexBackfill */, delay, async () => {
|
|
2630
|
+
this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* TimerId.IndexBackfill */, delay, async () => {
|
|
2631
2631
|
this.task = null;
|
|
2632
2632
|
try {
|
|
2633
2633
|
const documentsProcessed = await this.backfiller.backfill();
|
|
@@ -4423,40 +4423,40 @@ const MIN_VALUE = {
|
|
|
4423
4423
|
/** Extracts the backend's type order for the provided value. */
|
|
4424
4424
|
function typeOrder(value) {
|
|
4425
4425
|
if ('nullValue' in value) {
|
|
4426
|
-
return 0 /* NullValue */;
|
|
4426
|
+
return 0 /* TypeOrder.NullValue */;
|
|
4427
4427
|
}
|
|
4428
4428
|
else if ('booleanValue' in value) {
|
|
4429
|
-
return 1 /* BooleanValue */;
|
|
4429
|
+
return 1 /* TypeOrder.BooleanValue */;
|
|
4430
4430
|
}
|
|
4431
4431
|
else if ('integerValue' in value || 'doubleValue' in value) {
|
|
4432
|
-
return 2 /* NumberValue */;
|
|
4432
|
+
return 2 /* TypeOrder.NumberValue */;
|
|
4433
4433
|
}
|
|
4434
4434
|
else if ('timestampValue' in value) {
|
|
4435
|
-
return 3 /* TimestampValue */;
|
|
4435
|
+
return 3 /* TypeOrder.TimestampValue */;
|
|
4436
4436
|
}
|
|
4437
4437
|
else if ('stringValue' in value) {
|
|
4438
|
-
return 5 /* StringValue */;
|
|
4438
|
+
return 5 /* TypeOrder.StringValue */;
|
|
4439
4439
|
}
|
|
4440
4440
|
else if ('bytesValue' in value) {
|
|
4441
|
-
return 6 /* BlobValue */;
|
|
4441
|
+
return 6 /* TypeOrder.BlobValue */;
|
|
4442
4442
|
}
|
|
4443
4443
|
else if ('referenceValue' in value) {
|
|
4444
|
-
return 7 /* RefValue */;
|
|
4444
|
+
return 7 /* TypeOrder.RefValue */;
|
|
4445
4445
|
}
|
|
4446
4446
|
else if ('geoPointValue' in value) {
|
|
4447
|
-
return 8 /* GeoPointValue */;
|
|
4447
|
+
return 8 /* TypeOrder.GeoPointValue */;
|
|
4448
4448
|
}
|
|
4449
4449
|
else if ('arrayValue' in value) {
|
|
4450
|
-
return 9 /* ArrayValue */;
|
|
4450
|
+
return 9 /* TypeOrder.ArrayValue */;
|
|
4451
4451
|
}
|
|
4452
4452
|
else if ('mapValue' in value) {
|
|
4453
4453
|
if (isServerTimestamp(value)) {
|
|
4454
|
-
return 4 /* ServerTimestampValue */;
|
|
4454
|
+
return 4 /* TypeOrder.ServerTimestampValue */;
|
|
4455
4455
|
}
|
|
4456
4456
|
else if (isMaxValue(value)) {
|
|
4457
|
-
return 9007199254740991 /* MaxValue */;
|
|
4457
|
+
return 9007199254740991 /* TypeOrder.MaxValue */;
|
|
4458
4458
|
}
|
|
4459
|
-
return 10 /* ObjectValue */;
|
|
4459
|
+
return 10 /* TypeOrder.ObjectValue */;
|
|
4460
4460
|
}
|
|
4461
4461
|
else {
|
|
4462
4462
|
return fail();
|
|
@@ -4473,29 +4473,29 @@ function valueEquals(left, right) {
|
|
|
4473
4473
|
return false;
|
|
4474
4474
|
}
|
|
4475
4475
|
switch (leftType) {
|
|
4476
|
-
case 0 /* NullValue */:
|
|
4476
|
+
case 0 /* TypeOrder.NullValue */:
|
|
4477
4477
|
return true;
|
|
4478
|
-
case 1 /* BooleanValue */:
|
|
4478
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
4479
4479
|
return left.booleanValue === right.booleanValue;
|
|
4480
|
-
case 4 /* ServerTimestampValue */:
|
|
4480
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
4481
4481
|
return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
|
|
4482
|
-
case 3 /* TimestampValue */:
|
|
4482
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
4483
4483
|
return timestampEquals(left, right);
|
|
4484
|
-
case 5 /* StringValue */:
|
|
4484
|
+
case 5 /* TypeOrder.StringValue */:
|
|
4485
4485
|
return left.stringValue === right.stringValue;
|
|
4486
|
-
case 6 /* BlobValue */:
|
|
4486
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
4487
4487
|
return blobEquals(left, right);
|
|
4488
|
-
case 7 /* RefValue */:
|
|
4488
|
+
case 7 /* TypeOrder.RefValue */:
|
|
4489
4489
|
return left.referenceValue === right.referenceValue;
|
|
4490
|
-
case 8 /* GeoPointValue */:
|
|
4490
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
4491
4491
|
return geoPointEquals(left, right);
|
|
4492
|
-
case 2 /* NumberValue */:
|
|
4492
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
4493
4493
|
return numberEquals(left, right);
|
|
4494
|
-
case 9 /* ArrayValue */:
|
|
4494
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
4495
4495
|
return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
|
|
4496
|
-
case 10 /* ObjectValue */:
|
|
4496
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
4497
4497
|
return objectEquals(left, right);
|
|
4498
|
-
case 9007199254740991 /* MaxValue */:
|
|
4498
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
4499
4499
|
return true;
|
|
4500
4500
|
default:
|
|
4501
4501
|
return fail();
|
|
@@ -4568,28 +4568,28 @@ function valueCompare(left, right) {
|
|
|
4568
4568
|
return primitiveComparator(leftType, rightType);
|
|
4569
4569
|
}
|
|
4570
4570
|
switch (leftType) {
|
|
4571
|
-
case 0 /* NullValue */:
|
|
4572
|
-
case 9007199254740991 /* MaxValue */:
|
|
4571
|
+
case 0 /* TypeOrder.NullValue */:
|
|
4572
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
4573
4573
|
return 0;
|
|
4574
|
-
case 1 /* BooleanValue */:
|
|
4574
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
4575
4575
|
return primitiveComparator(left.booleanValue, right.booleanValue);
|
|
4576
|
-
case 2 /* NumberValue */:
|
|
4576
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
4577
4577
|
return compareNumbers(left, right);
|
|
4578
|
-
case 3 /* TimestampValue */:
|
|
4578
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
4579
4579
|
return compareTimestamps(left.timestampValue, right.timestampValue);
|
|
4580
|
-
case 4 /* ServerTimestampValue */:
|
|
4580
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
4581
4581
|
return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
|
|
4582
|
-
case 5 /* StringValue */:
|
|
4582
|
+
case 5 /* TypeOrder.StringValue */:
|
|
4583
4583
|
return primitiveComparator(left.stringValue, right.stringValue);
|
|
4584
|
-
case 6 /* BlobValue */:
|
|
4584
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
4585
4585
|
return compareBlobs(left.bytesValue, right.bytesValue);
|
|
4586
|
-
case 7 /* RefValue */:
|
|
4586
|
+
case 7 /* TypeOrder.RefValue */:
|
|
4587
4587
|
return compareReferences(left.referenceValue, right.referenceValue);
|
|
4588
|
-
case 8 /* GeoPointValue */:
|
|
4588
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
4589
4589
|
return compareGeoPoints(left.geoPointValue, right.geoPointValue);
|
|
4590
|
-
case 9 /* ArrayValue */:
|
|
4590
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
4591
4591
|
return compareArrays(left.arrayValue, right.arrayValue);
|
|
4592
|
-
case 10 /* ObjectValue */:
|
|
4592
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
4593
4593
|
return compareMaps(left.mapValue, right.mapValue);
|
|
4594
4594
|
default:
|
|
4595
4595
|
throw fail();
|
|
@@ -5142,11 +5142,12 @@ function extractFieldMask(value) {
|
|
|
5142
5142
|
* from all views.
|
|
5143
5143
|
*/
|
|
5144
5144
|
class MutableDocument {
|
|
5145
|
-
constructor(key, documentType, version, readTime, data, documentState) {
|
|
5145
|
+
constructor(key, documentType, version, readTime, createTime, data, documentState) {
|
|
5146
5146
|
this.key = key;
|
|
5147
5147
|
this.documentType = documentType;
|
|
5148
5148
|
this.version = version;
|
|
5149
5149
|
this.readTime = readTime;
|
|
5150
|
+
this.createTime = createTime;
|
|
5150
5151
|
this.data = data;
|
|
5151
5152
|
this.documentState = documentState;
|
|
5152
5153
|
}
|
|
@@ -5155,18 +5156,27 @@ class MutableDocument {
|
|
|
5155
5156
|
* base document for mutations.
|
|
5156
5157
|
*/
|
|
5157
5158
|
static newInvalidDocument(documentKey) {
|
|
5158
|
-
return new MutableDocument(documentKey, 0 /* INVALID */,
|
|
5159
|
+
return new MutableDocument(documentKey, 0 /* DocumentType.INVALID */,
|
|
5160
|
+
/* version */ SnapshotVersion.min(),
|
|
5161
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5162
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
5159
5163
|
}
|
|
5160
5164
|
/**
|
|
5161
5165
|
* Creates a new document that is known to exist with the given data at the
|
|
5162
5166
|
* given version.
|
|
5163
5167
|
*/
|
|
5164
|
-
static newFoundDocument(documentKey, version, value) {
|
|
5165
|
-
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */,
|
|
5168
|
+
static newFoundDocument(documentKey, version, createTime, value) {
|
|
5169
|
+
return new MutableDocument(documentKey, 1 /* DocumentType.FOUND_DOCUMENT */,
|
|
5170
|
+
/* version */ version,
|
|
5171
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5172
|
+
/* createTime */ createTime, value, 0 /* DocumentState.SYNCED */);
|
|
5166
5173
|
}
|
|
5167
5174
|
/** Creates a new document that is known to not exist at the given version. */
|
|
5168
5175
|
static newNoDocument(documentKey, version) {
|
|
5169
|
-
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */,
|
|
5176
|
+
return new MutableDocument(documentKey, 2 /* DocumentType.NO_DOCUMENT */,
|
|
5177
|
+
/* version */ version,
|
|
5178
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5179
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
5170
5180
|
}
|
|
5171
5181
|
/**
|
|
5172
5182
|
* Creates a new document that is known to exist at the given version but
|
|
@@ -5174,17 +5184,30 @@ class MutableDocument {
|
|
|
5174
5184
|
* base document).
|
|
5175
5185
|
*/
|
|
5176
5186
|
static newUnknownDocument(documentKey, version) {
|
|
5177
|
-
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */,
|
|
5187
|
+
return new MutableDocument(documentKey, 3 /* DocumentType.UNKNOWN_DOCUMENT */,
|
|
5188
|
+
/* version */ version,
|
|
5189
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5190
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */);
|
|
5178
5191
|
}
|
|
5179
5192
|
/**
|
|
5180
5193
|
* Changes the document type to indicate that it exists and that its version
|
|
5181
5194
|
* and data are known.
|
|
5182
5195
|
*/
|
|
5183
5196
|
convertToFoundDocument(version, value) {
|
|
5197
|
+
// If a document is switching state from being an invalid or deleted
|
|
5198
|
+
// document to a valid (FOUND_DOCUMENT) document, either due to receiving an
|
|
5199
|
+
// update from Watch or due to applying a local set mutation on top
|
|
5200
|
+
// of a deleted document, our best guess about its createTime would be the
|
|
5201
|
+
// version at which the document transitioned to a FOUND_DOCUMENT.
|
|
5202
|
+
if (this.createTime.isEqual(SnapshotVersion.min()) &&
|
|
5203
|
+
(this.documentType === 2 /* DocumentType.NO_DOCUMENT */ ||
|
|
5204
|
+
this.documentType === 0 /* DocumentType.INVALID */)) {
|
|
5205
|
+
this.createTime = version;
|
|
5206
|
+
}
|
|
5184
5207
|
this.version = version;
|
|
5185
|
-
this.documentType = 1 /* FOUND_DOCUMENT */;
|
|
5208
|
+
this.documentType = 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
5186
5209
|
this.data = value;
|
|
5187
|
-
this.documentState = 0 /* SYNCED */;
|
|
5210
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
5188
5211
|
return this;
|
|
5189
5212
|
}
|
|
5190
5213
|
/**
|
|
@@ -5193,9 +5216,9 @@ class MutableDocument {
|
|
|
5193
5216
|
*/
|
|
5194
5217
|
convertToNoDocument(version) {
|
|
5195
5218
|
this.version = version;
|
|
5196
|
-
this.documentType = 2 /* NO_DOCUMENT */;
|
|
5219
|
+
this.documentType = 2 /* DocumentType.NO_DOCUMENT */;
|
|
5197
5220
|
this.data = ObjectValue.empty();
|
|
5198
|
-
this.documentState = 0 /* SYNCED */;
|
|
5221
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
5199
5222
|
return this;
|
|
5200
5223
|
}
|
|
5201
5224
|
/**
|
|
@@ -5205,17 +5228,17 @@ class MutableDocument {
|
|
|
5205
5228
|
*/
|
|
5206
5229
|
convertToUnknownDocument(version) {
|
|
5207
5230
|
this.version = version;
|
|
5208
|
-
this.documentType = 3 /* UNKNOWN_DOCUMENT */;
|
|
5231
|
+
this.documentType = 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
5209
5232
|
this.data = ObjectValue.empty();
|
|
5210
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5233
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5211
5234
|
return this;
|
|
5212
5235
|
}
|
|
5213
5236
|
setHasCommittedMutations() {
|
|
5214
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5237
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5215
5238
|
return this;
|
|
5216
5239
|
}
|
|
5217
5240
|
setHasLocalMutations() {
|
|
5218
|
-
this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
|
|
5241
|
+
this.documentState = 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
5219
5242
|
this.version = SnapshotVersion.min();
|
|
5220
5243
|
return this;
|
|
5221
5244
|
}
|
|
@@ -5224,25 +5247,25 @@ class MutableDocument {
|
|
|
5224
5247
|
return this;
|
|
5225
5248
|
}
|
|
5226
5249
|
get hasLocalMutations() {
|
|
5227
|
-
return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
|
|
5250
|
+
return this.documentState === 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
5228
5251
|
}
|
|
5229
5252
|
get hasCommittedMutations() {
|
|
5230
|
-
return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5253
|
+
return this.documentState === 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5231
5254
|
}
|
|
5232
5255
|
get hasPendingWrites() {
|
|
5233
5256
|
return this.hasLocalMutations || this.hasCommittedMutations;
|
|
5234
5257
|
}
|
|
5235
5258
|
isValidDocument() {
|
|
5236
|
-
return this.documentType !== 0 /* INVALID */;
|
|
5259
|
+
return this.documentType !== 0 /* DocumentType.INVALID */;
|
|
5237
5260
|
}
|
|
5238
5261
|
isFoundDocument() {
|
|
5239
|
-
return this.documentType === 1 /* FOUND_DOCUMENT */;
|
|
5262
|
+
return this.documentType === 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
5240
5263
|
}
|
|
5241
5264
|
isNoDocument() {
|
|
5242
|
-
return this.documentType === 2 /* NO_DOCUMENT */;
|
|
5265
|
+
return this.documentType === 2 /* DocumentType.NO_DOCUMENT */;
|
|
5243
5266
|
}
|
|
5244
5267
|
isUnknownDocument() {
|
|
5245
|
-
return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
|
|
5268
|
+
return this.documentType === 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
5246
5269
|
}
|
|
5247
5270
|
isEqual(other) {
|
|
5248
5271
|
return (other instanceof MutableDocument &&
|
|
@@ -5253,10 +5276,11 @@ class MutableDocument {
|
|
|
5253
5276
|
this.data.isEqual(other.data));
|
|
5254
5277
|
}
|
|
5255
5278
|
mutableCopy() {
|
|
5256
|
-
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
|
|
5279
|
+
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.createTime, this.data.clone(), this.documentState);
|
|
5257
5280
|
}
|
|
5258
5281
|
toString() {
|
|
5259
5282
|
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
5283
|
+
`{createTime: ${this.createTime}}), ` +
|
|
5260
5284
|
`{documentType: ${this.documentType}}), ` +
|
|
5261
5285
|
`{documentState: ${this.documentState}})`);
|
|
5262
5286
|
}
|
|
@@ -5278,7 +5302,7 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
5278
5302
|
|
|
5279
5303
|
/**
|
|
5280
5304
|
* @license
|
|
5281
|
-
* Copyright
|
|
5305
|
+
* Copyright 2022 Google LLC
|
|
5282
5306
|
*
|
|
5283
5307
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5284
5308
|
* you may not use this file except in compliance with the License.
|
|
@@ -5292,453 +5316,353 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
5292
5316
|
* See the License for the specific language governing permissions and
|
|
5293
5317
|
* limitations under the License.
|
|
5294
5318
|
*/
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5319
|
+
/**
|
|
5320
|
+
* Represents a bound of a query.
|
|
5321
|
+
*
|
|
5322
|
+
* The bound is specified with the given components representing a position and
|
|
5323
|
+
* whether it's just before or just after the position (relative to whatever the
|
|
5324
|
+
* query order is).
|
|
5325
|
+
*
|
|
5326
|
+
* The position represents a logical index position for a query. It's a prefix
|
|
5327
|
+
* of values for the (potentially implicit) order by clauses of a query.
|
|
5328
|
+
*
|
|
5329
|
+
* Bound provides a function to determine whether a document comes before or
|
|
5330
|
+
* after a bound. This is influenced by whether the position is just before or
|
|
5331
|
+
* just after the provided values.
|
|
5332
|
+
*/
|
|
5333
|
+
class Bound {
|
|
5334
|
+
constructor(position, inclusive) {
|
|
5335
|
+
this.position = position;
|
|
5336
|
+
this.inclusive = inclusive;
|
|
5306
5337
|
}
|
|
5307
5338
|
}
|
|
5339
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
5340
|
+
let comparison = 0;
|
|
5341
|
+
for (let i = 0; i < bound.position.length; i++) {
|
|
5342
|
+
const orderByComponent = orderBy[i];
|
|
5343
|
+
const component = bound.position[i];
|
|
5344
|
+
if (orderByComponent.field.isKeyField()) {
|
|
5345
|
+
comparison = DocumentKey.comparator(DocumentKey.fromName(component.referenceValue), doc.key);
|
|
5346
|
+
}
|
|
5347
|
+
else {
|
|
5348
|
+
const docValue = doc.data.field(orderByComponent.field);
|
|
5349
|
+
comparison = valueCompare(component, docValue);
|
|
5350
|
+
}
|
|
5351
|
+
if (orderByComponent.dir === "desc" /* Direction.DESCENDING */) {
|
|
5352
|
+
comparison = comparison * -1;
|
|
5353
|
+
}
|
|
5354
|
+
if (comparison !== 0) {
|
|
5355
|
+
break;
|
|
5356
|
+
}
|
|
5357
|
+
}
|
|
5358
|
+
return comparison;
|
|
5359
|
+
}
|
|
5308
5360
|
/**
|
|
5309
|
-
*
|
|
5310
|
-
*
|
|
5361
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5362
|
+
* order.
|
|
5363
|
+
*/
|
|
5364
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5365
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5366
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5367
|
+
}
|
|
5368
|
+
/**
|
|
5369
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5370
|
+
* order.
|
|
5371
|
+
*/
|
|
5372
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5373
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5374
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
5375
|
+
}
|
|
5376
|
+
function boundEquals(left, right) {
|
|
5377
|
+
if (left === null) {
|
|
5378
|
+
return right === null;
|
|
5379
|
+
}
|
|
5380
|
+
else if (right === null) {
|
|
5381
|
+
return false;
|
|
5382
|
+
}
|
|
5383
|
+
if (left.inclusive !== right.inclusive ||
|
|
5384
|
+
left.position.length !== right.position.length) {
|
|
5385
|
+
return false;
|
|
5386
|
+
}
|
|
5387
|
+
for (let i = 0; i < left.position.length; i++) {
|
|
5388
|
+
const leftPosition = left.position[i];
|
|
5389
|
+
const rightPosition = right.position[i];
|
|
5390
|
+
if (!valueEquals(leftPosition, rightPosition)) {
|
|
5391
|
+
return false;
|
|
5392
|
+
}
|
|
5393
|
+
}
|
|
5394
|
+
return true;
|
|
5395
|
+
}
|
|
5396
|
+
|
|
5397
|
+
/**
|
|
5398
|
+
* @license
|
|
5399
|
+
* Copyright 2022 Google LLC
|
|
5311
5400
|
*
|
|
5312
|
-
*
|
|
5313
|
-
*
|
|
5314
|
-
*
|
|
5401
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5402
|
+
* you may not use this file except in compliance with the License.
|
|
5403
|
+
* You may obtain a copy of the License at
|
|
5404
|
+
*
|
|
5405
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5406
|
+
*
|
|
5407
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5408
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5409
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5410
|
+
* See the License for the specific language governing permissions and
|
|
5411
|
+
* limitations under the License.
|
|
5315
5412
|
*/
|
|
5316
|
-
|
|
5317
|
-
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
5413
|
+
class Filter {
|
|
5318
5414
|
}
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5415
|
+
class FieldFilter extends Filter {
|
|
5416
|
+
constructor(field, op, value) {
|
|
5417
|
+
super();
|
|
5418
|
+
this.field = field;
|
|
5419
|
+
this.op = op;
|
|
5420
|
+
this.value = value;
|
|
5421
|
+
}
|
|
5422
|
+
/**
|
|
5423
|
+
* Creates a filter based on the provided arguments.
|
|
5424
|
+
*/
|
|
5425
|
+
static create(field, op, value) {
|
|
5426
|
+
if (field.isKeyField()) {
|
|
5427
|
+
if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
5428
|
+
return this.createKeyFieldInFilter(field, op, value);
|
|
5429
|
+
}
|
|
5430
|
+
else {
|
|
5431
|
+
return new KeyFieldFilter(field, op, value);
|
|
5432
|
+
}
|
|
5325
5433
|
}
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
str += '|ob:';
|
|
5329
|
-
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
5330
|
-
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
5331
|
-
str += '|l:';
|
|
5332
|
-
str += targetImpl.limit;
|
|
5434
|
+
else if (op === "array-contains" /* Operator.ARRAY_CONTAINS */) {
|
|
5435
|
+
return new ArrayContainsFilter(field, value);
|
|
5333
5436
|
}
|
|
5334
|
-
if (
|
|
5335
|
-
|
|
5336
|
-
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
5337
|
-
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5437
|
+
else if (op === "in" /* Operator.IN */) {
|
|
5438
|
+
return new InFilter(field, value);
|
|
5338
5439
|
}
|
|
5339
|
-
if (
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5440
|
+
else if (op === "not-in" /* Operator.NOT_IN */) {
|
|
5441
|
+
return new NotInFilter(field, value);
|
|
5442
|
+
}
|
|
5443
|
+
else if (op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
5444
|
+
return new ArrayContainsAnyFilter(field, value);
|
|
5445
|
+
}
|
|
5446
|
+
else {
|
|
5447
|
+
return new FieldFilter(field, op, value);
|
|
5343
5448
|
}
|
|
5344
|
-
targetImpl.memoizedCanonicalId = str;
|
|
5345
5449
|
}
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
if (target.collectionGroup !== null) {
|
|
5351
|
-
str += ' collectionGroup=' + target.collectionGroup;
|
|
5450
|
+
static createKeyFieldInFilter(field, op, value) {
|
|
5451
|
+
return op === "in" /* Operator.IN */
|
|
5452
|
+
? new KeyFieldInFilter(field, value)
|
|
5453
|
+
: new KeyFieldNotInFilter(field, value);
|
|
5352
5454
|
}
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5455
|
+
matches(doc) {
|
|
5456
|
+
const other = doc.data.field(this.field);
|
|
5457
|
+
// Types do not have to match in NOT_EQUAL filters.
|
|
5458
|
+
if (this.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
5459
|
+
return (other !== null &&
|
|
5460
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
5461
|
+
}
|
|
5462
|
+
// Only compare types with matching backend order (such as double and int).
|
|
5463
|
+
return (other !== null &&
|
|
5464
|
+
typeOrder(this.value) === typeOrder(other) &&
|
|
5465
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
5357
5466
|
}
|
|
5358
|
-
|
|
5359
|
-
|
|
5467
|
+
matchesComparison(comparison) {
|
|
5468
|
+
switch (this.op) {
|
|
5469
|
+
case "<" /* Operator.LESS_THAN */:
|
|
5470
|
+
return comparison < 0;
|
|
5471
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
5472
|
+
return comparison <= 0;
|
|
5473
|
+
case "==" /* Operator.EQUAL */:
|
|
5474
|
+
return comparison === 0;
|
|
5475
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
5476
|
+
return comparison !== 0;
|
|
5477
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
5478
|
+
return comparison > 0;
|
|
5479
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
5480
|
+
return comparison >= 0;
|
|
5481
|
+
default:
|
|
5482
|
+
return fail();
|
|
5483
|
+
}
|
|
5360
5484
|
}
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
.
|
|
5364
|
-
.
|
|
5485
|
+
isInequality() {
|
|
5486
|
+
return ([
|
|
5487
|
+
"<" /* Operator.LESS_THAN */,
|
|
5488
|
+
"<=" /* Operator.LESS_THAN_OR_EQUAL */,
|
|
5489
|
+
">" /* Operator.GREATER_THAN */,
|
|
5490
|
+
">=" /* Operator.GREATER_THAN_OR_EQUAL */,
|
|
5491
|
+
"!=" /* Operator.NOT_EQUAL */,
|
|
5492
|
+
"not-in" /* Operator.NOT_IN */
|
|
5493
|
+
].indexOf(this.op) >= 0);
|
|
5365
5494
|
}
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
5369
|
-
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5495
|
+
getFlattenedFilters() {
|
|
5496
|
+
return [this];
|
|
5370
5497
|
}
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5498
|
+
getFilters() {
|
|
5499
|
+
return [this];
|
|
5500
|
+
}
|
|
5501
|
+
getFirstInequalityField() {
|
|
5502
|
+
if (this.isInequality()) {
|
|
5503
|
+
return this.field;
|
|
5504
|
+
}
|
|
5505
|
+
return null;
|
|
5375
5506
|
}
|
|
5376
|
-
return `Target(${str})`;
|
|
5377
5507
|
}
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5508
|
+
class CompositeFilter extends Filter {
|
|
5509
|
+
constructor(filters, op) {
|
|
5510
|
+
super();
|
|
5511
|
+
this.filters = filters;
|
|
5512
|
+
this.op = op;
|
|
5513
|
+
this.memoizedFlattenedFilters = null;
|
|
5381
5514
|
}
|
|
5382
|
-
|
|
5383
|
-
|
|
5515
|
+
/**
|
|
5516
|
+
* Creates a filter based on the provided arguments.
|
|
5517
|
+
*/
|
|
5518
|
+
static create(filters, op) {
|
|
5519
|
+
return new CompositeFilter(filters, op);
|
|
5384
5520
|
}
|
|
5385
|
-
|
|
5386
|
-
if (
|
|
5387
|
-
return false
|
|
5521
|
+
matches(doc) {
|
|
5522
|
+
if (compositeFilterIsConjunction(this)) {
|
|
5523
|
+
// For conjunctions, all filters must match, so return false if any filter doesn't match.
|
|
5524
|
+
return this.filters.find(filter => !filter.matches(doc)) === undefined;
|
|
5525
|
+
}
|
|
5526
|
+
else {
|
|
5527
|
+
// For disjunctions, at least one filter should match.
|
|
5528
|
+
return this.filters.find(filter => filter.matches(doc)) !== undefined;
|
|
5388
5529
|
}
|
|
5389
5530
|
}
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
for (let i = 0; i < left.filters.length; i++) {
|
|
5394
|
-
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
5395
|
-
return false;
|
|
5531
|
+
getFlattenedFilters() {
|
|
5532
|
+
if (this.memoizedFlattenedFilters !== null) {
|
|
5533
|
+
return this.memoizedFlattenedFilters;
|
|
5396
5534
|
}
|
|
5535
|
+
this.memoizedFlattenedFilters = this.filters.reduce((result, subfilter) => {
|
|
5536
|
+
return result.concat(subfilter.getFlattenedFilters());
|
|
5537
|
+
}, []);
|
|
5538
|
+
return this.memoizedFlattenedFilters;
|
|
5397
5539
|
}
|
|
5398
|
-
|
|
5399
|
-
|
|
5540
|
+
// Returns a mutable copy of `this.filters`
|
|
5541
|
+
getFilters() {
|
|
5542
|
+
return Object.assign([], this.filters);
|
|
5400
5543
|
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5544
|
+
getFirstInequalityField() {
|
|
5545
|
+
const found = this.findFirstMatchingFilter(filter => filter.isInequality());
|
|
5546
|
+
if (found !== null) {
|
|
5547
|
+
return found.field;
|
|
5548
|
+
}
|
|
5549
|
+
return null;
|
|
5403
5550
|
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5551
|
+
// Performs a depth-first search to find and return the first FieldFilter in the composite filter
|
|
5552
|
+
// that satisfies the predicate. Returns `null` if none of the FieldFilters satisfy the
|
|
5553
|
+
// predicate.
|
|
5554
|
+
findFirstMatchingFilter(predicate) {
|
|
5555
|
+
for (const fieldFilter of this.getFlattenedFilters()) {
|
|
5556
|
+
if (predicate(fieldFilter)) {
|
|
5557
|
+
return fieldFilter;
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
return null;
|
|
5406
5561
|
}
|
|
5407
|
-
return boundEquals(left.endAt, right.endAt);
|
|
5408
5562
|
}
|
|
5409
|
-
function
|
|
5410
|
-
return
|
|
5411
|
-
target.collectionGroup === null &&
|
|
5412
|
-
target.filters.length === 0);
|
|
5563
|
+
function compositeFilterIsConjunction(compositeFilter) {
|
|
5564
|
+
return compositeFilter.op === "and" /* CompositeOperator.AND */;
|
|
5413
5565
|
}
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
|
|
5566
|
+
function compositeFilterIsDisjunction(compositeFilter) {
|
|
5567
|
+
return compositeFilter.op === "or" /* CompositeOperator.OR */;
|
|
5417
5568
|
}
|
|
5418
5569
|
/**
|
|
5419
|
-
* Returns
|
|
5420
|
-
* filters. Returns `null` if there are no such filters.
|
|
5570
|
+
* Returns true if this filter is a conjunction of field filters only. Returns false otherwise.
|
|
5421
5571
|
*/
|
|
5422
|
-
function
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
return null;
|
|
5426
|
-
}
|
|
5427
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5428
|
-
switch (fieldFilter.op) {
|
|
5429
|
-
case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
|
|
5430
|
-
return fieldFilter.value.arrayValue.values || [];
|
|
5431
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
5432
|
-
return [fieldFilter.value];
|
|
5433
|
-
// Remaining filters are not array filters.
|
|
5434
|
-
}
|
|
5435
|
-
}
|
|
5436
|
-
return null;
|
|
5572
|
+
function compositeFilterIsFlatConjunction(compositeFilter) {
|
|
5573
|
+
return (compositeFilterIsFlat(compositeFilter) &&
|
|
5574
|
+
compositeFilterIsConjunction(compositeFilter));
|
|
5437
5575
|
}
|
|
5438
5576
|
/**
|
|
5439
|
-
* Returns
|
|
5440
|
-
* `null` if there are no such filters.
|
|
5577
|
+
* Returns true if this filter does not contain any composite filters. Returns false otherwise.
|
|
5441
5578
|
*/
|
|
5442
|
-
function
|
|
5443
|
-
const
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
switch (fieldFilter.op) {
|
|
5447
|
-
case "==" /* EQUAL */:
|
|
5448
|
-
case "in" /* IN */:
|
|
5449
|
-
// Encode equality prefix, which is encoded in the index value before
|
|
5450
|
-
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
|
|
5451
|
-
// `value != 'ab'`).
|
|
5452
|
-
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5453
|
-
break;
|
|
5454
|
-
case "not-in" /* NOT_IN */:
|
|
5455
|
-
case "!=" /* NOT_EQUAL */:
|
|
5456
|
-
// NotIn/NotEqual is always a suffix. There cannot be any remaining
|
|
5457
|
-
// segments and hence we can return early here.
|
|
5458
|
-
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5459
|
-
return Array.from(values.values());
|
|
5460
|
-
// Remaining filters cannot be used as notIn bounds.
|
|
5461
|
-
}
|
|
5579
|
+
function compositeFilterIsFlat(compositeFilter) {
|
|
5580
|
+
for (const filter of compositeFilter.filters) {
|
|
5581
|
+
if (filter instanceof CompositeFilter) {
|
|
5582
|
+
return false;
|
|
5462
5583
|
}
|
|
5463
5584
|
}
|
|
5464
|
-
return
|
|
5585
|
+
return true;
|
|
5465
5586
|
}
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5587
|
+
function canonifyFilter(filter) {
|
|
5588
|
+
if (filter instanceof FieldFilter) {
|
|
5589
|
+
// TODO(b/29183165): Technically, this won't be unique if two values have
|
|
5590
|
+
// the same description, such as the int 3 and the string "3". So we should
|
|
5591
|
+
// add the types in here somehow, too.
|
|
5592
|
+
return (filter.field.canonicalString() +
|
|
5593
|
+
filter.op.toString() +
|
|
5594
|
+
canonicalId(filter.value));
|
|
5595
|
+
}
|
|
5596
|
+
else if (compositeFilterIsFlatConjunction(filter)) {
|
|
5597
|
+
// Older SDK versions use an implicit AND operation between their filters.
|
|
5598
|
+
// In the new SDK versions, the developer may use an explicit AND filter.
|
|
5599
|
+
// To stay consistent with the old usages, we add a special case to ensure
|
|
5600
|
+
// the canonical ID for these two are the same. For example:
|
|
5601
|
+
// `col.whereEquals("a", 1).whereEquals("b", 2)` should have the same
|
|
5602
|
+
// canonical ID as `col.where(and(equals("a",1), equals("b",2)))`.
|
|
5603
|
+
return filter.filters.map(filter => canonifyFilter(filter)).join(',');
|
|
5482
5604
|
}
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
*/
|
|
5490
|
-
function targetGetUpperBound(target, fieldIndex) {
|
|
5491
|
-
const values = [];
|
|
5492
|
-
let inclusive = true;
|
|
5493
|
-
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
5494
|
-
// endAt.
|
|
5495
|
-
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5496
|
-
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
5497
|
-
? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
|
|
5498
|
-
: targetGetAscendingBound(target, segment.fieldPath, target.endAt);
|
|
5499
|
-
values.push(segmentBound.value);
|
|
5500
|
-
inclusive && (inclusive = segmentBound.inclusive);
|
|
5605
|
+
else {
|
|
5606
|
+
// filter instanceof CompositeFilter
|
|
5607
|
+
const canonicalIdsString = filter.filters
|
|
5608
|
+
.map(filter => canonifyFilter(filter))
|
|
5609
|
+
.join(',');
|
|
5610
|
+
return `${filter.op}(${canonicalIdsString})`;
|
|
5501
5611
|
}
|
|
5502
|
-
return new Bound(values, inclusive);
|
|
5503
5612
|
}
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
*/
|
|
5508
|
-
function targetGetAscendingBound(target, fieldPath, bound) {
|
|
5509
|
-
let value = MIN_VALUE;
|
|
5510
|
-
let inclusive = true;
|
|
5511
|
-
// Process all filters to find a value for the current field segment
|
|
5512
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
5513
|
-
let filterValue = MIN_VALUE;
|
|
5514
|
-
let filterInclusive = true;
|
|
5515
|
-
switch (fieldFilter.op) {
|
|
5516
|
-
case "<" /* LESS_THAN */:
|
|
5517
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5518
|
-
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
5519
|
-
break;
|
|
5520
|
-
case "==" /* EQUAL */:
|
|
5521
|
-
case "in" /* IN */:
|
|
5522
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5523
|
-
filterValue = fieldFilter.value;
|
|
5524
|
-
break;
|
|
5525
|
-
case ">" /* GREATER_THAN */:
|
|
5526
|
-
filterValue = fieldFilter.value;
|
|
5527
|
-
filterInclusive = false;
|
|
5528
|
-
break;
|
|
5529
|
-
case "!=" /* NOT_EQUAL */:
|
|
5530
|
-
case "not-in" /* NOT_IN */:
|
|
5531
|
-
filterValue = MIN_VALUE;
|
|
5532
|
-
break;
|
|
5533
|
-
// Remaining filters cannot be used as lower bounds.
|
|
5534
|
-
}
|
|
5535
|
-
if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
|
|
5536
|
-
value = filterValue;
|
|
5537
|
-
inclusive = filterInclusive;
|
|
5538
|
-
}
|
|
5539
|
-
}
|
|
5540
|
-
// If there is an additional bound, compare the values against the existing
|
|
5541
|
-
// range to see if we can narrow the scope.
|
|
5542
|
-
if (bound !== null) {
|
|
5543
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
5544
|
-
const orderBy = target.orderBy[i];
|
|
5545
|
-
if (orderBy.field.isEqual(fieldPath)) {
|
|
5546
|
-
const cursorValue = bound.position[i];
|
|
5547
|
-
if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
|
|
5548
|
-
value = cursorValue;
|
|
5549
|
-
inclusive = bound.inclusive;
|
|
5550
|
-
}
|
|
5551
|
-
break;
|
|
5552
|
-
}
|
|
5553
|
-
}
|
|
5613
|
+
function filterEquals(f1, f2) {
|
|
5614
|
+
if (f1 instanceof FieldFilter) {
|
|
5615
|
+
return fieldFilterEquals(f1, f2);
|
|
5554
5616
|
}
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
/**
|
|
5558
|
-
* Returns the value to use as the upper bound for ascending index segment at
|
|
5559
|
-
* the provided `fieldPath` (or the lower bound for a descending segment).
|
|
5560
|
-
*/
|
|
5561
|
-
function targetGetDescendingBound(target, fieldPath, bound) {
|
|
5562
|
-
let value = MAX_VALUE;
|
|
5563
|
-
let inclusive = true;
|
|
5564
|
-
// Process all filters to find a value for the current field segment
|
|
5565
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
5566
|
-
let filterValue = MAX_VALUE;
|
|
5567
|
-
let filterInclusive = true;
|
|
5568
|
-
switch (fieldFilter.op) {
|
|
5569
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5570
|
-
case ">" /* GREATER_THAN */:
|
|
5571
|
-
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
5572
|
-
filterInclusive = false;
|
|
5573
|
-
break;
|
|
5574
|
-
case "==" /* EQUAL */:
|
|
5575
|
-
case "in" /* IN */:
|
|
5576
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5577
|
-
filterValue = fieldFilter.value;
|
|
5578
|
-
break;
|
|
5579
|
-
case "<" /* LESS_THAN */:
|
|
5580
|
-
filterValue = fieldFilter.value;
|
|
5581
|
-
filterInclusive = false;
|
|
5582
|
-
break;
|
|
5583
|
-
case "!=" /* NOT_EQUAL */:
|
|
5584
|
-
case "not-in" /* NOT_IN */:
|
|
5585
|
-
filterValue = MAX_VALUE;
|
|
5586
|
-
break;
|
|
5587
|
-
// Remaining filters cannot be used as upper bounds.
|
|
5588
|
-
}
|
|
5589
|
-
if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
|
|
5590
|
-
value = filterValue;
|
|
5591
|
-
inclusive = filterInclusive;
|
|
5592
|
-
}
|
|
5617
|
+
else if (f1 instanceof CompositeFilter) {
|
|
5618
|
+
return compositeFilterEquals(f1, f2);
|
|
5593
5619
|
}
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
if (bound !== null) {
|
|
5597
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
5598
|
-
const orderBy = target.orderBy[i];
|
|
5599
|
-
if (orderBy.field.isEqual(fieldPath)) {
|
|
5600
|
-
const cursorValue = bound.position[i];
|
|
5601
|
-
if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
|
|
5602
|
-
value = cursorValue;
|
|
5603
|
-
inclusive = bound.inclusive;
|
|
5604
|
-
}
|
|
5605
|
-
break;
|
|
5606
|
-
}
|
|
5607
|
-
}
|
|
5620
|
+
else {
|
|
5621
|
+
fail();
|
|
5608
5622
|
}
|
|
5609
|
-
return { value, inclusive };
|
|
5610
5623
|
}
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
// ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
|
|
5624
|
-
// For instance, it is possible to have an index for "a ARRAY a ASC". Even
|
|
5625
|
-
// though these are on the same field, they should be counted as two
|
|
5626
|
-
// separate segments in an index.
|
|
5627
|
-
if (fieldFilter.op === "array-contains" /* ARRAY_CONTAINS */ ||
|
|
5628
|
-
fieldFilter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
5629
|
-
hasArraySegment = true;
|
|
5630
|
-
}
|
|
5631
|
-
else {
|
|
5632
|
-
fields = fields.add(fieldFilter.field);
|
|
5633
|
-
}
|
|
5634
|
-
}
|
|
5635
|
-
for (const orderBy of target.orderBy) {
|
|
5636
|
-
// __name__ is not an explicit segment of any index, so we don't need to
|
|
5637
|
-
// count it.
|
|
5638
|
-
if (!orderBy.field.isKeyField()) {
|
|
5639
|
-
fields = fields.add(orderBy.field);
|
|
5640
|
-
}
|
|
5624
|
+
function fieldFilterEquals(f1, f2) {
|
|
5625
|
+
return (f2 instanceof FieldFilter &&
|
|
5626
|
+
f1.op === f2.op &&
|
|
5627
|
+
f1.field.isEqual(f2.field) &&
|
|
5628
|
+
valueEquals(f1.value, f2.value));
|
|
5629
|
+
}
|
|
5630
|
+
function compositeFilterEquals(f1, f2) {
|
|
5631
|
+
if (f2 instanceof CompositeFilter &&
|
|
5632
|
+
f1.op === f2.op &&
|
|
5633
|
+
f1.filters.length === f2.filters.length) {
|
|
5634
|
+
const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
|
|
5635
|
+
return subFiltersMatch;
|
|
5641
5636
|
}
|
|
5642
|
-
return
|
|
5637
|
+
return false;
|
|
5643
5638
|
}
|
|
5644
|
-
|
|
5639
|
+
/**
|
|
5640
|
+
* Returns a new composite filter that contains all filter from
|
|
5641
|
+
* `compositeFilter` plus all the given filters in `otherFilters`.
|
|
5642
|
+
*/
|
|
5643
|
+
function compositeFilterWithAddedFilters(compositeFilter, otherFilters) {
|
|
5644
|
+
const mergedFilters = compositeFilter.filters.concat(otherFilters);
|
|
5645
|
+
return CompositeFilter.create(mergedFilters, compositeFilter.op);
|
|
5645
5646
|
}
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
this.op = op;
|
|
5651
|
-
this.value = value;
|
|
5652
|
-
}
|
|
5653
|
-
/**
|
|
5654
|
-
* Creates a filter based on the provided arguments.
|
|
5655
|
-
*/
|
|
5656
|
-
static create(field, op, value) {
|
|
5657
|
-
if (field.isKeyField()) {
|
|
5658
|
-
if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
5659
|
-
return this.createKeyFieldInFilter(field, op, value);
|
|
5660
|
-
}
|
|
5661
|
-
else {
|
|
5662
|
-
return new KeyFieldFilter(field, op, value);
|
|
5663
|
-
}
|
|
5664
|
-
}
|
|
5665
|
-
else if (op === "array-contains" /* ARRAY_CONTAINS */) {
|
|
5666
|
-
return new ArrayContainsFilter(field, value);
|
|
5667
|
-
}
|
|
5668
|
-
else if (op === "in" /* IN */) {
|
|
5669
|
-
return new InFilter(field, value);
|
|
5670
|
-
}
|
|
5671
|
-
else if (op === "not-in" /* NOT_IN */) {
|
|
5672
|
-
return new NotInFilter(field, value);
|
|
5673
|
-
}
|
|
5674
|
-
else if (op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
5675
|
-
return new ArrayContainsAnyFilter(field, value);
|
|
5676
|
-
}
|
|
5677
|
-
else {
|
|
5678
|
-
return new FieldFilter(field, op, value);
|
|
5679
|
-
}
|
|
5680
|
-
}
|
|
5681
|
-
static createKeyFieldInFilter(field, op, value) {
|
|
5682
|
-
return op === "in" /* IN */
|
|
5683
|
-
? new KeyFieldInFilter(field, value)
|
|
5684
|
-
: new KeyFieldNotInFilter(field, value);
|
|
5685
|
-
}
|
|
5686
|
-
matches(doc) {
|
|
5687
|
-
const other = doc.data.field(this.field);
|
|
5688
|
-
// Types do not have to match in NOT_EQUAL filters.
|
|
5689
|
-
if (this.op === "!=" /* NOT_EQUAL */) {
|
|
5690
|
-
return (other !== null &&
|
|
5691
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
5692
|
-
}
|
|
5693
|
-
// Only compare types with matching backend order (such as double and int).
|
|
5694
|
-
return (other !== null &&
|
|
5695
|
-
typeOrder(this.value) === typeOrder(other) &&
|
|
5696
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
5647
|
+
/** Returns a debug description for `filter`. */
|
|
5648
|
+
function stringifyFilter(filter) {
|
|
5649
|
+
if (filter instanceof FieldFilter) {
|
|
5650
|
+
return stringifyFieldFilter(filter);
|
|
5697
5651
|
}
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
case "<" /* LESS_THAN */:
|
|
5701
|
-
return comparison < 0;
|
|
5702
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5703
|
-
return comparison <= 0;
|
|
5704
|
-
case "==" /* EQUAL */:
|
|
5705
|
-
return comparison === 0;
|
|
5706
|
-
case "!=" /* NOT_EQUAL */:
|
|
5707
|
-
return comparison !== 0;
|
|
5708
|
-
case ">" /* GREATER_THAN */:
|
|
5709
|
-
return comparison > 0;
|
|
5710
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5711
|
-
return comparison >= 0;
|
|
5712
|
-
default:
|
|
5713
|
-
return fail();
|
|
5714
|
-
}
|
|
5652
|
+
else if (filter instanceof CompositeFilter) {
|
|
5653
|
+
return stringifyCompositeFilter(filter);
|
|
5715
5654
|
}
|
|
5716
|
-
|
|
5717
|
-
return
|
|
5718
|
-
"<" /* LESS_THAN */,
|
|
5719
|
-
"<=" /* LESS_THAN_OR_EQUAL */,
|
|
5720
|
-
">" /* GREATER_THAN */,
|
|
5721
|
-
">=" /* GREATER_THAN_OR_EQUAL */,
|
|
5722
|
-
"!=" /* NOT_EQUAL */,
|
|
5723
|
-
"not-in" /* NOT_IN */
|
|
5724
|
-
].indexOf(this.op) >= 0);
|
|
5655
|
+
else {
|
|
5656
|
+
return 'Filter';
|
|
5725
5657
|
}
|
|
5726
5658
|
}
|
|
5727
|
-
function
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
filter.op.toString() +
|
|
5733
|
-
canonicalId(filter.value));
|
|
5734
|
-
}
|
|
5735
|
-
function filterEquals(f1, f2) {
|
|
5736
|
-
return (f1.op === f2.op &&
|
|
5737
|
-
f1.field.isEqual(f2.field) &&
|
|
5738
|
-
valueEquals(f1.value, f2.value));
|
|
5659
|
+
function stringifyCompositeFilter(filter) {
|
|
5660
|
+
return (filter.op.toString() +
|
|
5661
|
+
` {` +
|
|
5662
|
+
filter.getFilters().map(stringifyFilter).join(' ,') +
|
|
5663
|
+
'}');
|
|
5739
5664
|
}
|
|
5740
|
-
|
|
5741
|
-
function stringifyFilter(filter) {
|
|
5665
|
+
function stringifyFieldFilter(filter) {
|
|
5742
5666
|
return `${filter.field.canonicalString()} ${filter.op} ${canonicalId(filter.value)}`;
|
|
5743
5667
|
}
|
|
5744
5668
|
/** Filter that matches on key fields (i.e. '__name__'). */
|
|
@@ -5755,8 +5679,8 @@ class KeyFieldFilter extends FieldFilter {
|
|
|
5755
5679
|
/** Filter that matches on key fields within an array. */
|
|
5756
5680
|
class KeyFieldInFilter extends FieldFilter {
|
|
5757
5681
|
constructor(field, value) {
|
|
5758
|
-
super(field, "in" /* IN */, value);
|
|
5759
|
-
this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
|
|
5682
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5683
|
+
this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
|
|
5760
5684
|
}
|
|
5761
5685
|
matches(doc) {
|
|
5762
5686
|
return this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5765,8 +5689,8 @@ class KeyFieldInFilter extends FieldFilter {
|
|
|
5765
5689
|
/** Filter that matches on key fields not present within an array. */
|
|
5766
5690
|
class KeyFieldNotInFilter extends FieldFilter {
|
|
5767
5691
|
constructor(field, value) {
|
|
5768
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5769
|
-
this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
|
|
5692
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5693
|
+
this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
|
|
5770
5694
|
}
|
|
5771
5695
|
matches(doc) {
|
|
5772
5696
|
return !this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5781,7 +5705,7 @@ function extractDocumentKeysFromArrayValue(op, value) {
|
|
|
5781
5705
|
/** A Filter that implements the array-contains operator. */
|
|
5782
5706
|
class ArrayContainsFilter extends FieldFilter {
|
|
5783
5707
|
constructor(field, value) {
|
|
5784
|
-
super(field, "array-contains" /* ARRAY_CONTAINS */, value);
|
|
5708
|
+
super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
|
|
5785
5709
|
}
|
|
5786
5710
|
matches(doc) {
|
|
5787
5711
|
const other = doc.data.field(this.field);
|
|
@@ -5791,7 +5715,7 @@ class ArrayContainsFilter extends FieldFilter {
|
|
|
5791
5715
|
/** A Filter that implements the IN operator. */
|
|
5792
5716
|
class InFilter extends FieldFilter {
|
|
5793
5717
|
constructor(field, value) {
|
|
5794
|
-
super(field, "in" /* IN */, value);
|
|
5718
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5795
5719
|
}
|
|
5796
5720
|
matches(doc) {
|
|
5797
5721
|
const other = doc.data.field(this.field);
|
|
@@ -5801,7 +5725,7 @@ class InFilter extends FieldFilter {
|
|
|
5801
5725
|
/** A Filter that implements the not-in operator. */
|
|
5802
5726
|
class NotInFilter extends FieldFilter {
|
|
5803
5727
|
constructor(field, value) {
|
|
5804
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5728
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5805
5729
|
}
|
|
5806
5730
|
matches(doc) {
|
|
5807
5731
|
if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
|
|
@@ -5814,7 +5738,7 @@ class NotInFilter extends FieldFilter {
|
|
|
5814
5738
|
/** A Filter that implements the array-contains-any operator. */
|
|
5815
5739
|
class ArrayContainsAnyFilter extends FieldFilter {
|
|
5816
5740
|
constructor(field, value) {
|
|
5817
|
-
super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
|
|
5741
|
+
super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
|
|
5818
5742
|
}
|
|
5819
5743
|
matches(doc) {
|
|
5820
5744
|
const other = doc.data.field(this.field);
|
|
@@ -5823,32 +5747,29 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
5823
5747
|
}
|
|
5824
5748
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
5825
5749
|
}
|
|
5826
|
-
}
|
|
5827
|
-
|
|
5828
|
-
|
|
5750
|
+
}
|
|
5751
|
+
|
|
5752
|
+
/**
|
|
5753
|
+
* @license
|
|
5754
|
+
* Copyright 2022 Google LLC
|
|
5829
5755
|
*
|
|
5830
|
-
*
|
|
5831
|
-
*
|
|
5832
|
-
*
|
|
5756
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5757
|
+
* you may not use this file except in compliance with the License.
|
|
5758
|
+
* You may obtain a copy of the License at
|
|
5833
5759
|
*
|
|
5834
|
-
*
|
|
5835
|
-
* of values for the (potentially implicit) order by clauses of a query.
|
|
5760
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5836
5761
|
*
|
|
5837
|
-
*
|
|
5838
|
-
*
|
|
5839
|
-
*
|
|
5762
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5763
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5764
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5765
|
+
* See the License for the specific language governing permissions and
|
|
5766
|
+
* limitations under the License.
|
|
5840
5767
|
*/
|
|
5841
|
-
class Bound {
|
|
5842
|
-
constructor(position, inclusive) {
|
|
5843
|
-
this.position = position;
|
|
5844
|
-
this.inclusive = inclusive;
|
|
5845
|
-
}
|
|
5846
|
-
}
|
|
5847
5768
|
/**
|
|
5848
5769
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
5849
5770
|
*/
|
|
5850
5771
|
class OrderBy {
|
|
5851
|
-
constructor(field, dir = "asc" /* ASCENDING */) {
|
|
5772
|
+
constructor(field, dir = "asc" /* Direction.ASCENDING */) {
|
|
5852
5773
|
this.field = field;
|
|
5853
5774
|
this.dir = dir;
|
|
5854
5775
|
}
|
|
@@ -5862,63 +5783,375 @@ function stringifyOrderBy(orderBy) {
|
|
|
5862
5783
|
}
|
|
5863
5784
|
function orderByEquals(left, right) {
|
|
5864
5785
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
5786
|
+
}
|
|
5787
|
+
|
|
5788
|
+
/**
|
|
5789
|
+
* @license
|
|
5790
|
+
* Copyright 2019 Google LLC
|
|
5791
|
+
*
|
|
5792
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5793
|
+
* you may not use this file except in compliance with the License.
|
|
5794
|
+
* You may obtain a copy of the License at
|
|
5795
|
+
*
|
|
5796
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5797
|
+
*
|
|
5798
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5799
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5800
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5801
|
+
* See the License for the specific language governing permissions and
|
|
5802
|
+
* limitations under the License.
|
|
5803
|
+
*/
|
|
5804
|
+
// Visible for testing
|
|
5805
|
+
class TargetImpl {
|
|
5806
|
+
constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5807
|
+
this.path = path;
|
|
5808
|
+
this.collectionGroup = collectionGroup;
|
|
5809
|
+
this.orderBy = orderBy;
|
|
5810
|
+
this.filters = filters;
|
|
5811
|
+
this.limit = limit;
|
|
5812
|
+
this.startAt = startAt;
|
|
5813
|
+
this.endAt = endAt;
|
|
5814
|
+
this.memoizedCanonicalId = null;
|
|
5815
|
+
}
|
|
5865
5816
|
}
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5817
|
+
/**
|
|
5818
|
+
* Initializes a Target with a path and optional additional query constraints.
|
|
5819
|
+
* Path must currently be empty if this is a collection group query.
|
|
5820
|
+
*
|
|
5821
|
+
* NOTE: you should always construct `Target` from `Query.toTarget` instead of
|
|
5822
|
+
* using this factory method, because `Query` provides an implicit `orderBy`
|
|
5823
|
+
* property.
|
|
5824
|
+
*/
|
|
5825
|
+
function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5826
|
+
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
5827
|
+
}
|
|
5828
|
+
function canonifyTarget(target) {
|
|
5829
|
+
const targetImpl = debugCast(target);
|
|
5830
|
+
if (targetImpl.memoizedCanonicalId === null) {
|
|
5831
|
+
let str = targetImpl.path.canonicalString();
|
|
5832
|
+
if (targetImpl.collectionGroup !== null) {
|
|
5833
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
5873
5834
|
}
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5835
|
+
str += '|f:';
|
|
5836
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
5837
|
+
str += '|ob:';
|
|
5838
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
5839
|
+
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
5840
|
+
str += '|l:';
|
|
5841
|
+
str += targetImpl.limit;
|
|
5877
5842
|
}
|
|
5878
|
-
if (
|
|
5879
|
-
|
|
5843
|
+
if (targetImpl.startAt) {
|
|
5844
|
+
str += '|lb:';
|
|
5845
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
5846
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5880
5847
|
}
|
|
5881
|
-
if (
|
|
5882
|
-
|
|
5848
|
+
if (targetImpl.endAt) {
|
|
5849
|
+
str += '|ub:';
|
|
5850
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
5851
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5883
5852
|
}
|
|
5853
|
+
targetImpl.memoizedCanonicalId = str;
|
|
5884
5854
|
}
|
|
5885
|
-
return
|
|
5855
|
+
return targetImpl.memoizedCanonicalId;
|
|
5856
|
+
}
|
|
5857
|
+
function stringifyTarget(target) {
|
|
5858
|
+
let str = target.path.canonicalString();
|
|
5859
|
+
if (target.collectionGroup !== null) {
|
|
5860
|
+
str += ' collectionGroup=' + target.collectionGroup;
|
|
5861
|
+
}
|
|
5862
|
+
if (target.filters.length > 0) {
|
|
5863
|
+
str += `, filters: [${target.filters
|
|
5864
|
+
.map(f => stringifyFilter(f))
|
|
5865
|
+
.join(', ')}]`;
|
|
5866
|
+
}
|
|
5867
|
+
if (!isNullOrUndefined(target.limit)) {
|
|
5868
|
+
str += ', limit: ' + target.limit;
|
|
5869
|
+
}
|
|
5870
|
+
if (target.orderBy.length > 0) {
|
|
5871
|
+
str += `, orderBy: [${target.orderBy
|
|
5872
|
+
.map(o => stringifyOrderBy(o))
|
|
5873
|
+
.join(', ')}]`;
|
|
5874
|
+
}
|
|
5875
|
+
if (target.startAt) {
|
|
5876
|
+
str += ', startAt: ';
|
|
5877
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
5878
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5879
|
+
}
|
|
5880
|
+
if (target.endAt) {
|
|
5881
|
+
str += ', endAt: ';
|
|
5882
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
5883
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5884
|
+
}
|
|
5885
|
+
return `Target(${str})`;
|
|
5886
|
+
}
|
|
5887
|
+
function targetEquals(left, right) {
|
|
5888
|
+
if (left.limit !== right.limit) {
|
|
5889
|
+
return false;
|
|
5890
|
+
}
|
|
5891
|
+
if (left.orderBy.length !== right.orderBy.length) {
|
|
5892
|
+
return false;
|
|
5893
|
+
}
|
|
5894
|
+
for (let i = 0; i < left.orderBy.length; i++) {
|
|
5895
|
+
if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
|
|
5896
|
+
return false;
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
if (left.filters.length !== right.filters.length) {
|
|
5900
|
+
return false;
|
|
5901
|
+
}
|
|
5902
|
+
for (let i = 0; i < left.filters.length; i++) {
|
|
5903
|
+
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
5904
|
+
return false;
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
if (left.collectionGroup !== right.collectionGroup) {
|
|
5908
|
+
return false;
|
|
5909
|
+
}
|
|
5910
|
+
if (!left.path.isEqual(right.path)) {
|
|
5911
|
+
return false;
|
|
5912
|
+
}
|
|
5913
|
+
if (!boundEquals(left.startAt, right.startAt)) {
|
|
5914
|
+
return false;
|
|
5915
|
+
}
|
|
5916
|
+
return boundEquals(left.endAt, right.endAt);
|
|
5917
|
+
}
|
|
5918
|
+
function targetIsDocumentTarget(target) {
|
|
5919
|
+
return (DocumentKey.isDocumentKey(target.path) &&
|
|
5920
|
+
target.collectionGroup === null &&
|
|
5921
|
+
target.filters.length === 0);
|
|
5922
|
+
}
|
|
5923
|
+
/** Returns the field filters that target the given field path. */
|
|
5924
|
+
function targetGetFieldFiltersForPath(target, path) {
|
|
5925
|
+
return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
|
|
5886
5926
|
}
|
|
5887
5927
|
/**
|
|
5888
|
-
* Returns
|
|
5889
|
-
*
|
|
5928
|
+
* Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
|
|
5929
|
+
* filters. Returns `null` if there are no such filters.
|
|
5890
5930
|
*/
|
|
5891
|
-
function
|
|
5892
|
-
const
|
|
5893
|
-
|
|
5931
|
+
function targetGetArrayValues(target, fieldIndex) {
|
|
5932
|
+
const segment = fieldIndexGetArraySegment(fieldIndex);
|
|
5933
|
+
if (segment === undefined) {
|
|
5934
|
+
return null;
|
|
5935
|
+
}
|
|
5936
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5937
|
+
switch (fieldFilter.op) {
|
|
5938
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
5939
|
+
return fieldFilter.value.arrayValue.values || [];
|
|
5940
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
5941
|
+
return [fieldFilter.value];
|
|
5942
|
+
// Remaining filters are not array filters.
|
|
5943
|
+
}
|
|
5944
|
+
}
|
|
5945
|
+
return null;
|
|
5894
5946
|
}
|
|
5895
5947
|
/**
|
|
5896
|
-
* Returns
|
|
5897
|
-
*
|
|
5948
|
+
* Returns the list of values that are used in != or NOT_IN filters. Returns
|
|
5949
|
+
* `null` if there are no such filters.
|
|
5898
5950
|
*/
|
|
5899
|
-
function
|
|
5900
|
-
const
|
|
5901
|
-
|
|
5951
|
+
function targetGetNotInValues(target, fieldIndex) {
|
|
5952
|
+
const values = new Map();
|
|
5953
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5954
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5955
|
+
switch (fieldFilter.op) {
|
|
5956
|
+
case "==" /* Operator.EQUAL */:
|
|
5957
|
+
case "in" /* Operator.IN */:
|
|
5958
|
+
// Encode equality prefix, which is encoded in the index value before
|
|
5959
|
+
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
|
|
5960
|
+
// `value != 'ab'`).
|
|
5961
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5962
|
+
break;
|
|
5963
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
5964
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
5965
|
+
// NotIn/NotEqual is always a suffix. There cannot be any remaining
|
|
5966
|
+
// segments and hence we can return early here.
|
|
5967
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5968
|
+
return Array.from(values.values());
|
|
5969
|
+
// Remaining filters cannot be used as notIn bounds.
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
}
|
|
5973
|
+
return null;
|
|
5902
5974
|
}
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5975
|
+
/**
|
|
5976
|
+
* Returns a lower bound of field values that can be used as a starting point to
|
|
5977
|
+
* scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
|
|
5978
|
+
* exists.
|
|
5979
|
+
*/
|
|
5980
|
+
function targetGetLowerBound(target, fieldIndex) {
|
|
5981
|
+
const values = [];
|
|
5982
|
+
let inclusive = true;
|
|
5983
|
+
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
5984
|
+
// startAt.
|
|
5985
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5986
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
5987
|
+
? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
|
|
5988
|
+
: targetGetDescendingBound(target, segment.fieldPath, target.startAt);
|
|
5989
|
+
values.push(segmentBound.value);
|
|
5990
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
5906
5991
|
}
|
|
5907
|
-
|
|
5908
|
-
|
|
5992
|
+
return new Bound(values, inclusive);
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5995
|
+
* Returns an upper bound of field values that can be used as an ending point
|
|
5996
|
+
* when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
|
|
5997
|
+
* upper bound exists.
|
|
5998
|
+
*/
|
|
5999
|
+
function targetGetUpperBound(target, fieldIndex) {
|
|
6000
|
+
const values = [];
|
|
6001
|
+
let inclusive = true;
|
|
6002
|
+
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
6003
|
+
// endAt.
|
|
6004
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
6005
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
6006
|
+
? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
|
|
6007
|
+
: targetGetAscendingBound(target, segment.fieldPath, target.endAt);
|
|
6008
|
+
values.push(segmentBound.value);
|
|
6009
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
6010
|
+
}
|
|
6011
|
+
return new Bound(values, inclusive);
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Returns the value to use as the lower bound for ascending index segment at
|
|
6015
|
+
* the provided `fieldPath` (or the upper bound for an descending segment).
|
|
6016
|
+
*/
|
|
6017
|
+
function targetGetAscendingBound(target, fieldPath, bound) {
|
|
6018
|
+
let value = MIN_VALUE;
|
|
6019
|
+
let inclusive = true;
|
|
6020
|
+
// Process all filters to find a value for the current field segment
|
|
6021
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6022
|
+
let filterValue = MIN_VALUE;
|
|
6023
|
+
let filterInclusive = true;
|
|
6024
|
+
switch (fieldFilter.op) {
|
|
6025
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6026
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6027
|
+
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
6028
|
+
break;
|
|
6029
|
+
case "==" /* Operator.EQUAL */:
|
|
6030
|
+
case "in" /* Operator.IN */:
|
|
6031
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6032
|
+
filterValue = fieldFilter.value;
|
|
6033
|
+
break;
|
|
6034
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6035
|
+
filterValue = fieldFilter.value;
|
|
6036
|
+
filterInclusive = false;
|
|
6037
|
+
break;
|
|
6038
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6039
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6040
|
+
filterValue = MIN_VALUE;
|
|
6041
|
+
break;
|
|
6042
|
+
// Remaining filters cannot be used as lower bounds.
|
|
6043
|
+
}
|
|
6044
|
+
if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
|
|
6045
|
+
value = filterValue;
|
|
6046
|
+
inclusive = filterInclusive;
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
// If there is an additional bound, compare the values against the existing
|
|
6050
|
+
// range to see if we can narrow the scope.
|
|
6051
|
+
if (bound !== null) {
|
|
6052
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6053
|
+
const orderBy = target.orderBy[i];
|
|
6054
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6055
|
+
const cursorValue = bound.position[i];
|
|
6056
|
+
if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
|
|
6057
|
+
value = cursorValue;
|
|
6058
|
+
inclusive = bound.inclusive;
|
|
6059
|
+
}
|
|
6060
|
+
break;
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
}
|
|
6064
|
+
return { value, inclusive };
|
|
6065
|
+
}
|
|
6066
|
+
/**
|
|
6067
|
+
* Returns the value to use as the upper bound for ascending index segment at
|
|
6068
|
+
* the provided `fieldPath` (or the lower bound for a descending segment).
|
|
6069
|
+
*/
|
|
6070
|
+
function targetGetDescendingBound(target, fieldPath, bound) {
|
|
6071
|
+
let value = MAX_VALUE;
|
|
6072
|
+
let inclusive = true;
|
|
6073
|
+
// Process all filters to find a value for the current field segment
|
|
6074
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6075
|
+
let filterValue = MAX_VALUE;
|
|
6076
|
+
let filterInclusive = true;
|
|
6077
|
+
switch (fieldFilter.op) {
|
|
6078
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6079
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6080
|
+
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
6081
|
+
filterInclusive = false;
|
|
6082
|
+
break;
|
|
6083
|
+
case "==" /* Operator.EQUAL */:
|
|
6084
|
+
case "in" /* Operator.IN */:
|
|
6085
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6086
|
+
filterValue = fieldFilter.value;
|
|
6087
|
+
break;
|
|
6088
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6089
|
+
filterValue = fieldFilter.value;
|
|
6090
|
+
filterInclusive = false;
|
|
6091
|
+
break;
|
|
6092
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6093
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6094
|
+
filterValue = MAX_VALUE;
|
|
6095
|
+
break;
|
|
6096
|
+
// Remaining filters cannot be used as upper bounds.
|
|
6097
|
+
}
|
|
6098
|
+
if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
|
|
6099
|
+
value = filterValue;
|
|
6100
|
+
inclusive = filterInclusive;
|
|
6101
|
+
}
|
|
6102
|
+
}
|
|
6103
|
+
// If there is an additional bound, compare the values against the existing
|
|
6104
|
+
// range to see if we can narrow the scope.
|
|
6105
|
+
if (bound !== null) {
|
|
6106
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6107
|
+
const orderBy = target.orderBy[i];
|
|
6108
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6109
|
+
const cursorValue = bound.position[i];
|
|
6110
|
+
if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
|
|
6111
|
+
value = cursorValue;
|
|
6112
|
+
inclusive = bound.inclusive;
|
|
6113
|
+
}
|
|
6114
|
+
break;
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
5909
6117
|
}
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
6118
|
+
return { value, inclusive };
|
|
6119
|
+
}
|
|
6120
|
+
/** Returns the number of segments of a perfect index for this target. */
|
|
6121
|
+
function targetGetSegmentCount(target) {
|
|
6122
|
+
let fields = new SortedSet(FieldPath$1.comparator);
|
|
6123
|
+
let hasArraySegment = false;
|
|
6124
|
+
for (const filter of target.filters) {
|
|
6125
|
+
for (const subFilter of filter.getFlattenedFilters()) {
|
|
6126
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6127
|
+
// count it.
|
|
6128
|
+
if (subFilter.field.isKeyField()) {
|
|
6129
|
+
continue;
|
|
6130
|
+
}
|
|
6131
|
+
// ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
|
|
6132
|
+
// For instance, it is possible to have an index for "a ARRAY a ASC". Even
|
|
6133
|
+
// though these are on the same field, they should be counted as two
|
|
6134
|
+
// separate segments in an index.
|
|
6135
|
+
if (subFilter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
6136
|
+
subFilter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
6137
|
+
hasArraySegment = true;
|
|
6138
|
+
}
|
|
6139
|
+
else {
|
|
6140
|
+
fields = fields.add(subFilter.field);
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
5913
6143
|
}
|
|
5914
|
-
for (
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
if (!
|
|
5918
|
-
|
|
6144
|
+
for (const orderBy of target.orderBy) {
|
|
6145
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6146
|
+
// count it.
|
|
6147
|
+
if (!orderBy.field.isKeyField()) {
|
|
6148
|
+
fields = fields.add(orderBy.field);
|
|
5919
6149
|
}
|
|
5920
6150
|
}
|
|
5921
|
-
return
|
|
6151
|
+
return fields.size + (hasArraySegment ? 1 : 0);
|
|
6152
|
+
}
|
|
6153
|
+
function targetHasLimit(target) {
|
|
6154
|
+
return target.limit !== null;
|
|
5922
6155
|
}
|
|
5923
6156
|
|
|
5924
6157
|
/**
|
|
@@ -5949,7 +6182,7 @@ class QueryImpl {
|
|
|
5949
6182
|
* Initializes a Query with a path and optional additional query constraints.
|
|
5950
6183
|
* Path must currently be empty if this is a collection group query.
|
|
5951
6184
|
*/
|
|
5952
|
-
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
|
|
6185
|
+
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
|
|
5953
6186
|
this.path = path;
|
|
5954
6187
|
this.collectionGroup = collectionGroup;
|
|
5955
6188
|
this.explicitOrderBy = explicitOrderBy;
|
|
@@ -6003,20 +6236,9 @@ function getFirstOrderByField(query) {
|
|
|
6003
6236
|
}
|
|
6004
6237
|
function getInequalityFilterField(query) {
|
|
6005
6238
|
for (const filter of query.filters) {
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
}
|
|
6010
|
-
return null;
|
|
6011
|
-
}
|
|
6012
|
-
/**
|
|
6013
|
-
* Checks if any of the provided Operators are included in the query and
|
|
6014
|
-
* returns the first one that is, or null if none are.
|
|
6015
|
-
*/
|
|
6016
|
-
function findFilterOperator(query, operators) {
|
|
6017
|
-
for (const filter of query.filters) {
|
|
6018
|
-
if (operators.indexOf(filter.op) >= 0) {
|
|
6019
|
-
return filter.op;
|
|
6239
|
+
const result = filter.getFirstInequalityField();
|
|
6240
|
+
if (result !== null) {
|
|
6241
|
+
return result;
|
|
6020
6242
|
}
|
|
6021
6243
|
}
|
|
6022
6244
|
return null;
|
|
@@ -6062,7 +6284,7 @@ function queryOrderBy(query) {
|
|
|
6062
6284
|
if (!inequalityField.isKeyField()) {
|
|
6063
6285
|
queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
|
|
6064
6286
|
}
|
|
6065
|
-
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
|
|
6287
|
+
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
|
|
6066
6288
|
}
|
|
6067
6289
|
else {
|
|
6068
6290
|
let foundKeyOrdering = false;
|
|
@@ -6078,7 +6300,7 @@ function queryOrderBy(query) {
|
|
|
6078
6300
|
const lastDirection = queryImpl.explicitOrderBy.length > 0
|
|
6079
6301
|
? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
|
|
6080
6302
|
.dir
|
|
6081
|
-
: "asc" /* ASCENDING */;
|
|
6303
|
+
: "asc" /* Direction.ASCENDING */;
|
|
6082
6304
|
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
|
|
6083
6305
|
}
|
|
6084
6306
|
}
|
|
@@ -6091,16 +6313,16 @@ function queryOrderBy(query) {
|
|
|
6091
6313
|
function queryToTarget(query) {
|
|
6092
6314
|
const queryImpl = debugCast(query);
|
|
6093
6315
|
if (!queryImpl.memoizedTarget) {
|
|
6094
|
-
if (queryImpl.limitType === "F" /* First */) {
|
|
6316
|
+
if (queryImpl.limitType === "F" /* LimitType.First */) {
|
|
6095
6317
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
|
|
6096
6318
|
}
|
|
6097
6319
|
else {
|
|
6098
6320
|
// Flip the orderBy directions since we want the last results
|
|
6099
6321
|
const orderBys = [];
|
|
6100
6322
|
for (const orderBy of queryOrderBy(queryImpl)) {
|
|
6101
|
-
const dir = orderBy.dir === "desc" /* DESCENDING */
|
|
6102
|
-
? "asc" /* ASCENDING */
|
|
6103
|
-
: "desc" /* DESCENDING */;
|
|
6323
|
+
const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
|
|
6324
|
+
? "asc" /* Direction.ASCENDING */
|
|
6325
|
+
: "desc" /* Direction.DESCENDING */;
|
|
6104
6326
|
orderBys.push(new OrderBy(orderBy.field, dir));
|
|
6105
6327
|
}
|
|
6106
6328
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
@@ -6117,6 +6339,8 @@ function queryToTarget(query) {
|
|
|
6117
6339
|
return queryImpl.memoizedTarget;
|
|
6118
6340
|
}
|
|
6119
6341
|
function queryWithAddedFilter(query, filter) {
|
|
6342
|
+
filter.getFirstInequalityField();
|
|
6343
|
+
getInequalityFilterField(query);
|
|
6120
6344
|
const newFilters = query.filters.concat([filter]);
|
|
6121
6345
|
return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
|
|
6122
6346
|
}
|
|
@@ -6177,7 +6401,13 @@ function queryMatchesPathAndCollectionGroup(query, doc) {
|
|
|
6177
6401
|
* in the results.
|
|
6178
6402
|
*/
|
|
6179
6403
|
function queryMatchesOrderBy(query, doc) {
|
|
6180
|
-
|
|
6404
|
+
// We must use `queryOrderBy()` to get the list of all orderBys (both implicit and explicit).
|
|
6405
|
+
// Note that for OR queries, orderBy applies to all disjunction terms and implicit orderBys must
|
|
6406
|
+
// be taken into account. For example, the query "a > 1 || b==1" has an implicit "orderBy a" due
|
|
6407
|
+
// to the inequality, and is evaluated as "a > 1 orderBy a || b==1 orderBy a".
|
|
6408
|
+
// A document with content of {b:1} matches the filters, but does not match the orderBy because
|
|
6409
|
+
// it's missing the field 'a'.
|
|
6410
|
+
for (const orderBy of queryOrderBy(query)) {
|
|
6181
6411
|
// order by key always matches
|
|
6182
6412
|
if (!orderBy.field.isKeyField() && doc.data.field(orderBy.field) === null) {
|
|
6183
6413
|
return false;
|
|
@@ -6239,9 +6469,9 @@ function compareDocs(orderBy, d1, d2) {
|
|
|
6239
6469
|
? DocumentKey.comparator(d1.key, d2.key)
|
|
6240
6470
|
: compareDocumentsByField(orderBy.field, d1, d2);
|
|
6241
6471
|
switch (orderBy.dir) {
|
|
6242
|
-
case "asc" /* ASCENDING */:
|
|
6472
|
+
case "asc" /* Direction.ASCENDING */:
|
|
6243
6473
|
return comparison;
|
|
6244
|
-
case "desc" /* DESCENDING */:
|
|
6474
|
+
case "desc" /* Direction.DESCENDING */:
|
|
6245
6475
|
return -1 * comparison;
|
|
6246
6476
|
default:
|
|
6247
6477
|
return fail();
|
|
@@ -6927,10 +7157,10 @@ function mutationEquals(left, right) {
|
|
|
6927
7157
|
if (!fieldTransformsAreEqual(left.fieldTransforms, right.fieldTransforms)) {
|
|
6928
7158
|
return false;
|
|
6929
7159
|
}
|
|
6930
|
-
if (left.type === 0 /* Set */) {
|
|
7160
|
+
if (left.type === 0 /* MutationType.Set */) {
|
|
6931
7161
|
return left.value.isEqual(right.value);
|
|
6932
7162
|
}
|
|
6933
|
-
if (left.type === 1 /* Patch */) {
|
|
7163
|
+
if (left.type === 1 /* MutationType.Patch */) {
|
|
6934
7164
|
return (left.data.isEqual(right.data) &&
|
|
6935
7165
|
left.fieldMask.isEqual(right.fieldMask));
|
|
6936
7166
|
}
|
|
@@ -6947,7 +7177,7 @@ class SetMutation extends Mutation {
|
|
|
6947
7177
|
this.value = value;
|
|
6948
7178
|
this.precondition = precondition;
|
|
6949
7179
|
this.fieldTransforms = fieldTransforms;
|
|
6950
|
-
this.type = 0 /* Set */;
|
|
7180
|
+
this.type = 0 /* MutationType.Set */;
|
|
6951
7181
|
}
|
|
6952
7182
|
getFieldMask() {
|
|
6953
7183
|
return null;
|
|
@@ -6999,7 +7229,7 @@ class PatchMutation extends Mutation {
|
|
|
6999
7229
|
this.fieldMask = fieldMask;
|
|
7000
7230
|
this.precondition = precondition;
|
|
7001
7231
|
this.fieldTransforms = fieldTransforms;
|
|
7002
|
-
this.type = 1 /* Patch */;
|
|
7232
|
+
this.type = 1 /* MutationType.Patch */;
|
|
7003
7233
|
}
|
|
7004
7234
|
getFieldMask() {
|
|
7005
7235
|
return this.fieldMask;
|
|
@@ -7101,7 +7331,7 @@ class DeleteMutation extends Mutation {
|
|
|
7101
7331
|
super();
|
|
7102
7332
|
this.key = key;
|
|
7103
7333
|
this.precondition = precondition;
|
|
7104
|
-
this.type = 2 /* Delete */;
|
|
7334
|
+
this.type = 2 /* MutationType.Delete */;
|
|
7105
7335
|
this.fieldTransforms = [];
|
|
7106
7336
|
}
|
|
7107
7337
|
getFieldMask() {
|
|
@@ -7135,7 +7365,7 @@ class VerifyMutation extends Mutation {
|
|
|
7135
7365
|
super();
|
|
7136
7366
|
this.key = key;
|
|
7137
7367
|
this.precondition = precondition;
|
|
7138
|
-
this.type = 3 /* Verify */;
|
|
7368
|
+
this.type = 3 /* MutationType.Verify */;
|
|
7139
7369
|
this.fieldTransforms = [];
|
|
7140
7370
|
}
|
|
7141
7371
|
getFieldMask() {
|
|
@@ -7764,13 +7994,13 @@ class TargetState {
|
|
|
7764
7994
|
let removedDocuments = documentKeySet();
|
|
7765
7995
|
this.documentChanges.forEach((key, changeType) => {
|
|
7766
7996
|
switch (changeType) {
|
|
7767
|
-
case 0 /* Added */:
|
|
7997
|
+
case 0 /* ChangeType.Added */:
|
|
7768
7998
|
addedDocuments = addedDocuments.add(key);
|
|
7769
7999
|
break;
|
|
7770
|
-
case 2 /* Modified */:
|
|
8000
|
+
case 2 /* ChangeType.Modified */:
|
|
7771
8001
|
modifiedDocuments = modifiedDocuments.add(key);
|
|
7772
8002
|
break;
|
|
7773
|
-
case 1 /* Removed */:
|
|
8003
|
+
case 1 /* ChangeType.Removed */:
|
|
7774
8004
|
removedDocuments = removedDocuments.add(key);
|
|
7775
8005
|
break;
|
|
7776
8006
|
default:
|
|
@@ -7846,12 +8076,12 @@ class WatchChangeAggregator {
|
|
|
7846
8076
|
this.forEachTarget(targetChange, targetId => {
|
|
7847
8077
|
const targetState = this.ensureTargetState(targetId);
|
|
7848
8078
|
switch (targetChange.state) {
|
|
7849
|
-
case 0 /* NoChange */:
|
|
8079
|
+
case 0 /* WatchTargetChangeState.NoChange */:
|
|
7850
8080
|
if (this.isActiveTarget(targetId)) {
|
|
7851
8081
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7852
8082
|
}
|
|
7853
8083
|
break;
|
|
7854
|
-
case 1 /* Added */:
|
|
8084
|
+
case 1 /* WatchTargetChangeState.Added */:
|
|
7855
8085
|
// We need to decrement the number of pending acks needed from watch
|
|
7856
8086
|
// for this targetId.
|
|
7857
8087
|
targetState.recordTargetResponse();
|
|
@@ -7863,7 +8093,7 @@ class WatchChangeAggregator {
|
|
|
7863
8093
|
}
|
|
7864
8094
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7865
8095
|
break;
|
|
7866
|
-
case 2 /* Removed */:
|
|
8096
|
+
case 2 /* WatchTargetChangeState.Removed */:
|
|
7867
8097
|
// We need to keep track of removed targets to we can post-filter and
|
|
7868
8098
|
// remove any target changes.
|
|
7869
8099
|
// We need to decrement the number of pending acks needed from watch
|
|
@@ -7873,13 +8103,13 @@ class WatchChangeAggregator {
|
|
|
7873
8103
|
this.removeTarget(targetId);
|
|
7874
8104
|
}
|
|
7875
8105
|
break;
|
|
7876
|
-
case 3 /* Current */:
|
|
8106
|
+
case 3 /* WatchTargetChangeState.Current */:
|
|
7877
8107
|
if (this.isActiveTarget(targetId)) {
|
|
7878
8108
|
targetState.markCurrent();
|
|
7879
8109
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7880
8110
|
}
|
|
7881
8111
|
break;
|
|
7882
|
-
case 4 /* Reset */:
|
|
8112
|
+
case 4 /* WatchTargetChangeState.Reset */:
|
|
7883
8113
|
if (this.isActiveTarget(targetId)) {
|
|
7884
8114
|
// Reset the target and synthesizes removes for all existing
|
|
7885
8115
|
// documents. The backend will re-add any documents that still
|
|
@@ -7989,7 +8219,7 @@ class WatchChangeAggregator {
|
|
|
7989
8219
|
targets.forEachWhile(targetId => {
|
|
7990
8220
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
7991
8221
|
if (targetData &&
|
|
7992
|
-
targetData.purpose !== 2 /* LimboResolution */) {
|
|
8222
|
+
targetData.purpose !== 2 /* TargetPurpose.LimboResolution */) {
|
|
7993
8223
|
isOnlyLimboTarget = false;
|
|
7994
8224
|
return false;
|
|
7995
8225
|
}
|
|
@@ -8016,8 +8246,8 @@ class WatchChangeAggregator {
|
|
|
8016
8246
|
return;
|
|
8017
8247
|
}
|
|
8018
8248
|
const changeType = this.targetContainsDocument(targetId, document.key)
|
|
8019
|
-
? 2 /* Modified */
|
|
8020
|
-
: 0 /* Added */;
|
|
8249
|
+
? 2 /* ChangeType.Modified */
|
|
8250
|
+
: 0 /* ChangeType.Added */;
|
|
8021
8251
|
const targetState = this.ensureTargetState(targetId);
|
|
8022
8252
|
targetState.addDocumentChange(document.key, changeType);
|
|
8023
8253
|
this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(document.key, document);
|
|
@@ -8038,7 +8268,7 @@ class WatchChangeAggregator {
|
|
|
8038
8268
|
}
|
|
8039
8269
|
const targetState = this.ensureTargetState(targetId);
|
|
8040
8270
|
if (this.targetContainsDocument(targetId, key)) {
|
|
8041
|
-
targetState.addDocumentChange(key, 1 /* Removed */);
|
|
8271
|
+
targetState.addDocumentChange(key, 1 /* ChangeType.Removed */);
|
|
8042
8272
|
}
|
|
8043
8273
|
else {
|
|
8044
8274
|
// The document may have entered and left the target before we raised a
|
|
@@ -8163,22 +8393,28 @@ function snapshotChangesMap() {
|
|
|
8163
8393
|
*/
|
|
8164
8394
|
const DIRECTIONS = (() => {
|
|
8165
8395
|
const dirs = {};
|
|
8166
|
-
dirs["asc" /* ASCENDING */] = 'ASCENDING';
|
|
8167
|
-
dirs["desc" /* DESCENDING */] = 'DESCENDING';
|
|
8396
|
+
dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
|
|
8397
|
+
dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
|
|
8168
8398
|
return dirs;
|
|
8169
8399
|
})();
|
|
8170
8400
|
const OPERATORS = (() => {
|
|
8171
8401
|
const ops = {};
|
|
8172
|
-
ops["<" /* LESS_THAN */] = 'LESS_THAN';
|
|
8173
|
-
ops["<=" /* LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
8174
|
-
ops[">" /* GREATER_THAN */] = 'GREATER_THAN';
|
|
8175
|
-
ops[">=" /* GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
8176
|
-
ops["==" /* EQUAL */] = 'EQUAL';
|
|
8177
|
-
ops["!=" /* NOT_EQUAL */] = 'NOT_EQUAL';
|
|
8178
|
-
ops["array-contains" /* ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
8179
|
-
ops["in" /* IN */] = 'IN';
|
|
8180
|
-
ops["not-in" /* NOT_IN */] = 'NOT_IN';
|
|
8181
|
-
ops["array-contains-any" /* ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
8402
|
+
ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
|
|
8403
|
+
ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
8404
|
+
ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
|
|
8405
|
+
ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
8406
|
+
ops["==" /* Operator.EQUAL */] = 'EQUAL';
|
|
8407
|
+
ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
|
|
8408
|
+
ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
8409
|
+
ops["in" /* Operator.IN */] = 'IN';
|
|
8410
|
+
ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
|
|
8411
|
+
ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
8412
|
+
return ops;
|
|
8413
|
+
})();
|
|
8414
|
+
const COMPOSITE_OPERATORS = (() => {
|
|
8415
|
+
const ops = {};
|
|
8416
|
+
ops["and" /* CompositeOperator.AND */] = 'AND';
|
|
8417
|
+
ops["or" /* CompositeOperator.OR */] = 'OR';
|
|
8182
8418
|
return ops;
|
|
8183
8419
|
})();
|
|
8184
8420
|
function assertPresent(value, description) {
|
|
@@ -8372,14 +8608,21 @@ function toDocument(serializer, document) {
|
|
|
8372
8608
|
return {
|
|
8373
8609
|
name: toName(serializer, document.key),
|
|
8374
8610
|
fields: document.data.value.mapValue.fields,
|
|
8375
|
-
updateTime: toTimestamp(serializer, document.version.toTimestamp())
|
|
8611
|
+
updateTime: toTimestamp(serializer, document.version.toTimestamp()),
|
|
8612
|
+
createTime: toTimestamp(serializer, document.createTime.toTimestamp())
|
|
8376
8613
|
};
|
|
8377
8614
|
}
|
|
8378
8615
|
function fromDocument(serializer, document, hasCommittedMutations) {
|
|
8379
8616
|
const key = fromName(serializer, document.name);
|
|
8380
8617
|
const version = fromVersion(document.updateTime);
|
|
8618
|
+
// If we read a document from persistence that is missing createTime, it's due
|
|
8619
|
+
// to older SDK versions not storing this information. In such cases, we'll
|
|
8620
|
+
// set the createTime to zero. This can be removed in the long term.
|
|
8621
|
+
const createTime = document.createTime
|
|
8622
|
+
? fromVersion(document.createTime)
|
|
8623
|
+
: SnapshotVersion.min();
|
|
8381
8624
|
const data = new ObjectValue({ mapValue: { fields: document.fields } });
|
|
8382
|
-
const result = MutableDocument.newFoundDocument(key, version, data);
|
|
8625
|
+
const result = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8383
8626
|
if (hasCommittedMutations) {
|
|
8384
8627
|
result.setHasCommittedMutations();
|
|
8385
8628
|
}
|
|
@@ -8391,8 +8634,11 @@ function fromFound(serializer, doc) {
|
|
|
8391
8634
|
assertPresent(doc.found.updateTime);
|
|
8392
8635
|
const key = fromName(serializer, doc.found.name);
|
|
8393
8636
|
const version = fromVersion(doc.found.updateTime);
|
|
8637
|
+
const createTime = doc.found.createTime
|
|
8638
|
+
? fromVersion(doc.found.createTime)
|
|
8639
|
+
: SnapshotVersion.min();
|
|
8394
8640
|
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
|
|
8395
|
-
return MutableDocument.newFoundDocument(key, version, data);
|
|
8641
|
+
return MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8396
8642
|
}
|
|
8397
8643
|
function fromMissing(serializer, result) {
|
|
8398
8644
|
hardAssert(!!result.missing);
|
|
@@ -8431,10 +8677,13 @@ function fromWatchChange(serializer, change) {
|
|
|
8431
8677
|
assertPresent(entityChange.document.updateTime);
|
|
8432
8678
|
const key = fromName(serializer, entityChange.document.name);
|
|
8433
8679
|
const version = fromVersion(entityChange.document.updateTime);
|
|
8680
|
+
const createTime = entityChange.document.createTime
|
|
8681
|
+
? fromVersion(entityChange.document.createTime)
|
|
8682
|
+
: SnapshotVersion.min();
|
|
8434
8683
|
const data = new ObjectValue({
|
|
8435
8684
|
mapValue: { fields: entityChange.document.fields }
|
|
8436
8685
|
});
|
|
8437
|
-
const doc = MutableDocument.newFoundDocument(key, version, data);
|
|
8686
|
+
const doc = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8438
8687
|
const updatedTargetIds = entityChange.targetIds || [];
|
|
8439
8688
|
const removedTargetIds = entityChange.removedTargetIds || [];
|
|
8440
8689
|
watchChange = new DocumentWatchChange(updatedTargetIds, removedTargetIds, doc.key, doc);
|
|
@@ -8476,19 +8725,19 @@ function fromWatchChange(serializer, change) {
|
|
|
8476
8725
|
}
|
|
8477
8726
|
function fromWatchTargetChangeState(state) {
|
|
8478
8727
|
if (state === 'NO_CHANGE') {
|
|
8479
|
-
return 0 /* NoChange */;
|
|
8728
|
+
return 0 /* WatchTargetChangeState.NoChange */;
|
|
8480
8729
|
}
|
|
8481
8730
|
else if (state === 'ADD') {
|
|
8482
|
-
return 1 /* Added */;
|
|
8731
|
+
return 1 /* WatchTargetChangeState.Added */;
|
|
8483
8732
|
}
|
|
8484
8733
|
else if (state === 'REMOVE') {
|
|
8485
|
-
return 2 /* Removed */;
|
|
8734
|
+
return 2 /* WatchTargetChangeState.Removed */;
|
|
8486
8735
|
}
|
|
8487
8736
|
else if (state === 'CURRENT') {
|
|
8488
|
-
return 3 /* Current */;
|
|
8737
|
+
return 3 /* WatchTargetChangeState.Current */;
|
|
8489
8738
|
}
|
|
8490
8739
|
else if (state === 'RESET') {
|
|
8491
|
-
return 4 /* Reset */;
|
|
8740
|
+
return 4 /* WatchTargetChangeState.Reset */;
|
|
8492
8741
|
}
|
|
8493
8742
|
else {
|
|
8494
8743
|
return fail();
|
|
@@ -8706,7 +8955,7 @@ function toQueryTarget(serializer, target) {
|
|
|
8706
8955
|
result.parent = toQueryPath(serializer, path.popLast());
|
|
8707
8956
|
result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
|
|
8708
8957
|
}
|
|
8709
|
-
const where =
|
|
8958
|
+
const where = toFilters(target.filters);
|
|
8710
8959
|
if (where) {
|
|
8711
8960
|
result.structuredQuery.where = where;
|
|
8712
8961
|
}
|
|
@@ -8758,7 +9007,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8758
9007
|
}
|
|
8759
9008
|
let filterBy = [];
|
|
8760
9009
|
if (query.where) {
|
|
8761
|
-
filterBy =
|
|
9010
|
+
filterBy = fromFilters(query.where);
|
|
8762
9011
|
}
|
|
8763
9012
|
let orderBy = [];
|
|
8764
9013
|
if (query.orderBy) {
|
|
@@ -8776,7 +9025,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8776
9025
|
if (query.endAt) {
|
|
8777
9026
|
endAt = fromEndAtCursor(query.endAt);
|
|
8778
9027
|
}
|
|
8779
|
-
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
9028
|
+
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* LimitType.First */, startAt, endAt);
|
|
8780
9029
|
}
|
|
8781
9030
|
function fromQueryTarget(target) {
|
|
8782
9031
|
return queryToTarget(convertQueryTargetToQuery(target));
|
|
@@ -8794,11 +9043,11 @@ function toListenRequestLabels(serializer, targetData) {
|
|
|
8794
9043
|
}
|
|
8795
9044
|
function toLabel(serializer, purpose) {
|
|
8796
9045
|
switch (purpose) {
|
|
8797
|
-
case 0 /* Listen */:
|
|
9046
|
+
case 0 /* TargetPurpose.Listen */:
|
|
8798
9047
|
return null;
|
|
8799
|
-
case 1 /* ExistenceFilterMismatch */:
|
|
9048
|
+
case 1 /* TargetPurpose.ExistenceFilterMismatch */:
|
|
8800
9049
|
return 'existence-filter-mismatch';
|
|
8801
|
-
case 2 /* LimboResolution */:
|
|
9050
|
+
case 2 /* TargetPurpose.LimboResolution */:
|
|
8802
9051
|
return 'limbo-document';
|
|
8803
9052
|
default:
|
|
8804
9053
|
return fail();
|
|
@@ -8825,32 +9074,29 @@ function toTarget(serializer, targetData) {
|
|
|
8825
9074
|
}
|
|
8826
9075
|
return result;
|
|
8827
9076
|
}
|
|
8828
|
-
function
|
|
9077
|
+
function toFilters(filters) {
|
|
8829
9078
|
if (filters.length === 0) {
|
|
8830
9079
|
return;
|
|
8831
9080
|
}
|
|
8832
|
-
|
|
8833
|
-
|
|
8834
|
-
|
|
8835
|
-
|
|
8836
|
-
|
|
9081
|
+
return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
|
|
9082
|
+
}
|
|
9083
|
+
function fromFilters(filter) {
|
|
9084
|
+
const result = fromFilter(filter);
|
|
9085
|
+
if (result instanceof CompositeFilter &&
|
|
9086
|
+
compositeFilterIsFlatConjunction(result)) {
|
|
9087
|
+
return result.getFilters();
|
|
8837
9088
|
}
|
|
8838
|
-
return
|
|
9089
|
+
return [result];
|
|
8839
9090
|
}
|
|
8840
9091
|
function fromFilter(filter) {
|
|
8841
|
-
if (
|
|
8842
|
-
return
|
|
8843
|
-
}
|
|
8844
|
-
else if (filter.unaryFilter !== undefined) {
|
|
8845
|
-
return [fromUnaryFilter(filter)];
|
|
9092
|
+
if (filter.unaryFilter !== undefined) {
|
|
9093
|
+
return fromUnaryFilter(filter);
|
|
8846
9094
|
}
|
|
8847
9095
|
else if (filter.fieldFilter !== undefined) {
|
|
8848
|
-
return
|
|
9096
|
+
return fromFieldFilter(filter);
|
|
8849
9097
|
}
|
|
8850
9098
|
else if (filter.compositeFilter !== undefined) {
|
|
8851
|
-
return filter
|
|
8852
|
-
.filters.map(f => fromFilter(f))
|
|
8853
|
-
.reduce((accum, current) => accum.concat(current));
|
|
9099
|
+
return fromCompositeFilter(filter);
|
|
8854
9100
|
}
|
|
8855
9101
|
else {
|
|
8856
9102
|
return fail();
|
|
@@ -8895,9 +9141,9 @@ function toDirection(dir) {
|
|
|
8895
9141
|
function fromDirection(dir) {
|
|
8896
9142
|
switch (dir) {
|
|
8897
9143
|
case 'ASCENDING':
|
|
8898
|
-
return "asc" /* ASCENDING */;
|
|
9144
|
+
return "asc" /* Direction.ASCENDING */;
|
|
8899
9145
|
case 'DESCENDING':
|
|
8900
|
-
return "desc" /* DESCENDING */;
|
|
9146
|
+
return "desc" /* Direction.DESCENDING */;
|
|
8901
9147
|
default:
|
|
8902
9148
|
return undefined;
|
|
8903
9149
|
}
|
|
@@ -8906,34 +9152,47 @@ function fromDirection(dir) {
|
|
|
8906
9152
|
function toOperatorName(op) {
|
|
8907
9153
|
return OPERATORS[op];
|
|
8908
9154
|
}
|
|
9155
|
+
function toCompositeOperatorName(op) {
|
|
9156
|
+
return COMPOSITE_OPERATORS[op];
|
|
9157
|
+
}
|
|
8909
9158
|
function fromOperatorName(op) {
|
|
8910
9159
|
switch (op) {
|
|
8911
9160
|
case 'EQUAL':
|
|
8912
|
-
return "==" /* EQUAL */;
|
|
9161
|
+
return "==" /* Operator.EQUAL */;
|
|
8913
9162
|
case 'NOT_EQUAL':
|
|
8914
|
-
return "!=" /* NOT_EQUAL */;
|
|
9163
|
+
return "!=" /* Operator.NOT_EQUAL */;
|
|
8915
9164
|
case 'GREATER_THAN':
|
|
8916
|
-
return ">" /* GREATER_THAN */;
|
|
9165
|
+
return ">" /* Operator.GREATER_THAN */;
|
|
8917
9166
|
case 'GREATER_THAN_OR_EQUAL':
|
|
8918
|
-
return ">=" /* GREATER_THAN_OR_EQUAL */;
|
|
9167
|
+
return ">=" /* Operator.GREATER_THAN_OR_EQUAL */;
|
|
8919
9168
|
case 'LESS_THAN':
|
|
8920
|
-
return "<" /* LESS_THAN */;
|
|
9169
|
+
return "<" /* Operator.LESS_THAN */;
|
|
8921
9170
|
case 'LESS_THAN_OR_EQUAL':
|
|
8922
|
-
return "<=" /* LESS_THAN_OR_EQUAL */;
|
|
9171
|
+
return "<=" /* Operator.LESS_THAN_OR_EQUAL */;
|
|
8923
9172
|
case 'ARRAY_CONTAINS':
|
|
8924
|
-
return "array-contains" /* ARRAY_CONTAINS */;
|
|
9173
|
+
return "array-contains" /* Operator.ARRAY_CONTAINS */;
|
|
8925
9174
|
case 'IN':
|
|
8926
|
-
return "in" /* IN */;
|
|
9175
|
+
return "in" /* Operator.IN */;
|
|
8927
9176
|
case 'NOT_IN':
|
|
8928
|
-
return "not-in" /* NOT_IN */;
|
|
9177
|
+
return "not-in" /* Operator.NOT_IN */;
|
|
8929
9178
|
case 'ARRAY_CONTAINS_ANY':
|
|
8930
|
-
return "array-contains-any" /* ARRAY_CONTAINS_ANY */;
|
|
9179
|
+
return "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
8931
9180
|
case 'OPERATOR_UNSPECIFIED':
|
|
8932
9181
|
return fail();
|
|
8933
9182
|
default:
|
|
8934
9183
|
return fail();
|
|
8935
9184
|
}
|
|
8936
9185
|
}
|
|
9186
|
+
function fromCompositeOperatorName(op) {
|
|
9187
|
+
switch (op) {
|
|
9188
|
+
case 'AND':
|
|
9189
|
+
return "and" /* CompositeOperator.AND */;
|
|
9190
|
+
case 'OR':
|
|
9191
|
+
return "or" /* CompositeOperator.OR */;
|
|
9192
|
+
default:
|
|
9193
|
+
return fail();
|
|
9194
|
+
}
|
|
9195
|
+
}
|
|
8937
9196
|
function toFieldPathReference(path) {
|
|
8938
9197
|
return { fieldPath: path.canonicalString() };
|
|
8939
9198
|
}
|
|
@@ -8950,12 +9209,32 @@ function toPropertyOrder(orderBy) {
|
|
|
8950
9209
|
function fromPropertyOrder(orderBy) {
|
|
8951
9210
|
return new OrderBy(fromFieldPathReference(orderBy.field), fromDirection(orderBy.direction));
|
|
8952
9211
|
}
|
|
8953
|
-
function fromFieldFilter(filter) {
|
|
8954
|
-
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
8955
|
-
}
|
|
8956
9212
|
// visible for testing
|
|
9213
|
+
function toFilter(filter) {
|
|
9214
|
+
if (filter instanceof FieldFilter) {
|
|
9215
|
+
return toUnaryOrFieldFilter(filter);
|
|
9216
|
+
}
|
|
9217
|
+
else if (filter instanceof CompositeFilter) {
|
|
9218
|
+
return toCompositeFilter(filter);
|
|
9219
|
+
}
|
|
9220
|
+
else {
|
|
9221
|
+
return fail();
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
function toCompositeFilter(filter) {
|
|
9225
|
+
const protos = filter.getFilters().map(filter => toFilter(filter));
|
|
9226
|
+
if (protos.length === 1) {
|
|
9227
|
+
return protos[0];
|
|
9228
|
+
}
|
|
9229
|
+
return {
|
|
9230
|
+
compositeFilter: {
|
|
9231
|
+
op: toCompositeOperatorName(filter.op),
|
|
9232
|
+
filters: protos
|
|
9233
|
+
}
|
|
9234
|
+
};
|
|
9235
|
+
}
|
|
8957
9236
|
function toUnaryOrFieldFilter(filter) {
|
|
8958
|
-
if (filter.op === "==" /* EQUAL */) {
|
|
9237
|
+
if (filter.op === "==" /* Operator.EQUAL */) {
|
|
8959
9238
|
if (isNanValue(filter.value)) {
|
|
8960
9239
|
return {
|
|
8961
9240
|
unaryFilter: {
|
|
@@ -8973,7 +9252,7 @@ function toUnaryOrFieldFilter(filter) {
|
|
|
8973
9252
|
};
|
|
8974
9253
|
}
|
|
8975
9254
|
}
|
|
8976
|
-
else if (filter.op === "!=" /* NOT_EQUAL */) {
|
|
9255
|
+
else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
8977
9256
|
if (isNanValue(filter.value)) {
|
|
8978
9257
|
return {
|
|
8979
9258
|
unaryFilter: {
|
|
@@ -9003,22 +9282,22 @@ function fromUnaryFilter(filter) {
|
|
|
9003
9282
|
switch (filter.unaryFilter.op) {
|
|
9004
9283
|
case 'IS_NAN':
|
|
9005
9284
|
const nanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9006
|
-
return FieldFilter.create(nanField, "==" /* EQUAL */, {
|
|
9285
|
+
return FieldFilter.create(nanField, "==" /* Operator.EQUAL */, {
|
|
9007
9286
|
doubleValue: NaN
|
|
9008
9287
|
});
|
|
9009
9288
|
case 'IS_NULL':
|
|
9010
9289
|
const nullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9011
|
-
return FieldFilter.create(nullField, "==" /* EQUAL */, {
|
|
9290
|
+
return FieldFilter.create(nullField, "==" /* Operator.EQUAL */, {
|
|
9012
9291
|
nullValue: 'NULL_VALUE'
|
|
9013
9292
|
});
|
|
9014
9293
|
case 'IS_NOT_NAN':
|
|
9015
9294
|
const notNanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9016
|
-
return FieldFilter.create(notNanField, "!=" /* NOT_EQUAL */, {
|
|
9295
|
+
return FieldFilter.create(notNanField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9017
9296
|
doubleValue: NaN
|
|
9018
9297
|
});
|
|
9019
9298
|
case 'IS_NOT_NULL':
|
|
9020
9299
|
const notNullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9021
|
-
return FieldFilter.create(notNullField, "!=" /* NOT_EQUAL */, {
|
|
9300
|
+
return FieldFilter.create(notNullField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9022
9301
|
nullValue: 'NULL_VALUE'
|
|
9023
9302
|
});
|
|
9024
9303
|
case 'OPERATOR_UNSPECIFIED':
|
|
@@ -9027,6 +9306,12 @@ function fromUnaryFilter(filter) {
|
|
|
9027
9306
|
return fail();
|
|
9028
9307
|
}
|
|
9029
9308
|
}
|
|
9309
|
+
function fromFieldFilter(filter) {
|
|
9310
|
+
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
9311
|
+
}
|
|
9312
|
+
function fromCompositeFilter(filter) {
|
|
9313
|
+
return CompositeFilter.create(filter.compositeFilter.filters.map(filter => fromFilter(filter)), fromCompositeOperatorName(filter.compositeFilter.op));
|
|
9314
|
+
}
|
|
9030
9315
|
function toDocumentMask(fieldMask) {
|
|
9031
9316
|
const canonicalFields = [];
|
|
9032
9317
|
fieldMask.fields.forEach(field => canonicalFields.push(field.canonicalString()));
|
|
@@ -9266,7 +9551,7 @@ function fromDbTarget(dbTarget) {
|
|
|
9266
9551
|
else {
|
|
9267
9552
|
target = fromQueryTarget(dbTarget.query);
|
|
9268
9553
|
}
|
|
9269
|
-
return new TargetData(target, dbTarget.targetId, 0 /* Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9554
|
+
return new TargetData(target, dbTarget.targetId, 0 /* TargetPurpose.Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9270
9555
|
}
|
|
9271
9556
|
/** Encodes TargetData into a DbTarget for storage locally. */
|
|
9272
9557
|
function toDbTarget(localSerializer, targetData) {
|
|
@@ -9343,7 +9628,7 @@ function fromBundledQuery(bundledQuery) {
|
|
|
9343
9628
|
structuredQuery: bundledQuery.structuredQuery
|
|
9344
9629
|
});
|
|
9345
9630
|
if (bundledQuery.limitType === 'LAST') {
|
|
9346
|
-
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
9631
|
+
return queryWithLimit(query, query.limit, "L" /* LimitType.Last */);
|
|
9347
9632
|
}
|
|
9348
9633
|
return query;
|
|
9349
9634
|
}
|
|
@@ -10118,7 +10403,7 @@ class IndexByteEncoder {
|
|
|
10118
10403
|
this.orderedCode.seed(encodedBytes);
|
|
10119
10404
|
}
|
|
10120
10405
|
forKind(kind) {
|
|
10121
|
-
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
10406
|
+
return kind === 0 /* IndexKind.ASCENDING */ ? this.ascending : this.descending;
|
|
10122
10407
|
}
|
|
10123
10408
|
encodedBytes() {
|
|
10124
10409
|
return this.orderedCode.encodedBytes();
|
|
@@ -10273,6 +10558,7 @@ class TargetIndexMatcher {
|
|
|
10273
10558
|
* omitted.
|
|
10274
10559
|
*/
|
|
10275
10560
|
servedByIndex(index) {
|
|
10561
|
+
hardAssert(index.collectionGroup === this.collectionId);
|
|
10276
10562
|
// If there is an array element, find a matching filter.
|
|
10277
10563
|
const arraySegment = fieldIndexGetArraySegment(index);
|
|
10278
10564
|
if (arraySegment !== undefined &&
|
|
@@ -10336,19 +10622,268 @@ class TargetIndexMatcher {
|
|
|
10336
10622
|
if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
|
|
10337
10623
|
return false;
|
|
10338
10624
|
}
|
|
10339
|
-
const isArrayOperator = filter.op === "array-contains" /* ARRAY_CONTAINS */ ||
|
|
10340
|
-
filter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */;
|
|
10341
|
-
return (segment.kind === 2 /* CONTAINS */) === isArrayOperator;
|
|
10625
|
+
const isArrayOperator = filter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
10626
|
+
filter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
10627
|
+
return (segment.kind === 2 /* IndexKind.CONTAINS */) === isArrayOperator;
|
|
10342
10628
|
}
|
|
10343
10629
|
matchesOrderBy(orderBy, segment) {
|
|
10344
10630
|
if (!orderBy.field.isEqual(segment.fieldPath)) {
|
|
10345
10631
|
return false;
|
|
10346
10632
|
}
|
|
10347
|
-
return ((segment.kind === 0 /* ASCENDING */ &&
|
|
10348
|
-
orderBy.dir === "asc" /* ASCENDING */) ||
|
|
10349
|
-
(segment.kind === 1 /* DESCENDING */ &&
|
|
10350
|
-
orderBy.dir === "desc" /* DESCENDING */));
|
|
10633
|
+
return ((segment.kind === 0 /* IndexKind.ASCENDING */ &&
|
|
10634
|
+
orderBy.dir === "asc" /* Direction.ASCENDING */) ||
|
|
10635
|
+
(segment.kind === 1 /* IndexKind.DESCENDING */ &&
|
|
10636
|
+
orderBy.dir === "desc" /* Direction.DESCENDING */));
|
|
10637
|
+
}
|
|
10638
|
+
}
|
|
10639
|
+
|
|
10640
|
+
/**
|
|
10641
|
+
* @license
|
|
10642
|
+
* Copyright 2022 Google LLC
|
|
10643
|
+
*
|
|
10644
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10645
|
+
* you may not use this file except in compliance with the License.
|
|
10646
|
+
* You may obtain a copy of the License at
|
|
10647
|
+
*
|
|
10648
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10649
|
+
*
|
|
10650
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10651
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10652
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10653
|
+
* See the License for the specific language governing permissions and
|
|
10654
|
+
* limitations under the License.
|
|
10655
|
+
*/
|
|
10656
|
+
/**
|
|
10657
|
+
* Provides utility functions that help with boolean logic transformations needed for handling
|
|
10658
|
+
* complex filters used in queries.
|
|
10659
|
+
*/
|
|
10660
|
+
/**
|
|
10661
|
+
* The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in
|
|
10662
|
+
* [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given
|
|
10663
|
+
* input into a disjunction of equality filters and returns the expanded filter.
|
|
10664
|
+
*/
|
|
10665
|
+
function computeInExpansion(filter) {
|
|
10666
|
+
var _a, _b;
|
|
10667
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10668
|
+
if (filter instanceof FieldFilter) {
|
|
10669
|
+
if (filter instanceof InFilter) {
|
|
10670
|
+
const expandedFilters = ((_b = (_a = filter.value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.map(value => FieldFilter.create(filter.field, "==" /* Operator.EQUAL */, value))) || [];
|
|
10671
|
+
return CompositeFilter.create(expandedFilters, "or" /* CompositeOperator.OR */);
|
|
10672
|
+
}
|
|
10673
|
+
else {
|
|
10674
|
+
// We have reached other kinds of field filters.
|
|
10675
|
+
return filter;
|
|
10676
|
+
}
|
|
10677
|
+
}
|
|
10678
|
+
// We have a composite filter.
|
|
10679
|
+
const expandedFilters = filter.filters.map(subfilter => computeInExpansion(subfilter));
|
|
10680
|
+
return CompositeFilter.create(expandedFilters, filter.op);
|
|
10681
|
+
}
|
|
10682
|
+
/**
|
|
10683
|
+
* Given a composite filter, returns the list of terms in its disjunctive normal form.
|
|
10684
|
+
*
|
|
10685
|
+
* <p>Each element in the return value is one term of the resulting DNF. For instance: For the
|
|
10686
|
+
* input: (A || B) && C, the DNF form is: (A && C) || (B && C), and the return value is a list
|
|
10687
|
+
* with two elements: a composite filter that performs (A && C), and a composite filter that
|
|
10688
|
+
* performs (B && C).
|
|
10689
|
+
*
|
|
10690
|
+
* @param filter the composite filter to calculate DNF transform for.
|
|
10691
|
+
* @return the terms in the DNF transform.
|
|
10692
|
+
*/
|
|
10693
|
+
function getDnfTerms(filter) {
|
|
10694
|
+
if (filter.getFilters().length === 0) {
|
|
10695
|
+
return [];
|
|
10696
|
+
}
|
|
10697
|
+
const result = computeDistributedNormalForm(computeInExpansion(filter));
|
|
10698
|
+
hardAssert(isDisjunctiveNormalForm(result));
|
|
10699
|
+
if (isSingleFieldFilter(result) || isFlatConjunction(result)) {
|
|
10700
|
+
return [result];
|
|
10701
|
+
}
|
|
10702
|
+
return result.getFilters();
|
|
10703
|
+
}
|
|
10704
|
+
/** Returns true if the given filter is a single field filter. e.g. (a == 10). */
|
|
10705
|
+
function isSingleFieldFilter(filter) {
|
|
10706
|
+
return filter instanceof FieldFilter;
|
|
10707
|
+
}
|
|
10708
|
+
/**
|
|
10709
|
+
* Returns true if the given filter is the conjunction of one or more field filters. e.g. (a == 10
|
|
10710
|
+
* && b == 20)
|
|
10711
|
+
*/
|
|
10712
|
+
function isFlatConjunction(filter) {
|
|
10713
|
+
return (filter instanceof CompositeFilter &&
|
|
10714
|
+
compositeFilterIsFlatConjunction(filter));
|
|
10715
|
+
}
|
|
10716
|
+
/**
|
|
10717
|
+
* Returns whether or not the given filter is in disjunctive normal form (DNF).
|
|
10718
|
+
*
|
|
10719
|
+
* <p>In boolean logic, a disjunctive normal form (DNF) is a canonical normal form of a logical
|
|
10720
|
+
* formula consisting of a disjunction of conjunctions; it can also be described as an OR of ANDs.
|
|
10721
|
+
*
|
|
10722
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
|
10723
|
+
*/
|
|
10724
|
+
function isDisjunctiveNormalForm(filter) {
|
|
10725
|
+
return (isSingleFieldFilter(filter) ||
|
|
10726
|
+
isFlatConjunction(filter) ||
|
|
10727
|
+
isDisjunctionOfFieldFiltersAndFlatConjunctions(filter));
|
|
10728
|
+
}
|
|
10729
|
+
/**
|
|
10730
|
+
* Returns true if the given filter is the disjunction of one or more "flat conjunctions" and
|
|
10731
|
+
* field filters. e.g. (a == 10) || (b==20 && c==30)
|
|
10732
|
+
*/
|
|
10733
|
+
function isDisjunctionOfFieldFiltersAndFlatConjunctions(filter) {
|
|
10734
|
+
if (filter instanceof CompositeFilter) {
|
|
10735
|
+
if (compositeFilterIsDisjunction(filter)) {
|
|
10736
|
+
for (const subFilter of filter.getFilters()) {
|
|
10737
|
+
if (!isSingleFieldFilter(subFilter) && !isFlatConjunction(subFilter)) {
|
|
10738
|
+
return false;
|
|
10739
|
+
}
|
|
10740
|
+
}
|
|
10741
|
+
return true;
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
return false;
|
|
10745
|
+
}
|
|
10746
|
+
function computeDistributedNormalForm(filter) {
|
|
10747
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10748
|
+
if (filter instanceof FieldFilter) {
|
|
10749
|
+
return filter;
|
|
10750
|
+
}
|
|
10751
|
+
if (filter.filters.length === 1) {
|
|
10752
|
+
return computeDistributedNormalForm(filter.filters[0]);
|
|
10753
|
+
}
|
|
10754
|
+
// Compute DNF for each of the subfilters first
|
|
10755
|
+
const result = filter.filters.map(subfilter => computeDistributedNormalForm(subfilter));
|
|
10756
|
+
let newFilter = CompositeFilter.create(result, filter.op);
|
|
10757
|
+
newFilter = applyAssociation(newFilter);
|
|
10758
|
+
if (isDisjunctiveNormalForm(newFilter)) {
|
|
10759
|
+
return newFilter;
|
|
10760
|
+
}
|
|
10761
|
+
hardAssert(newFilter instanceof CompositeFilter);
|
|
10762
|
+
hardAssert(compositeFilterIsConjunction(newFilter));
|
|
10763
|
+
hardAssert(newFilter.filters.length > 1);
|
|
10764
|
+
return newFilter.filters.reduce((runningResult, filter) => applyDistribution(runningResult, filter));
|
|
10765
|
+
}
|
|
10766
|
+
function applyDistribution(lhs, rhs) {
|
|
10767
|
+
hardAssert(lhs instanceof FieldFilter || lhs instanceof CompositeFilter);
|
|
10768
|
+
hardAssert(rhs instanceof FieldFilter || rhs instanceof CompositeFilter);
|
|
10769
|
+
let result;
|
|
10770
|
+
if (lhs instanceof FieldFilter) {
|
|
10771
|
+
if (rhs instanceof FieldFilter) {
|
|
10772
|
+
// FieldFilter FieldFilter
|
|
10773
|
+
result = applyDistributionFieldFilters(lhs, rhs);
|
|
10774
|
+
}
|
|
10775
|
+
else {
|
|
10776
|
+
// FieldFilter CompositeFilter
|
|
10777
|
+
result = applyDistributionFieldAndCompositeFilters(lhs, rhs);
|
|
10778
|
+
}
|
|
10779
|
+
}
|
|
10780
|
+
else {
|
|
10781
|
+
if (rhs instanceof FieldFilter) {
|
|
10782
|
+
// CompositeFilter FieldFilter
|
|
10783
|
+
result = applyDistributionFieldAndCompositeFilters(rhs, lhs);
|
|
10784
|
+
}
|
|
10785
|
+
else {
|
|
10786
|
+
// CompositeFilter CompositeFilter
|
|
10787
|
+
result = applyDistributionCompositeFilters(lhs, rhs);
|
|
10788
|
+
}
|
|
10789
|
+
}
|
|
10790
|
+
return applyAssociation(result);
|
|
10791
|
+
}
|
|
10792
|
+
function applyDistributionFieldFilters(lhs, rhs) {
|
|
10793
|
+
// Conjunction distribution for two field filters is the conjunction of them.
|
|
10794
|
+
return CompositeFilter.create([lhs, rhs], "and" /* CompositeOperator.AND */);
|
|
10795
|
+
}
|
|
10796
|
+
function applyDistributionCompositeFilters(lhs, rhs) {
|
|
10797
|
+
hardAssert(lhs.filters.length > 0 && rhs.filters.length > 0);
|
|
10798
|
+
// There are four cases:
|
|
10799
|
+
// (A & B) & (C & D) --> (A & B & C & D)
|
|
10800
|
+
// (A & B) & (C | D) --> (A & B & C) | (A & B & D)
|
|
10801
|
+
// (A | B) & (C & D) --> (C & D & A) | (C & D & B)
|
|
10802
|
+
// (A | B) & (C | D) --> (A & C) | (A & D) | (B & C) | (B & D)
|
|
10803
|
+
// Case 1 is a merge.
|
|
10804
|
+
if (compositeFilterIsConjunction(lhs) && compositeFilterIsConjunction(rhs)) {
|
|
10805
|
+
return compositeFilterWithAddedFilters(lhs, rhs.getFilters());
|
|
10806
|
+
}
|
|
10807
|
+
// Case 2,3,4 all have at least one side (lhs or rhs) that is a disjunction. In all three cases
|
|
10808
|
+
// we should take each element of the disjunction and distribute it over the other side, and
|
|
10809
|
+
// return the disjunction of the distribution results.
|
|
10810
|
+
const disjunctionSide = compositeFilterIsDisjunction(lhs) ? lhs : rhs;
|
|
10811
|
+
const otherSide = compositeFilterIsDisjunction(lhs) ? rhs : lhs;
|
|
10812
|
+
const results = disjunctionSide.filters.map(subfilter => applyDistribution(subfilter, otherSide));
|
|
10813
|
+
return CompositeFilter.create(results, "or" /* CompositeOperator.OR */);
|
|
10814
|
+
}
|
|
10815
|
+
function applyDistributionFieldAndCompositeFilters(fieldFilter, compositeFilter) {
|
|
10816
|
+
// There are two cases:
|
|
10817
|
+
// A & (B & C) --> (A & B & C)
|
|
10818
|
+
// A & (B | C) --> (A & B) | (A & C)
|
|
10819
|
+
if (compositeFilterIsConjunction(compositeFilter)) {
|
|
10820
|
+
// Case 1
|
|
10821
|
+
return compositeFilterWithAddedFilters(compositeFilter, fieldFilter.getFilters());
|
|
10822
|
+
}
|
|
10823
|
+
else {
|
|
10824
|
+
// Case 2
|
|
10825
|
+
const newFilters = compositeFilter.filters.map(subfilter => applyDistribution(fieldFilter, subfilter));
|
|
10826
|
+
return CompositeFilter.create(newFilters, "or" /* CompositeOperator.OR */);
|
|
10827
|
+
}
|
|
10828
|
+
}
|
|
10829
|
+
/**
|
|
10830
|
+
* Applies the associativity property to the given filter and returns the resulting filter.
|
|
10831
|
+
*
|
|
10832
|
+
* <ul>
|
|
10833
|
+
* <li>A | (B | C) == (A | B) | C == (A | B | C)
|
|
10834
|
+
* <li>A & (B & C) == (A & B) & C == (A & B & C)
|
|
10835
|
+
* </ul>
|
|
10836
|
+
*
|
|
10837
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Associative_property#Propositional_logic
|
|
10838
|
+
*/
|
|
10839
|
+
function applyAssociation(filter) {
|
|
10840
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10841
|
+
if (filter instanceof FieldFilter) {
|
|
10842
|
+
return filter;
|
|
10843
|
+
}
|
|
10844
|
+
const filters = filter.getFilters();
|
|
10845
|
+
// If the composite filter only contains 1 filter, apply associativity to it.
|
|
10846
|
+
if (filters.length === 1) {
|
|
10847
|
+
return applyAssociation(filters[0]);
|
|
10848
|
+
}
|
|
10849
|
+
// Associativity applied to a flat composite filter results is itself.
|
|
10850
|
+
if (compositeFilterIsFlat(filter)) {
|
|
10851
|
+
return filter;
|
|
10852
|
+
}
|
|
10853
|
+
// First apply associativity to all subfilters. This will in turn recursively apply
|
|
10854
|
+
// associativity to all nested composite filters and field filters.
|
|
10855
|
+
const updatedFilters = filters.map(subfilter => applyAssociation(subfilter));
|
|
10856
|
+
// For composite subfilters that perform the same kind of logical operation as `compositeFilter`
|
|
10857
|
+
// take out their filters and add them to `compositeFilter`. For example:
|
|
10858
|
+
// compositeFilter = (A | (B | C | D))
|
|
10859
|
+
// compositeSubfilter = (B | C | D)
|
|
10860
|
+
// Result: (A | B | C | D)
|
|
10861
|
+
// Note that the `compositeSubfilter` has been eliminated, and its filters (B, C, D) have been
|
|
10862
|
+
// added to the top-level "compositeFilter".
|
|
10863
|
+
const newSubfilters = [];
|
|
10864
|
+
updatedFilters.forEach(subfilter => {
|
|
10865
|
+
if (subfilter instanceof FieldFilter) {
|
|
10866
|
+
newSubfilters.push(subfilter);
|
|
10867
|
+
}
|
|
10868
|
+
else if (subfilter instanceof CompositeFilter) {
|
|
10869
|
+
if (subfilter.op === filter.op) {
|
|
10870
|
+
// compositeFilter: (A | (B | C))
|
|
10871
|
+
// compositeSubfilter: (B | C)
|
|
10872
|
+
// Result: (A | B | C)
|
|
10873
|
+
newSubfilters.push(...subfilter.filters);
|
|
10874
|
+
}
|
|
10875
|
+
else {
|
|
10876
|
+
// compositeFilter: (A | (B & C))
|
|
10877
|
+
// compositeSubfilter: (B & C)
|
|
10878
|
+
// Result: (A | (B & C))
|
|
10879
|
+
newSubfilters.push(subfilter);
|
|
10880
|
+
}
|
|
10881
|
+
}
|
|
10882
|
+
});
|
|
10883
|
+
if (newSubfilters.length === 1) {
|
|
10884
|
+
return newSubfilters[0];
|
|
10351
10885
|
}
|
|
10886
|
+
return CompositeFilter.create(newSubfilters, filter.op);
|
|
10352
10887
|
}
|
|
10353
10888
|
|
|
10354
10889
|
/**
|
|
@@ -10395,7 +10930,7 @@ class MemoryIndexManager {
|
|
|
10395
10930
|
}
|
|
10396
10931
|
getIndexType(transaction, target) {
|
|
10397
10932
|
// Field indices are not supported with memory persistence.
|
|
10398
|
-
return PersistencePromise.resolve(0 /* NONE */);
|
|
10933
|
+
return PersistencePromise.resolve(0 /* IndexType.NONE */);
|
|
10399
10934
|
}
|
|
10400
10935
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10401
10936
|
// Field indices are not supported with memory persistence.
|
|
@@ -10618,8 +11153,14 @@ class IndexedDbIndexManager {
|
|
|
10618
11153
|
if (subTargets) {
|
|
10619
11154
|
return subTargets;
|
|
10620
11155
|
}
|
|
10621
|
-
|
|
10622
|
-
|
|
11156
|
+
if (target.filters.length === 0) {
|
|
11157
|
+
subTargets = [target];
|
|
11158
|
+
}
|
|
11159
|
+
else {
|
|
11160
|
+
// There is an implicit AND operation between all the filters stored in the target
|
|
11161
|
+
const dnf = getDnfTerms(CompositeFilter.create(target.filters, "and" /* CompositeOperator.AND */));
|
|
11162
|
+
subTargets = dnf.map(term => newTarget(target.path, target.collectionGroup, target.orderBy, term.getFilters(), target.limit, target.startAt, target.endAt));
|
|
11163
|
+
}
|
|
10623
11164
|
this.targetToDnfSubTargets.set(target, subTargets);
|
|
10624
11165
|
return subTargets;
|
|
10625
11166
|
}
|
|
@@ -10677,18 +11218,29 @@ class IndexedDbIndexManager {
|
|
|
10677
11218
|
});
|
|
10678
11219
|
}
|
|
10679
11220
|
getIndexType(transaction, target) {
|
|
10680
|
-
let indexType = 2 /* FULL */;
|
|
10681
|
-
|
|
11221
|
+
let indexType = 2 /* IndexType.FULL */;
|
|
11222
|
+
const subTargets = this.getSubTargets(target);
|
|
11223
|
+
return PersistencePromise.forEach(subTargets, (target) => {
|
|
10682
11224
|
return this.getFieldIndex(transaction, target).next(index => {
|
|
10683
11225
|
if (!index) {
|
|
10684
|
-
indexType = 0 /* NONE */;
|
|
11226
|
+
indexType = 0 /* IndexType.NONE */;
|
|
10685
11227
|
}
|
|
10686
|
-
else if (indexType !== 0 /* NONE */ &&
|
|
11228
|
+
else if (indexType !== 0 /* IndexType.NONE */ &&
|
|
10687
11229
|
index.fields.length < targetGetSegmentCount(target)) {
|
|
10688
|
-
indexType = 1 /* PARTIAL */;
|
|
11230
|
+
indexType = 1 /* IndexType.PARTIAL */;
|
|
10689
11231
|
}
|
|
10690
11232
|
});
|
|
10691
|
-
}).next(() =>
|
|
11233
|
+
}).next(() => {
|
|
11234
|
+
// OR queries have more than one sub-target (one sub-target per DNF term). We currently consider
|
|
11235
|
+
// OR queries that have a `limit` to have a partial index. For such queries we perform sorting
|
|
11236
|
+
// and apply the limit in memory as a post-processing step.
|
|
11237
|
+
if (targetHasLimit(target) &&
|
|
11238
|
+
subTargets.length > 1 &&
|
|
11239
|
+
indexType === 2 /* IndexType.FULL */) {
|
|
11240
|
+
return 1 /* IndexType.PARTIAL */;
|
|
11241
|
+
}
|
|
11242
|
+
return indexType;
|
|
11243
|
+
});
|
|
10692
11244
|
}
|
|
10693
11245
|
/**
|
|
10694
11246
|
* Returns the byte encoded form of the directional values in the field index.
|
|
@@ -10710,7 +11262,7 @@ class IndexedDbIndexManager {
|
|
|
10710
11262
|
/** Encodes a single value to the ascending index format. */
|
|
10711
11263
|
encodeSingleElement(value) {
|
|
10712
11264
|
const encoder = new IndexByteEncoder();
|
|
10713
|
-
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
11265
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* IndexKind.ASCENDING */));
|
|
10714
11266
|
return encoder.encodedBytes();
|
|
10715
11267
|
}
|
|
10716
11268
|
/**
|
|
@@ -10726,15 +11278,15 @@ class IndexedDbIndexManager {
|
|
|
10726
11278
|
* Encodes the given field values according to the specification in `target`.
|
|
10727
11279
|
* For IN queries, a list of possible values is returned.
|
|
10728
11280
|
*/
|
|
10729
|
-
encodeValues(fieldIndex, target,
|
|
10730
|
-
if (
|
|
11281
|
+
encodeValues(fieldIndex, target, values) {
|
|
11282
|
+
if (values === null) {
|
|
10731
11283
|
return [];
|
|
10732
11284
|
}
|
|
10733
11285
|
let encoders = [];
|
|
10734
11286
|
encoders.push(new IndexByteEncoder());
|
|
10735
|
-
let
|
|
11287
|
+
let valueIdx = 0;
|
|
10736
11288
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
10737
|
-
const value =
|
|
11289
|
+
const value = values[valueIdx++];
|
|
10738
11290
|
for (const encoder of encoders) {
|
|
10739
11291
|
if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
|
|
10740
11292
|
encoders = this.expandIndexValues(encoders, segment, value);
|
|
@@ -10785,7 +11337,7 @@ class IndexedDbIndexManager {
|
|
|
10785
11337
|
isInFilter(target, fieldPath) {
|
|
10786
11338
|
return !!target.filters.find(f => f instanceof FieldFilter &&
|
|
10787
11339
|
f.field.isEqual(fieldPath) &&
|
|
10788
|
-
(f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
|
|
11340
|
+
(f.op === "in" /* Operator.IN */ || f.op === "not-in" /* Operator.NOT_IN */));
|
|
10789
11341
|
}
|
|
10790
11342
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10791
11343
|
const indexes = indexConfigurationStore(transaction);
|
|
@@ -10974,24 +11526,36 @@ class IndexedDbIndexManager {
|
|
|
10974
11526
|
bounds.push(upper);
|
|
10975
11527
|
const ranges = [];
|
|
10976
11528
|
for (let i = 0; i < bounds.length; i += 2) {
|
|
10977
|
-
|
|
11529
|
+
// If we encounter two bounds that will create an unmatchable key range,
|
|
11530
|
+
// then we return an empty set of key ranges.
|
|
11531
|
+
if (this.isRangeMatchable(bounds[i], bounds[i + 1])) {
|
|
11532
|
+
return [];
|
|
11533
|
+
}
|
|
11534
|
+
const lowerBound = [
|
|
10978
11535
|
bounds[i].indexId,
|
|
10979
11536
|
this.uid,
|
|
10980
11537
|
bounds[i].arrayValue,
|
|
10981
11538
|
bounds[i].directionalValue,
|
|
10982
11539
|
EMPTY_VALUE,
|
|
10983
11540
|
[]
|
|
10984
|
-
]
|
|
11541
|
+
];
|
|
11542
|
+
const upperBound = [
|
|
10985
11543
|
bounds[i + 1].indexId,
|
|
10986
11544
|
this.uid,
|
|
10987
11545
|
bounds[i + 1].arrayValue,
|
|
10988
11546
|
bounds[i + 1].directionalValue,
|
|
10989
11547
|
EMPTY_VALUE,
|
|
10990
11548
|
[]
|
|
10991
|
-
]
|
|
11549
|
+
];
|
|
11550
|
+
ranges.push(IDBKeyRange.bound(lowerBound, upperBound));
|
|
10992
11551
|
}
|
|
10993
11552
|
return ranges;
|
|
10994
11553
|
}
|
|
11554
|
+
isRangeMatchable(lowerBound, upperBound) {
|
|
11555
|
+
// If lower bound is greater than the upper bound, then the key
|
|
11556
|
+
// range can never be matched.
|
|
11557
|
+
return indexEntryComparator(lowerBound, upperBound) > 0;
|
|
11558
|
+
}
|
|
10995
11559
|
getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
|
|
10996
11560
|
return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
|
|
10997
11561
|
}
|
|
@@ -11968,7 +12532,7 @@ class LruScheduler {
|
|
|
11968
12532
|
}
|
|
11969
12533
|
scheduleGC(delay) {
|
|
11970
12534
|
logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
|
|
11971
|
-
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
|
|
12535
|
+
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* TimerId.LruGarbageCollection */, delay, async () => {
|
|
11972
12536
|
this.gcTask = null;
|
|
11973
12537
|
try {
|
|
11974
12538
|
await this.localStore.collectGarbage(this.garbageCollector);
|
|
@@ -12866,7 +13430,7 @@ class LocalDocumentsView {
|
|
|
12866
13430
|
.getOverlay(transaction, key)
|
|
12867
13431
|
.next(value => {
|
|
12868
13432
|
overlay = value;
|
|
12869
|
-
return this.
|
|
13433
|
+
return this.remoteDocumentCache.getEntry(transaction, key);
|
|
12870
13434
|
})
|
|
12871
13435
|
.next(document => {
|
|
12872
13436
|
if (overlay !== null) {
|
|
@@ -13089,7 +13653,9 @@ class LocalDocumentsView {
|
|
|
13089
13653
|
if (originalDocs.get(key)) {
|
|
13090
13654
|
return PersistencePromise.resolve();
|
|
13091
13655
|
}
|
|
13092
|
-
return this.
|
|
13656
|
+
return this.remoteDocumentCache
|
|
13657
|
+
.getEntry(transaction, key)
|
|
13658
|
+
.next(doc => {
|
|
13093
13659
|
modifiedDocs = modifiedDocs.insert(key, doc);
|
|
13094
13660
|
});
|
|
13095
13661
|
})
|
|
@@ -13163,12 +13729,6 @@ class LocalDocumentsView {
|
|
|
13163
13729
|
return results;
|
|
13164
13730
|
});
|
|
13165
13731
|
}
|
|
13166
|
-
/** Returns a base document that can be used to apply `overlay`. */
|
|
13167
|
-
getBaseDocument(transaction, key, overlay) {
|
|
13168
|
-
return overlay === null || overlay.mutation.type === 1 /* Patch */
|
|
13169
|
-
? this.remoteDocumentCache.getEntry(transaction, key)
|
|
13170
|
-
: PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
|
|
13171
|
-
}
|
|
13172
13732
|
}
|
|
13173
13733
|
|
|
13174
13734
|
/**
|
|
@@ -14918,7 +15478,7 @@ class IndexedDbPersistence {
|
|
|
14918
15478
|
* extend or acquire the primary lease if the client is eligible.
|
|
14919
15479
|
*/
|
|
14920
15480
|
scheduleClientMetadataAndPrimaryLeaseRefreshes() {
|
|
14921
|
-
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
15481
|
+
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* TimerId.ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
14922
15482
|
return this.updateClientMetadataAndTryBecomePrimary()
|
|
14923
15483
|
.then(() => this.maybeGarbageCollectMultiClientState())
|
|
14924
15484
|
.then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());
|
|
@@ -15928,7 +16488,7 @@ function localStoreAllocateTarget(localStore, target) {
|
|
|
15928
16488
|
return localStoreImpl.targetCache
|
|
15929
16489
|
.allocateTargetId(txn)
|
|
15930
16490
|
.next(targetId => {
|
|
15931
|
-
targetData = new TargetData(target, targetId, 0 /* Listen */, txn.currentSequenceNumber);
|
|
16491
|
+
targetData = new TargetData(target, targetId, 0 /* TargetPurpose.Listen */, txn.currentSequenceNumber);
|
|
15932
16492
|
return localStoreImpl.targetCache
|
|
15933
16493
|
.addTargetData(txn, targetData)
|
|
15934
16494
|
.next(() => targetData);
|
|
@@ -16350,11 +16910,11 @@ class QueryEngine {
|
|
|
16350
16910
|
return this.indexManager
|
|
16351
16911
|
.getIndexType(transaction, target)
|
|
16352
16912
|
.next(indexType => {
|
|
16353
|
-
if (indexType === 0 /* NONE */) {
|
|
16913
|
+
if (indexType === 0 /* IndexType.NONE */) {
|
|
16354
16914
|
// The target cannot be served from any index.
|
|
16355
16915
|
return null;
|
|
16356
16916
|
}
|
|
16357
|
-
if (query.limit !== null && indexType === 1 /* PARTIAL */) {
|
|
16917
|
+
if (query.limit !== null && indexType === 1 /* IndexType.PARTIAL */) {
|
|
16358
16918
|
// We cannot apply a limit for targets that are served using a partial
|
|
16359
16919
|
// index. If a partial index will be used to serve the target, the
|
|
16360
16920
|
// query may return a superset of documents that match the target
|
|
@@ -16362,7 +16922,7 @@ class QueryEngine {
|
|
|
16362
16922
|
// may return the correct set of documents in the wrong order (e.g. if
|
|
16363
16923
|
// the index doesn't include a segment for one of the orderBys).
|
|
16364
16924
|
// Therefore, a limit should not be applied in such cases.
|
|
16365
|
-
query = queryWithLimit(query, null, "F" /* First */);
|
|
16925
|
+
query = queryWithLimit(query, null, "F" /* LimitType.First */);
|
|
16366
16926
|
target = queryToTarget(query);
|
|
16367
16927
|
}
|
|
16368
16928
|
return this.indexManager
|
|
@@ -16383,7 +16943,7 @@ class QueryEngine {
|
|
|
16383
16943
|
// query's filters are included in the result set. The SDK
|
|
16384
16944
|
// can then apply the limit once all local edits are
|
|
16385
16945
|
// incorporated.
|
|
16386
|
-
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* First */));
|
|
16946
|
+
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* LimitType.First */));
|
|
16387
16947
|
}
|
|
16388
16948
|
return this.appendRemainingResults(transaction, previousResults, query, offset);
|
|
16389
16949
|
});
|
|
@@ -16462,7 +17022,7 @@ class QueryEngine {
|
|
|
16462
17022
|
// the boundary of the limit itself did not change and documents from cache
|
|
16463
17023
|
// will continue to be "rejected" by this boundary. Therefore, we can ignore
|
|
16464
17024
|
// any modifications that don't affect the last document.
|
|
16465
|
-
const docAtLimitEdge = query.limitType === "F" /* First */
|
|
17025
|
+
const docAtLimitEdge = query.limitType === "F" /* LimitType.First */
|
|
16466
17026
|
? sortedPreviousResults.last()
|
|
16467
17027
|
: sortedPreviousResults.first();
|
|
16468
17028
|
if (!docAtLimitEdge) {
|
|
@@ -17383,7 +17943,7 @@ function nodePromise(action) {
|
|
|
17383
17943
|
*/
|
|
17384
17944
|
// TODO: Fetch runtime version from grpc-js/package.json instead
|
|
17385
17945
|
// when there's a cleaner way to dynamic require JSON in both Node ESM and CJS
|
|
17386
|
-
const grpcVersion = '1.3
|
|
17946
|
+
const grpcVersion = '1.7.3';
|
|
17387
17947
|
const LOG_TAG$9 = 'Connection';
|
|
17388
17948
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
17389
17949
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
@@ -19831,7 +20391,8 @@ const nested = {
|
|
|
19831
20391
|
Operator: {
|
|
19832
20392
|
values: {
|
|
19833
20393
|
OPERATOR_UNSPECIFIED: 0,
|
|
19834
|
-
AND: 1
|
|
20394
|
+
AND: 1,
|
|
20395
|
+
OR: 2
|
|
19835
20396
|
}
|
|
19836
20397
|
}
|
|
19837
20398
|
}
|
|
@@ -20707,7 +21268,7 @@ class PersistentStream {
|
|
|
20707
21268
|
this.authCredentialsProvider = authCredentialsProvider;
|
|
20708
21269
|
this.appCheckCredentialsProvider = appCheckCredentialsProvider;
|
|
20709
21270
|
this.listener = listener;
|
|
20710
|
-
this.state = 0 /* Initial */;
|
|
21271
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20711
21272
|
/**
|
|
20712
21273
|
* A close count that's incremented every time the stream is closed; used by
|
|
20713
21274
|
* getCloseGuardedDispatcher() to invalidate callbacks that happen after
|
|
@@ -20727,8 +21288,8 @@ class PersistentStream {
|
|
|
20727
21288
|
* outbound requests.
|
|
20728
21289
|
*/
|
|
20729
21290
|
isStarted() {
|
|
20730
|
-
return (this.state === 1 /* Starting */ ||
|
|
20731
|
-
this.state === 5 /* Backoff */ ||
|
|
21291
|
+
return (this.state === 1 /* PersistentStreamState.Starting */ ||
|
|
21292
|
+
this.state === 5 /* PersistentStreamState.Backoff */ ||
|
|
20732
21293
|
this.isOpen());
|
|
20733
21294
|
}
|
|
20734
21295
|
/**
|
|
@@ -20736,8 +21297,8 @@ class PersistentStream {
|
|
|
20736
21297
|
* called) and the stream is ready for outbound requests.
|
|
20737
21298
|
*/
|
|
20738
21299
|
isOpen() {
|
|
20739
|
-
return (this.state === 2 /* Open */ ||
|
|
20740
|
-
this.state === 3 /* Healthy */);
|
|
21300
|
+
return (this.state === 2 /* PersistentStreamState.Open */ ||
|
|
21301
|
+
this.state === 3 /* PersistentStreamState.Healthy */);
|
|
20741
21302
|
}
|
|
20742
21303
|
/**
|
|
20743
21304
|
* Starts the RPC. Only allowed if isStarted() returns false. The stream is
|
|
@@ -20747,7 +21308,7 @@ class PersistentStream {
|
|
|
20747
21308
|
* When start returns, isStarted() will return true.
|
|
20748
21309
|
*/
|
|
20749
21310
|
start() {
|
|
20750
|
-
if (this.state === 4 /* Error */) {
|
|
21311
|
+
if (this.state === 4 /* PersistentStreamState.Error */) {
|
|
20751
21312
|
this.performBackoff();
|
|
20752
21313
|
return;
|
|
20753
21314
|
}
|
|
@@ -20761,7 +21322,7 @@ class PersistentStream {
|
|
|
20761
21322
|
*/
|
|
20762
21323
|
async stop() {
|
|
20763
21324
|
if (this.isStarted()) {
|
|
20764
|
-
await this.close(0 /* Initial */);
|
|
21325
|
+
await this.close(0 /* PersistentStreamState.Initial */);
|
|
20765
21326
|
}
|
|
20766
21327
|
}
|
|
20767
21328
|
/**
|
|
@@ -20773,7 +21334,7 @@ class PersistentStream {
|
|
|
20773
21334
|
* inhibit backoff if required.
|
|
20774
21335
|
*/
|
|
20775
21336
|
inhibitBackoff() {
|
|
20776
|
-
this.state = 0 /* Initial */;
|
|
21337
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20777
21338
|
this.backoff.reset();
|
|
20778
21339
|
}
|
|
20779
21340
|
/**
|
|
@@ -20803,7 +21364,7 @@ class PersistentStream {
|
|
|
20803
21364
|
if (this.isOpen()) {
|
|
20804
21365
|
// When timing out an idle stream there's no reason to force the stream into backoff when
|
|
20805
21366
|
// it restarts so set the stream state to Initial instead of Error.
|
|
20806
|
-
return this.close(0 /* Initial */);
|
|
21367
|
+
return this.close(0 /* PersistentStreamState.Initial */);
|
|
20807
21368
|
}
|
|
20808
21369
|
}
|
|
20809
21370
|
/** Marks the stream as active again. */
|
|
@@ -20841,7 +21402,7 @@ class PersistentStream {
|
|
|
20841
21402
|
// Invalidates any stream-related callbacks (e.g. from auth or the
|
|
20842
21403
|
// underlying stream), guaranteeing they won't execute.
|
|
20843
21404
|
this.closeCount++;
|
|
20844
|
-
if (finalState !== 4 /* Error */) {
|
|
21405
|
+
if (finalState !== 4 /* PersistentStreamState.Error */) {
|
|
20845
21406
|
// If this is an intentional close ensure we don't delay our next connection attempt.
|
|
20846
21407
|
this.backoff.reset();
|
|
20847
21408
|
}
|
|
@@ -20853,7 +21414,7 @@ class PersistentStream {
|
|
|
20853
21414
|
}
|
|
20854
21415
|
else if (error &&
|
|
20855
21416
|
error.code === Code.UNAUTHENTICATED &&
|
|
20856
|
-
this.state !== 3 /* Healthy */) {
|
|
21417
|
+
this.state !== 3 /* PersistentStreamState.Healthy */) {
|
|
20857
21418
|
// "unauthenticated" error means the token was rejected. This should rarely
|
|
20858
21419
|
// happen since both Auth and AppCheck ensure a sufficient TTL when we
|
|
20859
21420
|
// request a token. If a user manually resets their system clock this can
|
|
@@ -20881,7 +21442,7 @@ class PersistentStream {
|
|
|
20881
21442
|
*/
|
|
20882
21443
|
tearDown() { }
|
|
20883
21444
|
auth() {
|
|
20884
|
-
this.state = 1 /* Starting */;
|
|
21445
|
+
this.state = 1 /* PersistentStreamState.Starting */;
|
|
20885
21446
|
const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);
|
|
20886
21447
|
// TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.
|
|
20887
21448
|
const closeCount = this.closeCount;
|
|
@@ -20911,10 +21472,10 @@ class PersistentStream {
|
|
|
20911
21472
|
this.stream = this.startRpc(authToken, appCheckToken);
|
|
20912
21473
|
this.stream.onOpen(() => {
|
|
20913
21474
|
dispatchIfNotClosed(() => {
|
|
20914
|
-
this.state = 2 /* Open */;
|
|
21475
|
+
this.state = 2 /* PersistentStreamState.Open */;
|
|
20915
21476
|
this.healthCheck = this.queue.enqueueAfterDelay(this.healthTimerId, HEALTHY_TIMEOUT_MS, () => {
|
|
20916
21477
|
if (this.isOpen()) {
|
|
20917
|
-
this.state = 3 /* Healthy */;
|
|
21478
|
+
this.state = 3 /* PersistentStreamState.Healthy */;
|
|
20918
21479
|
}
|
|
20919
21480
|
return Promise.resolve();
|
|
20920
21481
|
});
|
|
@@ -20933,9 +21494,9 @@ class PersistentStream {
|
|
|
20933
21494
|
});
|
|
20934
21495
|
}
|
|
20935
21496
|
performBackoff() {
|
|
20936
|
-
this.state = 5 /* Backoff */;
|
|
21497
|
+
this.state = 5 /* PersistentStreamState.Backoff */;
|
|
20937
21498
|
this.backoff.backoffAndRun(async () => {
|
|
20938
|
-
this.state = 0 /* Initial */;
|
|
21499
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20939
21500
|
this.start();
|
|
20940
21501
|
});
|
|
20941
21502
|
}
|
|
@@ -20947,7 +21508,7 @@ class PersistentStream {
|
|
|
20947
21508
|
// we never expect this to happen because if we stop a stream ourselves,
|
|
20948
21509
|
// this callback will never be called. To prevent cases where we retry
|
|
20949
21510
|
// without a backoff accidentally, we set the stream to error in all cases.
|
|
20950
|
-
return this.close(4 /* Error */, error);
|
|
21511
|
+
return this.close(4 /* PersistentStreamState.Error */, error);
|
|
20951
21512
|
}
|
|
20952
21513
|
/**
|
|
20953
21514
|
* Returns a "dispatcher" function that dispatches operations onto the
|
|
@@ -20978,7 +21539,7 @@ class PersistentStream {
|
|
|
20978
21539
|
*/
|
|
20979
21540
|
class PersistentListenStream extends PersistentStream {
|
|
20980
21541
|
constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
|
|
20981
|
-
super(queue, "listen_stream_connection_backoff" /* ListenStreamConnectionBackoff */, "listen_stream_idle" /* ListenStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21542
|
+
super(queue, "listen_stream_connection_backoff" /* TimerId.ListenStreamConnectionBackoff */, "listen_stream_idle" /* TimerId.ListenStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
20982
21543
|
this.serializer = serializer;
|
|
20983
21544
|
}
|
|
20984
21545
|
startRpc(authToken, appCheckToken) {
|
|
@@ -21037,7 +21598,7 @@ class PersistentListenStream extends PersistentStream {
|
|
|
21037
21598
|
*/
|
|
21038
21599
|
class PersistentWriteStream extends PersistentStream {
|
|
21039
21600
|
constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
|
|
21040
|
-
super(queue, "write_stream_connection_backoff" /* WriteStreamConnectionBackoff */, "write_stream_idle" /* WriteStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21601
|
+
super(queue, "write_stream_connection_backoff" /* TimerId.WriteStreamConnectionBackoff */, "write_stream_idle" /* TimerId.WriteStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21041
21602
|
this.serializer = serializer;
|
|
21042
21603
|
this.handshakeComplete_ = false;
|
|
21043
21604
|
}
|
|
@@ -21296,7 +21857,7 @@ class OnlineStateTracker {
|
|
|
21296
21857
|
this.asyncQueue = asyncQueue;
|
|
21297
21858
|
this.onlineStateHandler = onlineStateHandler;
|
|
21298
21859
|
/** The current OnlineState. */
|
|
21299
|
-
this.state = "Unknown" /* Unknown */;
|
|
21860
|
+
this.state = "Unknown" /* OnlineState.Unknown */;
|
|
21300
21861
|
/**
|
|
21301
21862
|
* A count of consecutive failures to open the stream. If it reaches the
|
|
21302
21863
|
* maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to
|
|
@@ -21325,12 +21886,12 @@ class OnlineStateTracker {
|
|
|
21325
21886
|
*/
|
|
21326
21887
|
handleWatchStreamStart() {
|
|
21327
21888
|
if (this.watchStreamFailures === 0) {
|
|
21328
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21329
|
-
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21889
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21890
|
+
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* TimerId.OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21330
21891
|
this.onlineStateTimer = null;
|
|
21331
21892
|
this.logClientOfflineWarningIfNecessary(`Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +
|
|
21332
21893
|
`seconds.`);
|
|
21333
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21894
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21334
21895
|
// NOTE: handleWatchStreamFailure() will continue to increment
|
|
21335
21896
|
// watchStreamFailures even though we are already marked Offline,
|
|
21336
21897
|
// but this is non-harmful.
|
|
@@ -21345,8 +21906,8 @@ class OnlineStateTracker {
|
|
|
21345
21906
|
* actually transition to the 'Offline' state.
|
|
21346
21907
|
*/
|
|
21347
21908
|
handleWatchStreamFailure(error) {
|
|
21348
|
-
if (this.state === "Online" /* Online */) {
|
|
21349
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21909
|
+
if (this.state === "Online" /* OnlineState.Online */) {
|
|
21910
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21350
21911
|
}
|
|
21351
21912
|
else {
|
|
21352
21913
|
this.watchStreamFailures++;
|
|
@@ -21354,7 +21915,7 @@ class OnlineStateTracker {
|
|
|
21354
21915
|
this.clearOnlineStateTimer();
|
|
21355
21916
|
this.logClientOfflineWarningIfNecessary(`Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +
|
|
21356
21917
|
`times. Most recent error: ${error.toString()}`);
|
|
21357
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21918
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21358
21919
|
}
|
|
21359
21920
|
}
|
|
21360
21921
|
}
|
|
@@ -21368,7 +21929,7 @@ class OnlineStateTracker {
|
|
|
21368
21929
|
set(newState) {
|
|
21369
21930
|
this.clearOnlineStateTimer();
|
|
21370
21931
|
this.watchStreamFailures = 0;
|
|
21371
|
-
if (newState === "Online" /* Online */) {
|
|
21932
|
+
if (newState === "Online" /* OnlineState.Online */) {
|
|
21372
21933
|
// We've connected to watch at least once. Don't warn the developer
|
|
21373
21934
|
// about being offline going forward.
|
|
21374
21935
|
this.shouldWarnClientIsOffline = false;
|
|
@@ -21495,7 +22056,7 @@ function newRemoteStore(localStore, datastore, asyncQueue, onlineStateHandler, c
|
|
|
21495
22056
|
/** Re-enables the network. Idempotent. */
|
|
21496
22057
|
function remoteStoreEnableNetwork(remoteStore) {
|
|
21497
22058
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21498
|
-
remoteStoreImpl.offlineCauses.delete(0 /* UserDisabled */);
|
|
22059
|
+
remoteStoreImpl.offlineCauses.delete(0 /* OfflineCause.UserDisabled */);
|
|
21499
22060
|
return enableNetworkInternal(remoteStoreImpl);
|
|
21500
22061
|
}
|
|
21501
22062
|
async function enableNetworkInternal(remoteStoreImpl) {
|
|
@@ -21511,10 +22072,10 @@ async function enableNetworkInternal(remoteStoreImpl) {
|
|
|
21511
22072
|
*/
|
|
21512
22073
|
async function remoteStoreDisableNetwork(remoteStore) {
|
|
21513
22074
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21514
|
-
remoteStoreImpl.offlineCauses.add(0 /* UserDisabled */);
|
|
22075
|
+
remoteStoreImpl.offlineCauses.add(0 /* OfflineCause.UserDisabled */);
|
|
21515
22076
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21516
22077
|
// Set the OnlineState to Offline so get()s return from cache, etc.
|
|
21517
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22078
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21518
22079
|
}
|
|
21519
22080
|
async function disableNetworkInternal(remoteStoreImpl) {
|
|
21520
22081
|
for (const networkStatusHandler of remoteStoreImpl.onNetworkStatusChange) {
|
|
@@ -21524,12 +22085,12 @@ async function disableNetworkInternal(remoteStoreImpl) {
|
|
|
21524
22085
|
async function remoteStoreShutdown(remoteStore) {
|
|
21525
22086
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21526
22087
|
logDebug(LOG_TAG$5, 'RemoteStore shutting down.');
|
|
21527
|
-
remoteStoreImpl.offlineCauses.add(5 /* Shutdown */);
|
|
22088
|
+
remoteStoreImpl.offlineCauses.add(5 /* OfflineCause.Shutdown */);
|
|
21528
22089
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21529
22090
|
remoteStoreImpl.connectivityMonitor.shutdown();
|
|
21530
22091
|
// Set the OnlineState to Unknown (rather than Offline) to avoid potentially
|
|
21531
22092
|
// triggering spurious listener events with cached data, etc.
|
|
21532
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22093
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21533
22094
|
}
|
|
21534
22095
|
/**
|
|
21535
22096
|
* Starts new listen for the given target. Uses resume token if provided. It
|
|
@@ -21569,7 +22130,7 @@ function remoteStoreUnlisten(remoteStore, targetId) {
|
|
|
21569
22130
|
// Revert to OnlineState.Unknown if the watch stream is not open and we
|
|
21570
22131
|
// have no listeners, since without any listens to send we cannot
|
|
21571
22132
|
// confirm if the stream is healthy and upgrade to OnlineState.Online.
|
|
21572
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22133
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21573
22134
|
}
|
|
21574
22135
|
}
|
|
21575
22136
|
}
|
|
@@ -21630,14 +22191,14 @@ async function onWatchStreamClose(remoteStoreImpl, error) {
|
|
|
21630
22191
|
// No need to restart watch stream because there are no active targets.
|
|
21631
22192
|
// The online state is set to unknown because there is no active attempt
|
|
21632
22193
|
// at establishing a connection
|
|
21633
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22194
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21634
22195
|
}
|
|
21635
22196
|
}
|
|
21636
22197
|
async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion) {
|
|
21637
22198
|
// Mark the client as online since we got a message from the server
|
|
21638
|
-
remoteStoreImpl.onlineStateTracker.set("Online" /* Online */);
|
|
22199
|
+
remoteStoreImpl.onlineStateTracker.set("Online" /* OnlineState.Online */);
|
|
21639
22200
|
if (watchChange instanceof WatchTargetChange &&
|
|
21640
|
-
watchChange.state === 2 /* Removed */ &&
|
|
22201
|
+
watchChange.state === 2 /* WatchTargetChangeState.Removed */ &&
|
|
21641
22202
|
watchChange.cause) {
|
|
21642
22203
|
// There was an error on a target, don't wait for a consistent snapshot
|
|
21643
22204
|
// to raise events
|
|
@@ -21685,10 +22246,10 @@ async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion
|
|
|
21685
22246
|
*/
|
|
21686
22247
|
async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
21687
22248
|
if (isIndexedDbTransactionError(e)) {
|
|
21688
|
-
remoteStoreImpl.offlineCauses.add(1 /* IndexedDbFailed */);
|
|
22249
|
+
remoteStoreImpl.offlineCauses.add(1 /* OfflineCause.IndexedDbFailed */);
|
|
21689
22250
|
// Disable network and raise offline snapshots
|
|
21690
22251
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21691
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22252
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21692
22253
|
if (!op) {
|
|
21693
22254
|
// Use a simple read operation to determine if IndexedDB recovered.
|
|
21694
22255
|
// Ideally, we would expose a health check directly on SimpleDb, but
|
|
@@ -21699,7 +22260,7 @@ async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
|
21699
22260
|
remoteStoreImpl.asyncQueue.enqueueRetryable(async () => {
|
|
21700
22261
|
logDebug(LOG_TAG$5, 'Retrying IndexedDB access');
|
|
21701
22262
|
await op();
|
|
21702
|
-
remoteStoreImpl.offlineCauses.delete(1 /* IndexedDbFailed */);
|
|
22263
|
+
remoteStoreImpl.offlineCauses.delete(1 /* OfflineCause.IndexedDbFailed */);
|
|
21703
22264
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21704
22265
|
});
|
|
21705
22266
|
}
|
|
@@ -21750,7 +22311,7 @@ function raiseWatchSnapshot(remoteStoreImpl, snapshotVersion) {
|
|
|
21750
22311
|
// mismatch, but don't actually retain that in listenTargets. This ensures
|
|
21751
22312
|
// that we flag the first re-listen this way without impacting future
|
|
21752
22313
|
// listens of this target (that might happen e.g. on reconnect).
|
|
21753
|
-
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
22314
|
+
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* TargetPurpose.ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
21754
22315
|
sendWatchRequest(remoteStoreImpl, requestTargetData);
|
|
21755
22316
|
});
|
|
21756
22317
|
return remoteStoreImpl.remoteSyncer.applyRemoteEvent(remoteEvent);
|
|
@@ -21881,10 +22442,10 @@ async function handleWriteError(remoteStoreImpl, error) {
|
|
|
21881
22442
|
}
|
|
21882
22443
|
async function restartNetwork(remoteStore) {
|
|
21883
22444
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21884
|
-
remoteStoreImpl.offlineCauses.add(4 /* ConnectivityChange */);
|
|
22445
|
+
remoteStoreImpl.offlineCauses.add(4 /* OfflineCause.ConnectivityChange */);
|
|
21885
22446
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21886
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
21887
|
-
remoteStoreImpl.offlineCauses.delete(4 /* ConnectivityChange */);
|
|
22447
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
22448
|
+
remoteStoreImpl.offlineCauses.delete(4 /* OfflineCause.ConnectivityChange */);
|
|
21888
22449
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21889
22450
|
}
|
|
21890
22451
|
async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
@@ -21895,14 +22456,14 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21895
22456
|
// Tear down and re-create our network streams. This will ensure we get a
|
|
21896
22457
|
// fresh auth token for the new user and re-fill the write pipeline with
|
|
21897
22458
|
// new mutations from the LocalStore (since mutations are per-user).
|
|
21898
|
-
remoteStoreImpl.offlineCauses.add(3 /* CredentialChange */);
|
|
22459
|
+
remoteStoreImpl.offlineCauses.add(3 /* OfflineCause.CredentialChange */);
|
|
21899
22460
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21900
22461
|
if (usesNetwork) {
|
|
21901
22462
|
// Don't set the network status to Unknown if we are offline.
|
|
21902
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22463
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21903
22464
|
}
|
|
21904
22465
|
await remoteStoreImpl.remoteSyncer.handleCredentialChange(user);
|
|
21905
|
-
remoteStoreImpl.offlineCauses.delete(3 /* CredentialChange */);
|
|
22466
|
+
remoteStoreImpl.offlineCauses.delete(3 /* OfflineCause.CredentialChange */);
|
|
21906
22467
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21907
22468
|
}
|
|
21908
22469
|
/**
|
|
@@ -21911,13 +22472,13 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21911
22472
|
async function remoteStoreApplyPrimaryState(remoteStore, isPrimary) {
|
|
21912
22473
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21913
22474
|
if (isPrimary) {
|
|
21914
|
-
remoteStoreImpl.offlineCauses.delete(2 /* IsSecondary */);
|
|
22475
|
+
remoteStoreImpl.offlineCauses.delete(2 /* OfflineCause.IsSecondary */);
|
|
21915
22476
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21916
22477
|
}
|
|
21917
22478
|
else if (!isPrimary) {
|
|
21918
|
-
remoteStoreImpl.offlineCauses.add(2 /* IsSecondary */);
|
|
22479
|
+
remoteStoreImpl.offlineCauses.add(2 /* OfflineCause.IsSecondary */);
|
|
21919
22480
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21920
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22481
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21921
22482
|
}
|
|
21922
22483
|
}
|
|
21923
22484
|
/**
|
|
@@ -21943,7 +22504,7 @@ function ensureWatchStream(remoteStoreImpl) {
|
|
|
21943
22504
|
startWatchStream(remoteStoreImpl);
|
|
21944
22505
|
}
|
|
21945
22506
|
else {
|
|
21946
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22507
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21947
22508
|
}
|
|
21948
22509
|
}
|
|
21949
22510
|
else {
|
|
@@ -22274,46 +22835,46 @@ class DocumentChangeSet {
|
|
|
22274
22835
|
return;
|
|
22275
22836
|
}
|
|
22276
22837
|
// Merge the new change with the existing change.
|
|
22277
|
-
if (change.type !== 0 /* Added */ &&
|
|
22278
|
-
oldChange.type === 3 /* Metadata */) {
|
|
22838
|
+
if (change.type !== 0 /* ChangeType.Added */ &&
|
|
22839
|
+
oldChange.type === 3 /* ChangeType.Metadata */) {
|
|
22279
22840
|
this.changeMap = this.changeMap.insert(key, change);
|
|
22280
22841
|
}
|
|
22281
|
-
else if (change.type === 3 /* Metadata */ &&
|
|
22282
|
-
oldChange.type !== 1 /* Removed */) {
|
|
22842
|
+
else if (change.type === 3 /* ChangeType.Metadata */ &&
|
|
22843
|
+
oldChange.type !== 1 /* ChangeType.Removed */) {
|
|
22283
22844
|
this.changeMap = this.changeMap.insert(key, {
|
|
22284
22845
|
type: oldChange.type,
|
|
22285
22846
|
doc: change.doc
|
|
22286
22847
|
});
|
|
22287
22848
|
}
|
|
22288
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22289
|
-
oldChange.type === 2 /* Modified */) {
|
|
22849
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22850
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
22290
22851
|
this.changeMap = this.changeMap.insert(key, {
|
|
22291
|
-
type: 2 /* Modified */,
|
|
22852
|
+
type: 2 /* ChangeType.Modified */,
|
|
22292
22853
|
doc: change.doc
|
|
22293
22854
|
});
|
|
22294
22855
|
}
|
|
22295
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22296
|
-
oldChange.type === 0 /* Added */) {
|
|
22856
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22857
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22297
22858
|
this.changeMap = this.changeMap.insert(key, {
|
|
22298
|
-
type: 0 /* Added */,
|
|
22859
|
+
type: 0 /* ChangeType.Added */,
|
|
22299
22860
|
doc: change.doc
|
|
22300
22861
|
});
|
|
22301
22862
|
}
|
|
22302
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22303
|
-
oldChange.type === 0 /* Added */) {
|
|
22863
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22864
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22304
22865
|
this.changeMap = this.changeMap.remove(key);
|
|
22305
22866
|
}
|
|
22306
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22307
|
-
oldChange.type === 2 /* Modified */) {
|
|
22867
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22868
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
22308
22869
|
this.changeMap = this.changeMap.insert(key, {
|
|
22309
|
-
type: 1 /* Removed */,
|
|
22870
|
+
type: 1 /* ChangeType.Removed */,
|
|
22310
22871
|
doc: oldChange.doc
|
|
22311
22872
|
});
|
|
22312
22873
|
}
|
|
22313
|
-
else if (change.type === 0 /* Added */ &&
|
|
22314
|
-
oldChange.type === 1 /* Removed */) {
|
|
22874
|
+
else if (change.type === 0 /* ChangeType.Added */ &&
|
|
22875
|
+
oldChange.type === 1 /* ChangeType.Removed */) {
|
|
22315
22876
|
this.changeMap = this.changeMap.insert(key, {
|
|
22316
|
-
type: 2 /* Modified */,
|
|
22877
|
+
type: 2 /* ChangeType.Modified */,
|
|
22317
22878
|
doc: change.doc
|
|
22318
22879
|
});
|
|
22319
22880
|
}
|
|
@@ -22352,7 +22913,7 @@ class ViewSnapshot {
|
|
|
22352
22913
|
static fromInitialDocuments(query, documents, mutatedKeys, fromCache, hasCachedResults) {
|
|
22353
22914
|
const changes = [];
|
|
22354
22915
|
documents.forEach(doc => {
|
|
22355
|
-
changes.push({ type: 0 /* Added */, doc });
|
|
22916
|
+
changes.push({ type: 0 /* ChangeType.Added */, doc });
|
|
22356
22917
|
});
|
|
22357
22918
|
return new ViewSnapshot(query, documents, DocumentSet.emptySet(documents), changes, mutatedKeys, fromCache,
|
|
22358
22919
|
/* syncStateChanged= */ true,
|
|
@@ -22418,7 +22979,7 @@ function newEventManager() {
|
|
|
22418
22979
|
class EventManagerImpl {
|
|
22419
22980
|
constructor() {
|
|
22420
22981
|
this.queries = new ObjectMap(q => canonifyQuery(q), queryEquals);
|
|
22421
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
22982
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22422
22983
|
this.snapshotsInSyncListeners = new Set();
|
|
22423
22984
|
}
|
|
22424
22985
|
}
|
|
@@ -22549,7 +23110,7 @@ class QueryListener {
|
|
|
22549
23110
|
*/
|
|
22550
23111
|
this.raisedInitialEvent = false;
|
|
22551
23112
|
this.snap = null;
|
|
22552
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23113
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22553
23114
|
this.options = options || {};
|
|
22554
23115
|
}
|
|
22555
23116
|
/**
|
|
@@ -22563,7 +23124,7 @@ class QueryListener {
|
|
|
22563
23124
|
// Remove the metadata only changes.
|
|
22564
23125
|
const docChanges = [];
|
|
22565
23126
|
for (const docChange of snap.docChanges) {
|
|
22566
|
-
if (docChange.type !== 3 /* Metadata */) {
|
|
23127
|
+
if (docChange.type !== 3 /* ChangeType.Metadata */) {
|
|
22567
23128
|
docChanges.push(docChange);
|
|
22568
23129
|
}
|
|
22569
23130
|
}
|
|
@@ -22606,7 +23167,7 @@ class QueryListener {
|
|
|
22606
23167
|
}
|
|
22607
23168
|
// NOTE: We consider OnlineState.Unknown as online (it should become Offline
|
|
22608
23169
|
// or Online if we wait long enough).
|
|
22609
|
-
const maybeOnline = onlineState !== "Offline" /* Offline */;
|
|
23170
|
+
const maybeOnline = onlineState !== "Offline" /* OnlineState.Offline */;
|
|
22610
23171
|
// Don't raise the event if we're online, aren't synced yet (checked
|
|
22611
23172
|
// above) and are waiting for a sync.
|
|
22612
23173
|
if (this.options.waitForSyncWhenOnline && maybeOnline) {
|
|
@@ -22616,7 +23177,7 @@ class QueryListener {
|
|
|
22616
23177
|
// or we are offline.
|
|
22617
23178
|
return (!snap.docs.isEmpty() ||
|
|
22618
23179
|
snap.hasCachedResults ||
|
|
22619
|
-
onlineState === "Offline" /* Offline */);
|
|
23180
|
+
onlineState === "Offline" /* OnlineState.Offline */);
|
|
22620
23181
|
}
|
|
22621
23182
|
shouldRaiseEvent(snap) {
|
|
22622
23183
|
// We don't need to handle includeDocumentMetadataChanges here because
|
|
@@ -22675,10 +23236,10 @@ class LocalViewChanges {
|
|
|
22675
23236
|
let removedKeys = documentKeySet();
|
|
22676
23237
|
for (const docChange of viewSnapshot.docChanges) {
|
|
22677
23238
|
switch (docChange.type) {
|
|
22678
|
-
case 0 /* Added */:
|
|
23239
|
+
case 0 /* ChangeType.Added */:
|
|
22679
23240
|
addedKeys = addedKeys.add(docChange.doc.key);
|
|
22680
23241
|
break;
|
|
22681
|
-
case 1 /* Removed */:
|
|
23242
|
+
case 1 /* ChangeType.Removed */:
|
|
22682
23243
|
removedKeys = removedKeys.add(docChange.doc.key);
|
|
22683
23244
|
break;
|
|
22684
23245
|
// do nothing
|
|
@@ -22926,11 +23487,11 @@ class View {
|
|
|
22926
23487
|
//
|
|
22927
23488
|
// Note that this should never get used in a refill (when previousChanges is
|
|
22928
23489
|
// set), because there will only be adds -- no deletes or updates.
|
|
22929
|
-
const lastDocInLimit = this.query.limitType === "F" /* First */ &&
|
|
23490
|
+
const lastDocInLimit = this.query.limitType === "F" /* LimitType.First */ &&
|
|
22930
23491
|
oldDocumentSet.size === this.query.limit
|
|
22931
23492
|
? oldDocumentSet.last()
|
|
22932
23493
|
: null;
|
|
22933
|
-
const firstDocInLimit = this.query.limitType === "L" /* Last */ &&
|
|
23494
|
+
const firstDocInLimit = this.query.limitType === "L" /* LimitType.Last */ &&
|
|
22934
23495
|
oldDocumentSet.size === this.query.limit
|
|
22935
23496
|
? oldDocumentSet.first()
|
|
22936
23497
|
: null;
|
|
@@ -22953,7 +23514,7 @@ class View {
|
|
|
22953
23514
|
if (!docsEqual) {
|
|
22954
23515
|
if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {
|
|
22955
23516
|
changeSet.track({
|
|
22956
|
-
type: 2 /* Modified */,
|
|
23517
|
+
type: 2 /* ChangeType.Modified */,
|
|
22957
23518
|
doc: newDoc
|
|
22958
23519
|
});
|
|
22959
23520
|
changeApplied = true;
|
|
@@ -22969,16 +23530,16 @@ class View {
|
|
|
22969
23530
|
}
|
|
22970
23531
|
}
|
|
22971
23532
|
else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {
|
|
22972
|
-
changeSet.track({ type: 3 /* Metadata */, doc: newDoc });
|
|
23533
|
+
changeSet.track({ type: 3 /* ChangeType.Metadata */, doc: newDoc });
|
|
22973
23534
|
changeApplied = true;
|
|
22974
23535
|
}
|
|
22975
23536
|
}
|
|
22976
23537
|
else if (!oldDoc && newDoc) {
|
|
22977
|
-
changeSet.track({ type: 0 /* Added */, doc: newDoc });
|
|
23538
|
+
changeSet.track({ type: 0 /* ChangeType.Added */, doc: newDoc });
|
|
22978
23539
|
changeApplied = true;
|
|
22979
23540
|
}
|
|
22980
23541
|
else if (oldDoc && !newDoc) {
|
|
22981
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23542
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
22982
23543
|
changeApplied = true;
|
|
22983
23544
|
if (lastDocInLimit || firstDocInLimit) {
|
|
22984
23545
|
// A doc was removed from a full limit query. We'll need to
|
|
@@ -23006,12 +23567,12 @@ class View {
|
|
|
23006
23567
|
// Drop documents out to meet limit/limitToLast requirement.
|
|
23007
23568
|
if (this.query.limit !== null) {
|
|
23008
23569
|
while (newDocumentSet.size > this.query.limit) {
|
|
23009
|
-
const oldDoc = this.query.limitType === "F" /* First */
|
|
23570
|
+
const oldDoc = this.query.limitType === "F" /* LimitType.First */
|
|
23010
23571
|
? newDocumentSet.last()
|
|
23011
23572
|
: newDocumentSet.first();
|
|
23012
23573
|
newDocumentSet = newDocumentSet.delete(oldDoc.key);
|
|
23013
23574
|
newMutatedKeys = newMutatedKeys.delete(oldDoc.key);
|
|
23014
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23575
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
23015
23576
|
}
|
|
23016
23577
|
}
|
|
23017
23578
|
return {
|
|
@@ -23059,7 +23620,7 @@ class View {
|
|
|
23059
23620
|
? this.updateLimboDocuments()
|
|
23060
23621
|
: [];
|
|
23061
23622
|
const synced = this.limboDocuments.size === 0 && this.current;
|
|
23062
|
-
const newSyncState = synced ? 1 /* Synced */ : 0 /* Local */;
|
|
23623
|
+
const newSyncState = synced ? 1 /* SyncState.Synced */ : 0 /* SyncState.Local */;
|
|
23063
23624
|
const syncStateChanged = newSyncState !== this.syncState;
|
|
23064
23625
|
this.syncState = newSyncState;
|
|
23065
23626
|
if (changes.length === 0 && !syncStateChanged) {
|
|
@@ -23067,7 +23628,7 @@ class View {
|
|
|
23067
23628
|
return { limboChanges };
|
|
23068
23629
|
}
|
|
23069
23630
|
else {
|
|
23070
|
-
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* Local */, syncStateChanged,
|
|
23631
|
+
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* SyncState.Local */, syncStateChanged,
|
|
23071
23632
|
/* excludesMetadataChanges= */ false, targetChange
|
|
23072
23633
|
? targetChange.resumeToken.approximateByteSize() > 0
|
|
23073
23634
|
: false);
|
|
@@ -23082,7 +23643,7 @@ class View {
|
|
|
23082
23643
|
* ViewChange if the view's syncState changes as a result.
|
|
23083
23644
|
*/
|
|
23084
23645
|
applyOnlineStateChange(onlineState) {
|
|
23085
|
-
if (this.current && onlineState === "Offline" /* Offline */) {
|
|
23646
|
+
if (this.current && onlineState === "Offline" /* OnlineState.Offline */) {
|
|
23086
23647
|
// If we're offline, set `current` to false and then call applyChanges()
|
|
23087
23648
|
// to refresh our syncState and generate a ViewChange as appropriate. We
|
|
23088
23649
|
// are guaranteed to get a new TargetChange that sets `current` back to
|
|
@@ -23197,22 +23758,22 @@ class View {
|
|
|
23197
23758
|
*/
|
|
23198
23759
|
// PORTING NOTE: Multi-tab only.
|
|
23199
23760
|
computeInitialSnapshot() {
|
|
23200
|
-
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* Local */, this.hasCachedResults);
|
|
23761
|
+
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* SyncState.Local */, this.hasCachedResults);
|
|
23201
23762
|
}
|
|
23202
23763
|
}
|
|
23203
23764
|
function compareChangeType(c1, c2) {
|
|
23204
23765
|
const order = (change) => {
|
|
23205
23766
|
switch (change) {
|
|
23206
|
-
case 0 /* Added */:
|
|
23767
|
+
case 0 /* ChangeType.Added */:
|
|
23207
23768
|
return 1;
|
|
23208
|
-
case 2 /* Modified */:
|
|
23769
|
+
case 2 /* ChangeType.Modified */:
|
|
23209
23770
|
return 2;
|
|
23210
|
-
case 3 /* Metadata */:
|
|
23771
|
+
case 3 /* ChangeType.Metadata */:
|
|
23211
23772
|
// A metadata change is converted to a modified change at the public
|
|
23212
23773
|
// api layer. Since we sort by document key and then change type,
|
|
23213
23774
|
// metadata and modified changes must be sorted equivalently.
|
|
23214
23775
|
return 2;
|
|
23215
|
-
case 1 /* Removed */:
|
|
23776
|
+
case 1 /* ChangeType.Removed */:
|
|
23216
23777
|
return 0;
|
|
23217
23778
|
default:
|
|
23218
23779
|
return fail();
|
|
@@ -23330,7 +23891,7 @@ class SyncEngineImpl {
|
|
|
23330
23891
|
/** Stores user callbacks waiting for all pending writes to be acknowledged. */
|
|
23331
23892
|
this.pendingWritesCallbacks = new Map();
|
|
23332
23893
|
this.limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();
|
|
23333
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23894
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
23334
23895
|
// The primary state is set to `true` or `false` immediately after Firestore
|
|
23335
23896
|
// startup. In the interim, a client should only be considered primary if
|
|
23336
23897
|
// `isPrimary` is true.
|
|
@@ -23394,7 +23955,7 @@ async function initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId,
|
|
|
23394
23955
|
/* usePreviousResults= */ true);
|
|
23395
23956
|
const view = new View(query, queryResult.remoteKeys);
|
|
23396
23957
|
const viewDocChanges = view.computeDocChanges(queryResult.documents);
|
|
23397
|
-
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* Offline */, resumeToken);
|
|
23958
|
+
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* OnlineState.Offline */, resumeToken);
|
|
23398
23959
|
const viewChange = view.applyChanges(viewDocChanges,
|
|
23399
23960
|
/* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, synthesizedTargetChange);
|
|
23400
23961
|
updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
|
|
@@ -23520,9 +24081,9 @@ function syncEngineApplyOnlineStateChange(syncEngine, onlineState, source) {
|
|
|
23520
24081
|
// tab remains online) and only apply the primary tab's online state from
|
|
23521
24082
|
// SharedClientState.
|
|
23522
24083
|
if ((syncEngineImpl.isPrimaryClient &&
|
|
23523
|
-
source === 0 /* RemoteStore */) ||
|
|
24084
|
+
source === 0 /* OnlineStateSource.RemoteStore */) ||
|
|
23524
24085
|
(!syncEngineImpl.isPrimaryClient &&
|
|
23525
|
-
source === 1 /* SharedClientState */)) {
|
|
24086
|
+
source === 1 /* OnlineStateSource.SharedClientState */)) {
|
|
23526
24087
|
const newViewSnapshots = [];
|
|
23527
24088
|
syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => {
|
|
23528
24089
|
const viewChange = queryView.view.applyOnlineStateChange(onlineState);
|
|
@@ -23790,7 +24351,7 @@ function pumpEnqueuedLimboResolutions(syncEngineImpl) {
|
|
|
23790
24351
|
syncEngineImpl.activeLimboResolutionsByTarget.set(limboTargetId, new LimboResolution(key));
|
|
23791
24352
|
syncEngineImpl.activeLimboTargetsByKey =
|
|
23792
24353
|
syncEngineImpl.activeLimboTargetsByKey.insert(key, limboTargetId);
|
|
23793
|
-
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* LimboResolution */, ListenSequence.INVALID));
|
|
24354
|
+
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* TargetPurpose.LimboResolution */, ListenSequence.INVALID));
|
|
23794
24355
|
}
|
|
23795
24356
|
}
|
|
23796
24357
|
async function syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, changes, remoteEvent) {
|
|
@@ -24043,7 +24604,7 @@ async function synchronizeQueryViewsAndRaiseSnapshots(syncEngine, targets, trans
|
|
|
24043
24604
|
*/
|
|
24044
24605
|
// PORTING NOTE: Multi-Tab only.
|
|
24045
24606
|
function synthesizeTargetToQuery(target) {
|
|
24046
|
-
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* First */, target.startAt, target.endAt);
|
|
24607
|
+
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* LimitType.First */, target.startAt, target.endAt);
|
|
24047
24608
|
}
|
|
24048
24609
|
/** Returns the IDs of the clients that are currently active. */
|
|
24049
24610
|
// PORTING NOTE: Multi-Tab only.
|
|
@@ -24374,7 +24935,7 @@ class OnlineComponentProvider {
|
|
|
24374
24935
|
this.eventManager = this.createEventManager(cfg);
|
|
24375
24936
|
this.syncEngine = this.createSyncEngine(cfg,
|
|
24376
24937
|
/* startAsPrimary=*/ !offlineComponentProvider.synchronizeTabs);
|
|
24377
|
-
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* SharedClientState */);
|
|
24938
|
+
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* OnlineStateSource.SharedClientState */);
|
|
24378
24939
|
this.remoteStore.remoteSyncer.handleCredentialChange =
|
|
24379
24940
|
syncEngineHandleCredentialChange.bind(null, this.syncEngine);
|
|
24380
24941
|
await remoteStoreApplyPrimaryState(this.remoteStore, this.syncEngine.isPrimaryClient);
|
|
@@ -24388,7 +24949,7 @@ class OnlineComponentProvider {
|
|
|
24388
24949
|
return newDatastore(cfg.authCredentials, cfg.appCheckCredentials, connection, serializer);
|
|
24389
24950
|
}
|
|
24390
24951
|
createRemoteStore(cfg) {
|
|
24391
|
-
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* RemoteStore */), newConnectivityMonitor());
|
|
24952
|
+
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* OnlineStateSource.RemoteStore */), newConnectivityMonitor());
|
|
24392
24953
|
}
|
|
24393
24954
|
createSyncEngine(cfg, startAsPrimary) {
|
|
24394
24955
|
return newSyncEngine(this.localStore, this.remoteStore, this.eventManager, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, startAsPrimary);
|
|
@@ -25145,7 +25706,7 @@ class TransactionRunner {
|
|
|
25145
25706
|
this.updateFunction = updateFunction;
|
|
25146
25707
|
this.deferred = deferred;
|
|
25147
25708
|
this.attemptsRemaining = options.maxAttempts;
|
|
25148
|
-
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
|
|
25709
|
+
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
|
|
25149
25710
|
}
|
|
25150
25711
|
/** Runs the transaction and sets the result on deferred. */
|
|
25151
25712
|
run() {
|
|
@@ -26178,7 +26739,7 @@ class AsyncQueueImpl {
|
|
|
26178
26739
|
// List of TimerIds to fast-forward delays for.
|
|
26179
26740
|
this.timerIdsToSkip = [];
|
|
26180
26741
|
// Backoff timer used to schedule retries for retryable operations
|
|
26181
|
-
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
|
|
26742
|
+
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
|
|
26182
26743
|
// Visibility handler that triggers an immediate retry of all retryable
|
|
26183
26744
|
// operations. Meant to speed up recovery when we regain file system access
|
|
26184
26745
|
// after page comes into foreground.
|
|
@@ -26348,7 +26909,7 @@ class AsyncQueueImpl {
|
|
|
26348
26909
|
this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
|
|
26349
26910
|
for (const op of this.delayedOperations) {
|
|
26350
26911
|
op.skipDelay();
|
|
26351
|
-
if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
|
|
26912
|
+
if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
|
|
26352
26913
|
break;
|
|
26353
26914
|
}
|
|
26354
26915
|
}
|
|
@@ -27261,12 +27822,12 @@ class ParsedUpdateData {
|
|
|
27261
27822
|
}
|
|
27262
27823
|
function isWrite(dataSource) {
|
|
27263
27824
|
switch (dataSource) {
|
|
27264
|
-
case 0 /* Set */: // fall through
|
|
27265
|
-
case 2 /* MergeSet */: // fall through
|
|
27266
|
-
case 1 /* Update */:
|
|
27825
|
+
case 0 /* UserDataSource.Set */: // fall through
|
|
27826
|
+
case 2 /* UserDataSource.MergeSet */: // fall through
|
|
27827
|
+
case 1 /* UserDataSource.Update */:
|
|
27267
27828
|
return true;
|
|
27268
|
-
case 3 /* Argument */:
|
|
27269
|
-
case 4 /* ArrayArgument */:
|
|
27829
|
+
case 3 /* UserDataSource.Argument */:
|
|
27830
|
+
case 4 /* UserDataSource.ArrayArgument */:
|
|
27270
27831
|
return false;
|
|
27271
27832
|
default:
|
|
27272
27833
|
throw fail();
|
|
@@ -27391,8 +27952,8 @@ function newUserDataReader(firestore) {
|
|
|
27391
27952
|
/** Parse document data from a set() call. */
|
|
27392
27953
|
function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
|
|
27393
27954
|
const context = userDataReader.createContext(options.merge || options.mergeFields
|
|
27394
|
-
? 2 /* MergeSet */
|
|
27395
|
-
: 0 /* Set */, methodName, targetDoc, hasConverter);
|
|
27955
|
+
? 2 /* UserDataSource.MergeSet */
|
|
27956
|
+
: 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
|
|
27396
27957
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27397
27958
|
const updateData = parseObject(input, context);
|
|
27398
27959
|
let fieldMask;
|
|
@@ -27423,12 +27984,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
|
|
|
27423
27984
|
}
|
|
27424
27985
|
class DeleteFieldValueImpl extends FieldValue {
|
|
27425
27986
|
_toFieldTransform(context) {
|
|
27426
|
-
if (context.dataSource === 2 /* MergeSet */) {
|
|
27987
|
+
if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
|
|
27427
27988
|
// No transform to add for a delete, but we need to add it to our
|
|
27428
27989
|
// fieldMask so it gets deleted.
|
|
27429
27990
|
context.fieldMask.push(context.path);
|
|
27430
27991
|
}
|
|
27431
|
-
else if (context.dataSource === 1 /* Update */) {
|
|
27992
|
+
else if (context.dataSource === 1 /* UserDataSource.Update */) {
|
|
27432
27993
|
throw context.createError(`${this._methodName}() can only appear at the top level ` +
|
|
27433
27994
|
'of your update data');
|
|
27434
27995
|
}
|
|
@@ -27461,7 +28022,7 @@ class DeleteFieldValueImpl extends FieldValue {
|
|
|
27461
28022
|
*/
|
|
27462
28023
|
function createSentinelChildContext(fieldValue, context, arrayElement) {
|
|
27463
28024
|
return new ParseContextImpl({
|
|
27464
|
-
dataSource: 3 /* Argument */,
|
|
28025
|
+
dataSource: 3 /* UserDataSource.Argument */,
|
|
27465
28026
|
targetDoc: context.settings.targetDoc,
|
|
27466
28027
|
methodName: fieldValue._methodName,
|
|
27467
28028
|
arrayElement
|
|
@@ -27525,7 +28086,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
|
|
|
27525
28086
|
}
|
|
27526
28087
|
/** Parse update data from an update() call. */
|
|
27527
28088
|
function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
27528
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28089
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27529
28090
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27530
28091
|
const fieldMaskPaths = [];
|
|
27531
28092
|
const updateData = ObjectValue.empty();
|
|
@@ -27552,7 +28113,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
|
27552
28113
|
}
|
|
27553
28114
|
/** Parse update data from a list of field/value arguments. */
|
|
27554
28115
|
function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
|
|
27555
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28116
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27556
28117
|
const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
|
|
27557
28118
|
const values = [value];
|
|
27558
28119
|
if (moreFieldsAndValues.length % 2 !== 0) {
|
|
@@ -27599,7 +28160,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
|
|
|
27599
28160
|
* contain additional arrays (e.g. the operand of an `in` query).
|
|
27600
28161
|
*/
|
|
27601
28162
|
function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
|
|
27602
|
-
const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
|
|
28163
|
+
const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
|
|
27603
28164
|
const parsed = parseData(input, context);
|
|
27604
28165
|
return parsed;
|
|
27605
28166
|
}
|
|
@@ -27649,7 +28210,7 @@ function parseData(input, context) {
|
|
|
27649
28210
|
// contain additional arrays (each representing an individual field
|
|
27650
28211
|
// value), so we disable this validation.
|
|
27651
28212
|
if (context.settings.arrayElement &&
|
|
27652
|
-
context.dataSource !== 4 /* ArrayArgument */) {
|
|
28213
|
+
context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
|
|
27653
28214
|
throw context.createError('Nested arrays are not supported');
|
|
27654
28215
|
}
|
|
27655
28216
|
return parseArray(input, context);
|
|
@@ -28031,164 +28592,336 @@ function fieldPathFromArgument(methodName, arg) {
|
|
|
28031
28592
|
* limitations under the License.
|
|
28032
28593
|
*/
|
|
28033
28594
|
function validateHasExplicitOrderByForLimitToLast(query) {
|
|
28034
|
-
if (query.limitType === "L" /* Last */ &&
|
|
28595
|
+
if (query.limitType === "L" /* LimitType.Last */ &&
|
|
28035
28596
|
query.explicitOrderBy.length === 0) {
|
|
28036
28597
|
throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
|
|
28037
28598
|
}
|
|
28038
28599
|
}
|
|
28600
|
+
/**
|
|
28601
|
+
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
|
|
28602
|
+
* to a Firestore query.
|
|
28603
|
+
*/
|
|
28604
|
+
class AppliableConstraint {
|
|
28605
|
+
}
|
|
28039
28606
|
/**
|
|
28040
28607
|
* A `QueryConstraint` is used to narrow the set of documents returned by a
|
|
28041
28608
|
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
|
|
28042
|
-
* {@link orderBy}, {@link
|
|
28043
|
-
* endBefore
|
|
28609
|
+
* {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
|
|
28610
|
+
* endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
|
|
28044
28611
|
* can then be passed to {@link query} to create a new query instance that
|
|
28045
28612
|
* also contains this `QueryConstraint`.
|
|
28046
28613
|
*/
|
|
28047
|
-
class QueryConstraint {
|
|
28614
|
+
class QueryConstraint extends AppliableConstraint {
|
|
28048
28615
|
}
|
|
28049
|
-
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
* existing or new constraints.
|
|
28057
|
-
*/
|
|
28058
|
-
function query(query, ...queryConstraints) {
|
|
28616
|
+
function query(query, queryConstraint, ...additionalQueryConstraints) {
|
|
28617
|
+
let queryConstraints = [];
|
|
28618
|
+
if (queryConstraint instanceof AppliableConstraint) {
|
|
28619
|
+
queryConstraints.push(queryConstraint);
|
|
28620
|
+
}
|
|
28621
|
+
queryConstraints = queryConstraints.concat(additionalQueryConstraints);
|
|
28622
|
+
validateQueryConstraintArray(queryConstraints);
|
|
28059
28623
|
for (const constraint of queryConstraints) {
|
|
28060
28624
|
query = constraint._apply(query);
|
|
28061
28625
|
}
|
|
28062
28626
|
return query;
|
|
28063
28627
|
}
|
|
28064
|
-
|
|
28628
|
+
/**
|
|
28629
|
+
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
|
|
28630
|
+
* a Firestore query by filtering on one or more document fields.
|
|
28631
|
+
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
|
|
28632
|
+
* be passed to {@link query} to create a new query instance that also contains
|
|
28633
|
+
* this `QueryFieldFilterConstraint`.
|
|
28634
|
+
*/
|
|
28635
|
+
class QueryFieldFilterConstraint extends QueryConstraint {
|
|
28636
|
+
/**
|
|
28637
|
+
* @internal
|
|
28638
|
+
*/
|
|
28065
28639
|
constructor(_field, _op, _value) {
|
|
28066
28640
|
super();
|
|
28067
28641
|
this._field = _field;
|
|
28068
28642
|
this._op = _op;
|
|
28069
28643
|
this._value = _value;
|
|
28644
|
+
/** The type of this query constraint */
|
|
28070
28645
|
this.type = 'where';
|
|
28071
28646
|
}
|
|
28647
|
+
static _create(_field, _op, _value) {
|
|
28648
|
+
return new QueryFieldFilterConstraint(_field, _op, _value);
|
|
28649
|
+
}
|
|
28072
28650
|
_apply(query) {
|
|
28651
|
+
const filter = this._parse(query);
|
|
28652
|
+
validateNewFieldFilter(query._query, filter);
|
|
28653
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
|
|
28654
|
+
}
|
|
28655
|
+
_parse(query) {
|
|
28073
28656
|
const reader = newUserDataReader(query.firestore);
|
|
28074
28657
|
const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
|
|
28075
|
-
return
|
|
28658
|
+
return filter;
|
|
28076
28659
|
}
|
|
28077
28660
|
}
|
|
28078
28661
|
/**
|
|
28079
|
-
* Creates a {@link
|
|
28080
|
-
* specified field and that the value should satisfy the
|
|
28081
|
-
* provided.
|
|
28662
|
+
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents
|
|
28663
|
+
* must contain the specified field and that the value should satisfy the
|
|
28664
|
+
* relation constraint provided.
|
|
28082
28665
|
*
|
|
28083
28666
|
* @param fieldPath - The path to compare
|
|
28084
28667
|
* @param opStr - The operation string (e.g "<", "<=", "==", "<",
|
|
28085
28668
|
* "<=", "!=").
|
|
28086
28669
|
* @param value - The value for comparison
|
|
28087
|
-
* @returns The created {@link
|
|
28670
|
+
* @returns The created {@link QueryFieldFilterConstraint}.
|
|
28088
28671
|
*/
|
|
28089
28672
|
function where(fieldPath, opStr, value) {
|
|
28090
28673
|
const op = opStr;
|
|
28091
28674
|
const field = fieldPathFromArgument('where', fieldPath);
|
|
28092
|
-
return
|
|
28675
|
+
return QueryFieldFilterConstraint._create(field, op, value);
|
|
28676
|
+
}
|
|
28677
|
+
/**
|
|
28678
|
+
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
|
|
28679
|
+
* returned by a Firestore query by performing the logical OR or AND of multiple
|
|
28680
|
+
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
|
|
28681
|
+
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
|
|
28682
|
+
* {@link and} and can then be passed to {@link query} to create a new query
|
|
28683
|
+
* instance that also contains the `QueryCompositeFilterConstraint`.
|
|
28684
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28685
|
+
*/
|
|
28686
|
+
class QueryCompositeFilterConstraint extends AppliableConstraint {
|
|
28687
|
+
/**
|
|
28688
|
+
* @internal
|
|
28689
|
+
*/
|
|
28690
|
+
constructor(
|
|
28691
|
+
/** The type of this query constraint */
|
|
28692
|
+
type, _queryConstraints) {
|
|
28693
|
+
super();
|
|
28694
|
+
this.type = type;
|
|
28695
|
+
this._queryConstraints = _queryConstraints;
|
|
28696
|
+
}
|
|
28697
|
+
static _create(type, _queryConstraints) {
|
|
28698
|
+
return new QueryCompositeFilterConstraint(type, _queryConstraints);
|
|
28699
|
+
}
|
|
28700
|
+
_parse(query) {
|
|
28701
|
+
const parsedFilters = this._queryConstraints
|
|
28702
|
+
.map(queryConstraint => {
|
|
28703
|
+
return queryConstraint._parse(query);
|
|
28704
|
+
})
|
|
28705
|
+
.filter(parsedFilter => parsedFilter.getFilters().length > 0);
|
|
28706
|
+
if (parsedFilters.length === 1) {
|
|
28707
|
+
return parsedFilters[0];
|
|
28708
|
+
}
|
|
28709
|
+
return CompositeFilter.create(parsedFilters, this._getOperator());
|
|
28710
|
+
}
|
|
28711
|
+
_apply(query) {
|
|
28712
|
+
const parsedFilter = this._parse(query);
|
|
28713
|
+
if (parsedFilter.getFilters().length === 0) {
|
|
28714
|
+
// Return the existing query if not adding any more filters (e.g. an empty
|
|
28715
|
+
// composite filter).
|
|
28716
|
+
return query;
|
|
28717
|
+
}
|
|
28718
|
+
validateNewFilter(query._query, parsedFilter);
|
|
28719
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
|
|
28720
|
+
}
|
|
28721
|
+
_getQueryConstraints() {
|
|
28722
|
+
return this._queryConstraints;
|
|
28723
|
+
}
|
|
28724
|
+
_getOperator() {
|
|
28725
|
+
return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
|
|
28726
|
+
}
|
|
28727
|
+
}
|
|
28728
|
+
/**
|
|
28729
|
+
* Creates a new {@link QueryCompositeFilterConstraint} that is a disjunction of
|
|
28730
|
+
* the given filter constraints. A disjunction filter includes a document if it
|
|
28731
|
+
* satisfies any of the given filters.
|
|
28732
|
+
*
|
|
28733
|
+
* @param queryConstraints - Optional. The list of
|
|
28734
|
+
* {@link QueryFilterConstraint}s to perform a disjunction for. These must be
|
|
28735
|
+
* created with calls to {@link where}, {@link or}, or {@link and}.
|
|
28736
|
+
* @returns The newly created {@link QueryCompositeFilterConstraint}.
|
|
28737
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28738
|
+
*/
|
|
28739
|
+
function or(...queryConstraints) {
|
|
28740
|
+
// Only support QueryFilterConstraints
|
|
28741
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
|
|
28742
|
+
return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
|
|
28743
|
+
}
|
|
28744
|
+
/**
|
|
28745
|
+
* Creates a new {@link QueryCompositeFilterConstraint} that is a conjunction of
|
|
28746
|
+
* the given filter constraints. A conjunction filter includes a document if it
|
|
28747
|
+
* satisfies all of the given filters.
|
|
28748
|
+
*
|
|
28749
|
+
* @param queryConstraints - Optional. The list of
|
|
28750
|
+
* {@link QueryFilterConstraint}s to perform a conjunction for. These must be
|
|
28751
|
+
* created with calls to {@link where}, {@link or}, or {@link and}.
|
|
28752
|
+
* @returns The newly created {@link QueryCompositeFilterConstraint}.
|
|
28753
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28754
|
+
*/
|
|
28755
|
+
function and(...queryConstraints) {
|
|
28756
|
+
// Only support QueryFilterConstraints
|
|
28757
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
|
|
28758
|
+
return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
|
|
28093
28759
|
}
|
|
28760
|
+
/**
|
|
28761
|
+
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a
|
|
28762
|
+
* Firestore query. `QueryOrderByConstraint`s are created by invoking
|
|
28763
|
+
* {@link orderBy} and can then be passed to {@link query} to create a new query
|
|
28764
|
+
* instance that also contains this `QueryOrderByConstraint`.
|
|
28765
|
+
*
|
|
28766
|
+
* Note: Documents that do not contain the orderBy field will not be present in
|
|
28767
|
+
* the query result.
|
|
28768
|
+
*/
|
|
28094
28769
|
class QueryOrderByConstraint extends QueryConstraint {
|
|
28770
|
+
/**
|
|
28771
|
+
* @internal
|
|
28772
|
+
*/
|
|
28095
28773
|
constructor(_field, _direction) {
|
|
28096
28774
|
super();
|
|
28097
28775
|
this._field = _field;
|
|
28098
28776
|
this._direction = _direction;
|
|
28777
|
+
/** The type of this query constraint */
|
|
28099
28778
|
this.type = 'orderBy';
|
|
28100
28779
|
}
|
|
28780
|
+
static _create(_field, _direction) {
|
|
28781
|
+
return new QueryOrderByConstraint(_field, _direction);
|
|
28782
|
+
}
|
|
28101
28783
|
_apply(query) {
|
|
28102
28784
|
const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
|
|
28103
28785
|
return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
|
|
28104
28786
|
}
|
|
28105
28787
|
}
|
|
28106
28788
|
/**
|
|
28107
|
-
* Creates a {@link
|
|
28789
|
+
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the
|
|
28108
28790
|
* specified field, optionally in descending order instead of ascending.
|
|
28109
28791
|
*
|
|
28792
|
+
* Note: Documents that do not contain the specified field will not be present
|
|
28793
|
+
* in the query result.
|
|
28794
|
+
*
|
|
28110
28795
|
* @param fieldPath - The field to sort by.
|
|
28111
28796
|
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
|
|
28112
28797
|
* not specified, order will be ascending.
|
|
28113
|
-
* @returns The created {@link
|
|
28798
|
+
* @returns The created {@link QueryOrderByConstraint}.
|
|
28114
28799
|
*/
|
|
28115
28800
|
function orderBy(fieldPath, directionStr = 'asc') {
|
|
28116
28801
|
const direction = directionStr;
|
|
28117
28802
|
const path = fieldPathFromArgument('orderBy', fieldPath);
|
|
28118
|
-
return
|
|
28803
|
+
return QueryOrderByConstraint._create(path, direction);
|
|
28119
28804
|
}
|
|
28805
|
+
/**
|
|
28806
|
+
* A `QueryLimitConstraint` is used to limit the number of documents returned by
|
|
28807
|
+
* a Firestore query.
|
|
28808
|
+
* `QueryLimitConstraint`s are created by invoking {@link limit} or
|
|
28809
|
+
* {@link limitToLast} and can then be passed to {@link query} to create a new
|
|
28810
|
+
* query instance that also contains this `QueryLimitConstraint`.
|
|
28811
|
+
*/
|
|
28120
28812
|
class QueryLimitConstraint extends QueryConstraint {
|
|
28121
|
-
|
|
28813
|
+
/**
|
|
28814
|
+
* @internal
|
|
28815
|
+
*/
|
|
28816
|
+
constructor(
|
|
28817
|
+
/** The type of this query constraint */
|
|
28818
|
+
type, _limit, _limitType) {
|
|
28122
28819
|
super();
|
|
28123
28820
|
this.type = type;
|
|
28124
28821
|
this._limit = _limit;
|
|
28125
28822
|
this._limitType = _limitType;
|
|
28126
28823
|
}
|
|
28824
|
+
static _create(type, _limit, _limitType) {
|
|
28825
|
+
return new QueryLimitConstraint(type, _limit, _limitType);
|
|
28826
|
+
}
|
|
28127
28827
|
_apply(query) {
|
|
28128
28828
|
return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
|
|
28129
28829
|
}
|
|
28130
28830
|
}
|
|
28131
28831
|
/**
|
|
28132
|
-
* Creates a {@link
|
|
28832
|
+
* Creates a {@link QueryLimitConstraint} that only returns the first matching
|
|
28833
|
+
* documents.
|
|
28133
28834
|
*
|
|
28134
28835
|
* @param limit - The maximum number of items to return.
|
|
28135
|
-
* @returns The created {@link
|
|
28836
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28136
28837
|
*/
|
|
28137
28838
|
function limit(limit) {
|
|
28138
28839
|
validatePositiveNumber('limit', limit);
|
|
28139
|
-
return
|
|
28840
|
+
return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
|
|
28140
28841
|
}
|
|
28141
28842
|
/**
|
|
28142
|
-
* Creates a {@link
|
|
28843
|
+
* Creates a {@link QueryLimitConstraint} that only returns the last matching
|
|
28844
|
+
* documents.
|
|
28143
28845
|
*
|
|
28144
28846
|
* You must specify at least one `orderBy` clause for `limitToLast` queries,
|
|
28145
28847
|
* otherwise an exception will be thrown during execution.
|
|
28146
28848
|
*
|
|
28147
28849
|
* @param limit - The maximum number of items to return.
|
|
28148
|
-
* @returns The created {@link
|
|
28850
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28149
28851
|
*/
|
|
28150
28852
|
function limitToLast(limit) {
|
|
28151
28853
|
validatePositiveNumber('limitToLast', limit);
|
|
28152
|
-
return
|
|
28854
|
+
return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
|
|
28153
28855
|
}
|
|
28856
|
+
/**
|
|
28857
|
+
* A `QueryStartAtConstraint` is used to exclude documents from the start of a
|
|
28858
|
+
* result set returned by a Firestore query.
|
|
28859
|
+
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
|
|
28860
|
+
* {@link (startAfter:1)} and can then be passed to {@link query} to create a
|
|
28861
|
+
* new query instance that also contains this `QueryStartAtConstraint`.
|
|
28862
|
+
*/
|
|
28154
28863
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
28155
|
-
|
|
28864
|
+
/**
|
|
28865
|
+
* @internal
|
|
28866
|
+
*/
|
|
28867
|
+
constructor(
|
|
28868
|
+
/** The type of this query constraint */
|
|
28869
|
+
type, _docOrFields, _inclusive) {
|
|
28156
28870
|
super();
|
|
28157
28871
|
this.type = type;
|
|
28158
28872
|
this._docOrFields = _docOrFields;
|
|
28159
28873
|
this._inclusive = _inclusive;
|
|
28160
28874
|
}
|
|
28875
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28876
|
+
return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
|
|
28877
|
+
}
|
|
28161
28878
|
_apply(query) {
|
|
28162
28879
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28163
28880
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
28164
28881
|
}
|
|
28165
28882
|
}
|
|
28166
28883
|
function startAt(...docOrFields) {
|
|
28167
|
-
return
|
|
28884
|
+
return QueryStartAtConstraint._create('startAt', docOrFields,
|
|
28168
28885
|
/*inclusive=*/ true);
|
|
28169
28886
|
}
|
|
28170
28887
|
function startAfter(...docOrFields) {
|
|
28171
|
-
return
|
|
28888
|
+
return QueryStartAtConstraint._create('startAfter', docOrFields,
|
|
28172
28889
|
/*inclusive=*/ false);
|
|
28173
28890
|
}
|
|
28891
|
+
/**
|
|
28892
|
+
* A `QueryEndAtConstraint` is used to exclude documents from the end of a
|
|
28893
|
+
* result set returned by a Firestore query.
|
|
28894
|
+
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
|
|
28895
|
+
* {@link (endBefore:1)} and can then be passed to {@link query} to create a new
|
|
28896
|
+
* query instance that also contains this `QueryEndAtConstraint`.
|
|
28897
|
+
*/
|
|
28174
28898
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
28175
|
-
|
|
28899
|
+
/**
|
|
28900
|
+
* @internal
|
|
28901
|
+
*/
|
|
28902
|
+
constructor(
|
|
28903
|
+
/** The type of this query constraint */
|
|
28904
|
+
type, _docOrFields, _inclusive) {
|
|
28176
28905
|
super();
|
|
28177
28906
|
this.type = type;
|
|
28178
28907
|
this._docOrFields = _docOrFields;
|
|
28179
28908
|
this._inclusive = _inclusive;
|
|
28180
28909
|
}
|
|
28910
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28911
|
+
return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
|
|
28912
|
+
}
|
|
28181
28913
|
_apply(query) {
|
|
28182
28914
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28183
28915
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
28184
28916
|
}
|
|
28185
28917
|
}
|
|
28186
28918
|
function endBefore(...docOrFields) {
|
|
28187
|
-
return
|
|
28919
|
+
return QueryEndAtConstraint._create('endBefore', docOrFields,
|
|
28188
28920
|
/*inclusive=*/ false);
|
|
28189
28921
|
}
|
|
28190
28922
|
function endAt(...docOrFields) {
|
|
28191
|
-
return
|
|
28923
|
+
return QueryEndAtConstraint._create('endAt', docOrFields,
|
|
28924
|
+
/*inclusive=*/ true);
|
|
28192
28925
|
}
|
|
28193
28926
|
/** Helper function to create a bound from a document or fields */
|
|
28194
28927
|
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
@@ -28204,10 +28937,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
|
|
|
28204
28937
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
28205
28938
|
let fieldValue;
|
|
28206
28939
|
if (fieldPath.isKeyField()) {
|
|
28207
|
-
if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28940
|
+
if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28208
28941
|
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
|
|
28209
28942
|
}
|
|
28210
|
-
else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
28943
|
+
else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
28211
28944
|
validateDisjunctiveFilterElements(value, op);
|
|
28212
28945
|
const referenceList = [];
|
|
28213
28946
|
for (const arrayValue of value) {
|
|
@@ -28220,16 +28953,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
|
|
|
28220
28953
|
}
|
|
28221
28954
|
}
|
|
28222
28955
|
else {
|
|
28223
|
-
if (op === "in" /* IN */ ||
|
|
28224
|
-
op === "not-in" /* NOT_IN */ ||
|
|
28225
|
-
op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28956
|
+
if (op === "in" /* Operator.IN */ ||
|
|
28957
|
+
op === "not-in" /* Operator.NOT_IN */ ||
|
|
28958
|
+
op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28226
28959
|
validateDisjunctiveFilterElements(value, op);
|
|
28227
28960
|
}
|
|
28228
28961
|
fieldValue = parseQueryValue(dataReader, methodName, value,
|
|
28229
|
-
/* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
|
|
28962
|
+
/* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
|
|
28230
28963
|
}
|
|
28231
28964
|
const filter = FieldFilter.create(fieldPath, op, fieldValue);
|
|
28232
|
-
validateNewFilter(query, filter);
|
|
28233
28965
|
return filter;
|
|
28234
28966
|
}
|
|
28235
28967
|
function newQueryOrderBy(query, fieldPath, direction) {
|
|
@@ -28399,62 +29131,84 @@ function validateDisjunctiveFilterElements(value, operator) {
|
|
|
28399
29131
|
*/
|
|
28400
29132
|
function conflictingOps(op) {
|
|
28401
29133
|
switch (op) {
|
|
28402
|
-
case "!=" /* NOT_EQUAL */:
|
|
28403
|
-
return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
|
|
28404
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
29134
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
29135
|
+
return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
|
|
29136
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
28405
29137
|
return [
|
|
28406
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28407
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28408
|
-
"not-in" /* NOT_IN */
|
|
29138
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29139
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29140
|
+
"not-in" /* Operator.NOT_IN */
|
|
28409
29141
|
];
|
|
28410
|
-
case "in" /* IN */:
|
|
28411
|
-
return ["array-contains-any" /* ARRAY_CONTAINS_ANY */, "in" /* IN */, "not-in" /* NOT_IN */];
|
|
28412
|
-
case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
|
|
29142
|
+
case "in" /* Operator.IN */:
|
|
29143
|
+
return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
|
|
29144
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
28413
29145
|
return [
|
|
28414
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28415
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28416
|
-
"in" /* IN */,
|
|
28417
|
-
"not-in" /* NOT_IN */
|
|
29146
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29147
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29148
|
+
"in" /* Operator.IN */,
|
|
29149
|
+
"not-in" /* Operator.NOT_IN */
|
|
28418
29150
|
];
|
|
28419
|
-
case "not-in" /* NOT_IN */:
|
|
29151
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
28420
29152
|
return [
|
|
28421
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28422
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28423
|
-
"in" /* IN */,
|
|
28424
|
-
"not-in" /* NOT_IN */,
|
|
28425
|
-
"!=" /* NOT_EQUAL */
|
|
29153
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29154
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29155
|
+
"in" /* Operator.IN */,
|
|
29156
|
+
"not-in" /* Operator.NOT_IN */,
|
|
29157
|
+
"!=" /* Operator.NOT_EQUAL */
|
|
28426
29158
|
];
|
|
28427
29159
|
default:
|
|
28428
29160
|
return [];
|
|
28429
29161
|
}
|
|
28430
29162
|
}
|
|
28431
|
-
function
|
|
28432
|
-
if (
|
|
28433
|
-
const
|
|
28434
|
-
|
|
29163
|
+
function validateNewFieldFilter(query, fieldFilter) {
|
|
29164
|
+
if (fieldFilter.isInequality()) {
|
|
29165
|
+
const existingInequality = getInequalityFilterField(query);
|
|
29166
|
+
const newInequality = fieldFilter.field;
|
|
29167
|
+
if (existingInequality !== null &&
|
|
29168
|
+
!existingInequality.isEqual(newInequality)) {
|
|
28435
29169
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
|
|
28436
29170
|
' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
|
|
28437
|
-
` inequality filters on '${
|
|
28438
|
-
` and '${
|
|
29171
|
+
` inequality filters on '${existingInequality.toString()}'` +
|
|
29172
|
+
` and '${newInequality.toString()}'`);
|
|
28439
29173
|
}
|
|
28440
29174
|
const firstOrderByField = getFirstOrderByField(query);
|
|
28441
29175
|
if (firstOrderByField !== null) {
|
|
28442
|
-
validateOrderByAndInequalityMatch(query,
|
|
29176
|
+
validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
|
|
28443
29177
|
}
|
|
28444
29178
|
}
|
|
28445
|
-
const conflictingOp =
|
|
29179
|
+
const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
|
|
28446
29180
|
if (conflictingOp !== null) {
|
|
28447
29181
|
// Special case when it's a duplicate op to give a slightly clearer error message.
|
|
28448
|
-
if (conflictingOp ===
|
|
29182
|
+
if (conflictingOp === fieldFilter.op) {
|
|
28449
29183
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
|
|
28450
|
-
`'${
|
|
29184
|
+
`'${fieldFilter.op.toString()}' filter.`);
|
|
28451
29185
|
}
|
|
28452
29186
|
else {
|
|
28453
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${
|
|
29187
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
|
|
28454
29188
|
`with '${conflictingOp.toString()}' filters.`);
|
|
28455
29189
|
}
|
|
28456
29190
|
}
|
|
28457
29191
|
}
|
|
29192
|
+
function validateNewFilter(query, filter) {
|
|
29193
|
+
let testQuery = query;
|
|
29194
|
+
const subFilters = filter.getFlattenedFilters();
|
|
29195
|
+
for (const subFilter of subFilters) {
|
|
29196
|
+
validateNewFieldFilter(testQuery, subFilter);
|
|
29197
|
+
testQuery = queryWithAddedFilter(testQuery, subFilter);
|
|
29198
|
+
}
|
|
29199
|
+
}
|
|
29200
|
+
// Checks if any of the provided filter operators are included in the given list of filters and
|
|
29201
|
+
// returns the first one that is, or null if none are.
|
|
29202
|
+
function findOpInsideFilters(filters, operators) {
|
|
29203
|
+
for (const filter of filters) {
|
|
29204
|
+
for (const fieldFilter of filter.getFlattenedFilters()) {
|
|
29205
|
+
if (operators.indexOf(fieldFilter.op) >= 0) {
|
|
29206
|
+
return fieldFilter.op;
|
|
29207
|
+
}
|
|
29208
|
+
}
|
|
29209
|
+
}
|
|
29210
|
+
return null;
|
|
29211
|
+
}
|
|
28458
29212
|
function validateNewOrderBy(query, orderBy) {
|
|
28459
29213
|
if (getFirstOrderByField(query) === null) {
|
|
28460
29214
|
// This is the first order by. It must match any inequality.
|
|
@@ -28472,6 +29226,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28472
29226
|
`as your first argument to orderBy(), but your first orderBy() ` +
|
|
28473
29227
|
`is on field '${orderBy.toString()}' instead.`);
|
|
28474
29228
|
}
|
|
29229
|
+
}
|
|
29230
|
+
function validateQueryFilterConstraint(functionName, queryConstraint) {
|
|
29231
|
+
if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
|
|
29232
|
+
!(queryConstraint instanceof QueryCompositeFilterConstraint)) {
|
|
29233
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
|
|
29234
|
+
}
|
|
29235
|
+
}
|
|
29236
|
+
function validateQueryConstraintArray(queryConstraint) {
|
|
29237
|
+
const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
|
|
29238
|
+
const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
|
|
29239
|
+
if (compositeFilterCount > 1 ||
|
|
29240
|
+
(compositeFilterCount > 0 && fieldFilterCount > 0)) {
|
|
29241
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
|
|
29242
|
+
'more than one filter at the top level. Consider nesting the multiple ' +
|
|
29243
|
+
'filters within an `and(...)` statement. For example: ' +
|
|
29244
|
+
'change `query(query, where(...), or(...))` to ' +
|
|
29245
|
+
'`query(query, and(where(...), or(...)))`.');
|
|
29246
|
+
}
|
|
28475
29247
|
}
|
|
28476
29248
|
|
|
28477
29249
|
/**
|
|
@@ -28499,27 +29271,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28499
29271
|
class AbstractUserDataWriter {
|
|
28500
29272
|
convertValue(value, serverTimestampBehavior = 'none') {
|
|
28501
29273
|
switch (typeOrder(value)) {
|
|
28502
|
-
case 0 /* NullValue */:
|
|
29274
|
+
case 0 /* TypeOrder.NullValue */:
|
|
28503
29275
|
return null;
|
|
28504
|
-
case 1 /* BooleanValue */:
|
|
29276
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
28505
29277
|
return value.booleanValue;
|
|
28506
|
-
case 2 /* NumberValue */:
|
|
29278
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
28507
29279
|
return normalizeNumber(value.integerValue || value.doubleValue);
|
|
28508
|
-
case 3 /* TimestampValue */:
|
|
29280
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
28509
29281
|
return this.convertTimestamp(value.timestampValue);
|
|
28510
|
-
case 4 /* ServerTimestampValue */:
|
|
29282
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
28511
29283
|
return this.convertServerTimestamp(value, serverTimestampBehavior);
|
|
28512
|
-
case 5 /* StringValue */:
|
|
29284
|
+
case 5 /* TypeOrder.StringValue */:
|
|
28513
29285
|
return value.stringValue;
|
|
28514
|
-
case 6 /* BlobValue */:
|
|
29286
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
28515
29287
|
return this.convertBytes(normalizeByteString(value.bytesValue));
|
|
28516
|
-
case 7 /* RefValue */:
|
|
29288
|
+
case 7 /* TypeOrder.RefValue */:
|
|
28517
29289
|
return this.convertReference(value.referenceValue);
|
|
28518
|
-
case 8 /* GeoPointValue */:
|
|
29290
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
28519
29291
|
return this.convertGeoPoint(value.geoPointValue);
|
|
28520
|
-
case 9 /* ArrayValue */:
|
|
29292
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
28521
29293
|
return this.convertArray(value.arrayValue, serverTimestampBehavior);
|
|
28522
|
-
case 10 /* ObjectValue */:
|
|
29294
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
28523
29295
|
return this.convertObject(value.mapValue, serverTimestampBehavior);
|
|
28524
29296
|
default:
|
|
28525
29297
|
throw fail();
|
|
@@ -28861,16 +29633,16 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28861
29633
|
// to lookup the index of a document.
|
|
28862
29634
|
let indexTracker = querySnapshot._snapshot.oldDocs;
|
|
28863
29635
|
return querySnapshot._snapshot.docChanges
|
|
28864
|
-
.filter(change => includeMetadataChanges || change.type !== 3 /* Metadata */)
|
|
29636
|
+
.filter(change => includeMetadataChanges || change.type !== 3 /* ChangeType.Metadata */)
|
|
28865
29637
|
.map(change => {
|
|
28866
29638
|
const doc = new QueryDocumentSnapshot(querySnapshot._firestore, querySnapshot._userDataWriter, change.doc.key, change.doc, new SnapshotMetadata(querySnapshot._snapshot.mutatedKeys.has(change.doc.key), querySnapshot._snapshot.fromCache), querySnapshot.query.converter);
|
|
28867
29639
|
let oldIndex = -1;
|
|
28868
29640
|
let newIndex = -1;
|
|
28869
|
-
if (change.type !== 0 /* Added */) {
|
|
29641
|
+
if (change.type !== 0 /* ChangeType.Added */) {
|
|
28870
29642
|
oldIndex = indexTracker.indexOf(change.doc.key);
|
|
28871
29643
|
indexTracker = indexTracker.delete(change.doc.key);
|
|
28872
29644
|
}
|
|
28873
|
-
if (change.type !== 1 /* Removed */) {
|
|
29645
|
+
if (change.type !== 1 /* ChangeType.Removed */) {
|
|
28874
29646
|
indexTracker = indexTracker.add(change.doc);
|
|
28875
29647
|
newIndex = indexTracker.indexOf(change.doc.key);
|
|
28876
29648
|
}
|
|
@@ -28885,12 +29657,12 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28885
29657
|
}
|
|
28886
29658
|
function resultChangeType(type) {
|
|
28887
29659
|
switch (type) {
|
|
28888
|
-
case 0 /* Added */:
|
|
29660
|
+
case 0 /* ChangeType.Added */:
|
|
28889
29661
|
return 'added';
|
|
28890
|
-
case 2 /* Modified */:
|
|
28891
|
-
case 3 /* Metadata */:
|
|
29662
|
+
case 2 /* ChangeType.Modified */:
|
|
29663
|
+
case 3 /* ChangeType.Metadata */:
|
|
28892
29664
|
return 'modified';
|
|
28893
|
-
case 1 /* Removed */:
|
|
29665
|
+
case 1 /* ChangeType.Removed */:
|
|
28894
29666
|
return 'removed';
|
|
28895
29667
|
default:
|
|
28896
29668
|
return fail();
|
|
@@ -29020,7 +29792,8 @@ function getDocs(query) {
|
|
|
29020
29792
|
}
|
|
29021
29793
|
/**
|
|
29022
29794
|
* Executes the query and returns the results as a `QuerySnapshot` from cache.
|
|
29023
|
-
* Returns an
|
|
29795
|
+
* Returns an empty result set if no documents matching the query are currently
|
|
29796
|
+
* cached.
|
|
29024
29797
|
*
|
|
29025
29798
|
* @returns A `Promise` that will be resolved with the results of the query.
|
|
29026
29799
|
*/
|
|
@@ -29724,13 +30497,13 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29724
30497
|
const fieldPathString = tryGetString(field, 'fieldPath');
|
|
29725
30498
|
const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
|
|
29726
30499
|
if (field.arrayConfig === 'CONTAINS') {
|
|
29727
|
-
segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
|
|
30500
|
+
segments.push(new IndexSegment(fieldPath, 2 /* IndexKind.CONTAINS */));
|
|
29728
30501
|
}
|
|
29729
30502
|
else if (field.order === 'ASCENDING') {
|
|
29730
|
-
segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
|
|
30503
|
+
segments.push(new IndexSegment(fieldPath, 0 /* IndexKind.ASCENDING */));
|
|
29731
30504
|
}
|
|
29732
30505
|
else if (field.order === 'DESCENDING') {
|
|
29733
|
-
segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
|
|
30506
|
+
segments.push(new IndexSegment(fieldPath, 1 /* IndexKind.DESCENDING */));
|
|
29734
30507
|
}
|
|
29735
30508
|
}
|
|
29736
30509
|
}
|
|
@@ -29740,12 +30513,11 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29740
30513
|
return parsedIndexes;
|
|
29741
30514
|
}
|
|
29742
30515
|
function tryParseJson(json) {
|
|
29743
|
-
var _a;
|
|
29744
30516
|
try {
|
|
29745
30517
|
return JSON.parse(json);
|
|
29746
30518
|
}
|
|
29747
30519
|
catch (e) {
|
|
29748
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (
|
|
30520
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (e === null || e === void 0 ? void 0 : e.message));
|
|
29749
30521
|
}
|
|
29750
30522
|
}
|
|
29751
30523
|
function tryGetString(data, property) {
|
|
@@ -29773,5 +30545,5 @@ function tryGetString(data, property) {
|
|
|
29773
30545
|
*/
|
|
29774
30546
|
registerFirestore('node');
|
|
29775
30547
|
|
|
29776
|
-
export { AbstractUserDataWriter, AggregateField, AggregateQuerySnapshot, Bytes, CACHE_SIZE_UNLIMITED, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, LoadBundleTask, Query, QueryConstraint, QueryDocumentSnapshot, QuerySnapshot, SnapshotMetadata, Timestamp, Transaction, WriteBatch, DatabaseId as _DatabaseId, DocumentKey as _DocumentKey, EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider, EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider, FieldPath$1 as _FieldPath, cast as _cast, debugAssert as _debugAssert, isBase64Available as _isBase64Available, logWarn as _logWarn, validateIsNotUsedTogether as _validateIsNotUsedTogether, addDoc, aggregateQuerySnapshotEqual, arrayRemove, arrayUnion, clearIndexedDbPersistence, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, disableNetwork, doc, documentId, enableIndexedDbPersistence, enableMultiTabIndexedDbPersistence, enableNetwork, endAt, endBefore, ensureFirestoreConfigured, executeWrite, getCountFromServer, getDoc, getDocFromCache, getDocFromServer, getDocs, getDocsFromCache, getDocsFromServer, getFirestore, increment, initializeFirestore, limit, limitToLast, loadBundle, namedQuery, onSnapshot, onSnapshotsInSync, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setIndexConfiguration, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, waitForPendingWrites, where, writeBatch };
|
|
30548
|
+
export { AbstractUserDataWriter, AggregateField, AggregateQuerySnapshot, Bytes, CACHE_SIZE_UNLIMITED, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, LoadBundleTask, Query, QueryCompositeFilterConstraint, QueryConstraint, QueryDocumentSnapshot, QueryEndAtConstraint, QueryFieldFilterConstraint, QueryLimitConstraint, QueryOrderByConstraint, QuerySnapshot, QueryStartAtConstraint, SnapshotMetadata, Timestamp, Transaction, WriteBatch, DatabaseId as _DatabaseId, DocumentKey as _DocumentKey, EmptyAppCheckTokenProvider as _EmptyAppCheckTokenProvider, EmptyAuthCredentialsProvider as _EmptyAuthCredentialsProvider, FieldPath$1 as _FieldPath, cast as _cast, debugAssert as _debugAssert, isBase64Available as _isBase64Available, logWarn as _logWarn, validateIsNotUsedTogether as _validateIsNotUsedTogether, addDoc, aggregateQuerySnapshotEqual, and, arrayRemove, arrayUnion, clearIndexedDbPersistence, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, disableNetwork, doc, documentId, enableIndexedDbPersistence, enableMultiTabIndexedDbPersistence, enableNetwork, endAt, endBefore, ensureFirestoreConfigured, executeWrite, getCountFromServer, getDoc, getDocFromCache, getDocFromServer, getDocs, getDocsFromCache, getDocsFromServer, getFirestore, increment, initializeFirestore, limit, limitToLast, loadBundle, namedQuery, onSnapshot, onSnapshotsInSync, or, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setIndexConfiguration, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, waitForPendingWrites, where, writeBatch };
|
|
29777
30549
|
//# sourceMappingURL=index.node.mjs.map
|