@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.cjs.js
CHANGED
|
@@ -33,7 +33,7 @@ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
|
|
|
33
33
|
var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
|
|
34
34
|
|
|
35
35
|
const name = "@firebase/firestore";
|
|
36
|
-
const version$1 = "3.
|
|
36
|
+
const version$1 = "3.8.0-canary.06dc1364d";
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* @license
|
|
@@ -86,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
86
86
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
87
87
|
User.MOCK_USER = new User('mock-user');
|
|
88
88
|
|
|
89
|
-
const version = "9.
|
|
89
|
+
const version = "9.15.0-canary.06dc1364d";
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* @license
|
|
@@ -1524,11 +1524,11 @@ class FieldIndex {
|
|
|
1524
1524
|
FieldIndex.UNKNOWN_ID = -1;
|
|
1525
1525
|
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
1526
1526
|
function fieldIndexGetArraySegment(fieldIndex) {
|
|
1527
|
-
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
1527
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* IndexKind.CONTAINS */);
|
|
1528
1528
|
}
|
|
1529
1529
|
/** Returns all directional (ascending/descending) segments for this index. */
|
|
1530
1530
|
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
1531
|
-
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
1531
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* IndexKind.CONTAINS */);
|
|
1532
1532
|
}
|
|
1533
1533
|
/**
|
|
1534
1534
|
* Returns the order of the document key component for the given index.
|
|
@@ -1538,7 +1538,7 @@ function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
|
1538
1538
|
function fieldIndexGetKeyOrder(fieldIndex) {
|
|
1539
1539
|
const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
|
|
1540
1540
|
return directionalSegments.length === 0
|
|
1541
|
-
? 0 /* ASCENDING */
|
|
1541
|
+
? 0 /* IndexKind.ASCENDING */
|
|
1542
1542
|
: directionalSegments[directionalSegments.length - 1].kind;
|
|
1543
1543
|
}
|
|
1544
1544
|
/**
|
|
@@ -2652,7 +2652,7 @@ class IndexBackfillerScheduler {
|
|
|
2652
2652
|
}
|
|
2653
2653
|
schedule(delay) {
|
|
2654
2654
|
logDebug(LOG_TAG$h, `Scheduled in ${delay}ms`);
|
|
2655
|
-
this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* IndexBackfill */, delay, async () => {
|
|
2655
|
+
this.task = this.asyncQueue.enqueueAfterDelay("index_backfill" /* TimerId.IndexBackfill */, delay, async () => {
|
|
2656
2656
|
this.task = null;
|
|
2657
2657
|
try {
|
|
2658
2658
|
const documentsProcessed = await this.backfiller.backfill();
|
|
@@ -4448,40 +4448,40 @@ const MIN_VALUE = {
|
|
|
4448
4448
|
/** Extracts the backend's type order for the provided value. */
|
|
4449
4449
|
function typeOrder(value) {
|
|
4450
4450
|
if ('nullValue' in value) {
|
|
4451
|
-
return 0 /* NullValue */;
|
|
4451
|
+
return 0 /* TypeOrder.NullValue */;
|
|
4452
4452
|
}
|
|
4453
4453
|
else if ('booleanValue' in value) {
|
|
4454
|
-
return 1 /* BooleanValue */;
|
|
4454
|
+
return 1 /* TypeOrder.BooleanValue */;
|
|
4455
4455
|
}
|
|
4456
4456
|
else if ('integerValue' in value || 'doubleValue' in value) {
|
|
4457
|
-
return 2 /* NumberValue */;
|
|
4457
|
+
return 2 /* TypeOrder.NumberValue */;
|
|
4458
4458
|
}
|
|
4459
4459
|
else if ('timestampValue' in value) {
|
|
4460
|
-
return 3 /* TimestampValue */;
|
|
4460
|
+
return 3 /* TypeOrder.TimestampValue */;
|
|
4461
4461
|
}
|
|
4462
4462
|
else if ('stringValue' in value) {
|
|
4463
|
-
return 5 /* StringValue */;
|
|
4463
|
+
return 5 /* TypeOrder.StringValue */;
|
|
4464
4464
|
}
|
|
4465
4465
|
else if ('bytesValue' in value) {
|
|
4466
|
-
return 6 /* BlobValue */;
|
|
4466
|
+
return 6 /* TypeOrder.BlobValue */;
|
|
4467
4467
|
}
|
|
4468
4468
|
else if ('referenceValue' in value) {
|
|
4469
|
-
return 7 /* RefValue */;
|
|
4469
|
+
return 7 /* TypeOrder.RefValue */;
|
|
4470
4470
|
}
|
|
4471
4471
|
else if ('geoPointValue' in value) {
|
|
4472
|
-
return 8 /* GeoPointValue */;
|
|
4472
|
+
return 8 /* TypeOrder.GeoPointValue */;
|
|
4473
4473
|
}
|
|
4474
4474
|
else if ('arrayValue' in value) {
|
|
4475
|
-
return 9 /* ArrayValue */;
|
|
4475
|
+
return 9 /* TypeOrder.ArrayValue */;
|
|
4476
4476
|
}
|
|
4477
4477
|
else if ('mapValue' in value) {
|
|
4478
4478
|
if (isServerTimestamp(value)) {
|
|
4479
|
-
return 4 /* ServerTimestampValue */;
|
|
4479
|
+
return 4 /* TypeOrder.ServerTimestampValue */;
|
|
4480
4480
|
}
|
|
4481
4481
|
else if (isMaxValue(value)) {
|
|
4482
|
-
return 9007199254740991 /* MaxValue */;
|
|
4482
|
+
return 9007199254740991 /* TypeOrder.MaxValue */;
|
|
4483
4483
|
}
|
|
4484
|
-
return 10 /* ObjectValue */;
|
|
4484
|
+
return 10 /* TypeOrder.ObjectValue */;
|
|
4485
4485
|
}
|
|
4486
4486
|
else {
|
|
4487
4487
|
return fail();
|
|
@@ -4498,29 +4498,29 @@ function valueEquals(left, right) {
|
|
|
4498
4498
|
return false;
|
|
4499
4499
|
}
|
|
4500
4500
|
switch (leftType) {
|
|
4501
|
-
case 0 /* NullValue */:
|
|
4501
|
+
case 0 /* TypeOrder.NullValue */:
|
|
4502
4502
|
return true;
|
|
4503
|
-
case 1 /* BooleanValue */:
|
|
4503
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
4504
4504
|
return left.booleanValue === right.booleanValue;
|
|
4505
|
-
case 4 /* ServerTimestampValue */:
|
|
4505
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
4506
4506
|
return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
|
|
4507
|
-
case 3 /* TimestampValue */:
|
|
4507
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
4508
4508
|
return timestampEquals(left, right);
|
|
4509
|
-
case 5 /* StringValue */:
|
|
4509
|
+
case 5 /* TypeOrder.StringValue */:
|
|
4510
4510
|
return left.stringValue === right.stringValue;
|
|
4511
|
-
case 6 /* BlobValue */:
|
|
4511
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
4512
4512
|
return blobEquals(left, right);
|
|
4513
|
-
case 7 /* RefValue */:
|
|
4513
|
+
case 7 /* TypeOrder.RefValue */:
|
|
4514
4514
|
return left.referenceValue === right.referenceValue;
|
|
4515
|
-
case 8 /* GeoPointValue */:
|
|
4515
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
4516
4516
|
return geoPointEquals(left, right);
|
|
4517
|
-
case 2 /* NumberValue */:
|
|
4517
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
4518
4518
|
return numberEquals(left, right);
|
|
4519
|
-
case 9 /* ArrayValue */:
|
|
4519
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
4520
4520
|
return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
|
|
4521
|
-
case 10 /* ObjectValue */:
|
|
4521
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
4522
4522
|
return objectEquals(left, right);
|
|
4523
|
-
case 9007199254740991 /* MaxValue */:
|
|
4523
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
4524
4524
|
return true;
|
|
4525
4525
|
default:
|
|
4526
4526
|
return fail();
|
|
@@ -4593,28 +4593,28 @@ function valueCompare(left, right) {
|
|
|
4593
4593
|
return primitiveComparator(leftType, rightType);
|
|
4594
4594
|
}
|
|
4595
4595
|
switch (leftType) {
|
|
4596
|
-
case 0 /* NullValue */:
|
|
4597
|
-
case 9007199254740991 /* MaxValue */:
|
|
4596
|
+
case 0 /* TypeOrder.NullValue */:
|
|
4597
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
4598
4598
|
return 0;
|
|
4599
|
-
case 1 /* BooleanValue */:
|
|
4599
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
4600
4600
|
return primitiveComparator(left.booleanValue, right.booleanValue);
|
|
4601
|
-
case 2 /* NumberValue */:
|
|
4601
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
4602
4602
|
return compareNumbers(left, right);
|
|
4603
|
-
case 3 /* TimestampValue */:
|
|
4603
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
4604
4604
|
return compareTimestamps(left.timestampValue, right.timestampValue);
|
|
4605
|
-
case 4 /* ServerTimestampValue */:
|
|
4605
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
4606
4606
|
return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
|
|
4607
|
-
case 5 /* StringValue */:
|
|
4607
|
+
case 5 /* TypeOrder.StringValue */:
|
|
4608
4608
|
return primitiveComparator(left.stringValue, right.stringValue);
|
|
4609
|
-
case 6 /* BlobValue */:
|
|
4609
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
4610
4610
|
return compareBlobs(left.bytesValue, right.bytesValue);
|
|
4611
|
-
case 7 /* RefValue */:
|
|
4611
|
+
case 7 /* TypeOrder.RefValue */:
|
|
4612
4612
|
return compareReferences(left.referenceValue, right.referenceValue);
|
|
4613
|
-
case 8 /* GeoPointValue */:
|
|
4613
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
4614
4614
|
return compareGeoPoints(left.geoPointValue, right.geoPointValue);
|
|
4615
|
-
case 9 /* ArrayValue */:
|
|
4615
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
4616
4616
|
return compareArrays(left.arrayValue, right.arrayValue);
|
|
4617
|
-
case 10 /* ObjectValue */:
|
|
4617
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
4618
4618
|
return compareMaps(left.mapValue, right.mapValue);
|
|
4619
4619
|
default:
|
|
4620
4620
|
throw fail();
|
|
@@ -5167,11 +5167,12 @@ function extractFieldMask(value) {
|
|
|
5167
5167
|
* from all views.
|
|
5168
5168
|
*/
|
|
5169
5169
|
class MutableDocument {
|
|
5170
|
-
constructor(key, documentType, version, readTime, data, documentState) {
|
|
5170
|
+
constructor(key, documentType, version, readTime, createTime, data, documentState) {
|
|
5171
5171
|
this.key = key;
|
|
5172
5172
|
this.documentType = documentType;
|
|
5173
5173
|
this.version = version;
|
|
5174
5174
|
this.readTime = readTime;
|
|
5175
|
+
this.createTime = createTime;
|
|
5175
5176
|
this.data = data;
|
|
5176
5177
|
this.documentState = documentState;
|
|
5177
5178
|
}
|
|
@@ -5180,18 +5181,27 @@ class MutableDocument {
|
|
|
5180
5181
|
* base document for mutations.
|
|
5181
5182
|
*/
|
|
5182
5183
|
static newInvalidDocument(documentKey) {
|
|
5183
|
-
return new MutableDocument(documentKey, 0 /* INVALID */,
|
|
5184
|
+
return new MutableDocument(documentKey, 0 /* DocumentType.INVALID */,
|
|
5185
|
+
/* version */ SnapshotVersion.min(),
|
|
5186
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5187
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
5184
5188
|
}
|
|
5185
5189
|
/**
|
|
5186
5190
|
* Creates a new document that is known to exist with the given data at the
|
|
5187
5191
|
* given version.
|
|
5188
5192
|
*/
|
|
5189
|
-
static newFoundDocument(documentKey, version, value) {
|
|
5190
|
-
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */,
|
|
5193
|
+
static newFoundDocument(documentKey, version, createTime, value) {
|
|
5194
|
+
return new MutableDocument(documentKey, 1 /* DocumentType.FOUND_DOCUMENT */,
|
|
5195
|
+
/* version */ version,
|
|
5196
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5197
|
+
/* createTime */ createTime, value, 0 /* DocumentState.SYNCED */);
|
|
5191
5198
|
}
|
|
5192
5199
|
/** Creates a new document that is known to not exist at the given version. */
|
|
5193
5200
|
static newNoDocument(documentKey, version) {
|
|
5194
|
-
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */,
|
|
5201
|
+
return new MutableDocument(documentKey, 2 /* DocumentType.NO_DOCUMENT */,
|
|
5202
|
+
/* version */ version,
|
|
5203
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5204
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
5195
5205
|
}
|
|
5196
5206
|
/**
|
|
5197
5207
|
* Creates a new document that is known to exist at the given version but
|
|
@@ -5199,17 +5209,30 @@ class MutableDocument {
|
|
|
5199
5209
|
* base document).
|
|
5200
5210
|
*/
|
|
5201
5211
|
static newUnknownDocument(documentKey, version) {
|
|
5202
|
-
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */,
|
|
5212
|
+
return new MutableDocument(documentKey, 3 /* DocumentType.UNKNOWN_DOCUMENT */,
|
|
5213
|
+
/* version */ version,
|
|
5214
|
+
/* readTime */ SnapshotVersion.min(),
|
|
5215
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */);
|
|
5203
5216
|
}
|
|
5204
5217
|
/**
|
|
5205
5218
|
* Changes the document type to indicate that it exists and that its version
|
|
5206
5219
|
* and data are known.
|
|
5207
5220
|
*/
|
|
5208
5221
|
convertToFoundDocument(version, value) {
|
|
5222
|
+
// If a document is switching state from being an invalid or deleted
|
|
5223
|
+
// document to a valid (FOUND_DOCUMENT) document, either due to receiving an
|
|
5224
|
+
// update from Watch or due to applying a local set mutation on top
|
|
5225
|
+
// of a deleted document, our best guess about its createTime would be the
|
|
5226
|
+
// version at which the document transitioned to a FOUND_DOCUMENT.
|
|
5227
|
+
if (this.createTime.isEqual(SnapshotVersion.min()) &&
|
|
5228
|
+
(this.documentType === 2 /* DocumentType.NO_DOCUMENT */ ||
|
|
5229
|
+
this.documentType === 0 /* DocumentType.INVALID */)) {
|
|
5230
|
+
this.createTime = version;
|
|
5231
|
+
}
|
|
5209
5232
|
this.version = version;
|
|
5210
|
-
this.documentType = 1 /* FOUND_DOCUMENT */;
|
|
5233
|
+
this.documentType = 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
5211
5234
|
this.data = value;
|
|
5212
|
-
this.documentState = 0 /* SYNCED */;
|
|
5235
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
5213
5236
|
return this;
|
|
5214
5237
|
}
|
|
5215
5238
|
/**
|
|
@@ -5218,9 +5241,9 @@ class MutableDocument {
|
|
|
5218
5241
|
*/
|
|
5219
5242
|
convertToNoDocument(version) {
|
|
5220
5243
|
this.version = version;
|
|
5221
|
-
this.documentType = 2 /* NO_DOCUMENT */;
|
|
5244
|
+
this.documentType = 2 /* DocumentType.NO_DOCUMENT */;
|
|
5222
5245
|
this.data = ObjectValue.empty();
|
|
5223
|
-
this.documentState = 0 /* SYNCED */;
|
|
5246
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
5224
5247
|
return this;
|
|
5225
5248
|
}
|
|
5226
5249
|
/**
|
|
@@ -5230,17 +5253,17 @@ class MutableDocument {
|
|
|
5230
5253
|
*/
|
|
5231
5254
|
convertToUnknownDocument(version) {
|
|
5232
5255
|
this.version = version;
|
|
5233
|
-
this.documentType = 3 /* UNKNOWN_DOCUMENT */;
|
|
5256
|
+
this.documentType = 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
5234
5257
|
this.data = ObjectValue.empty();
|
|
5235
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5258
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5236
5259
|
return this;
|
|
5237
5260
|
}
|
|
5238
5261
|
setHasCommittedMutations() {
|
|
5239
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5262
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5240
5263
|
return this;
|
|
5241
5264
|
}
|
|
5242
5265
|
setHasLocalMutations() {
|
|
5243
|
-
this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
|
|
5266
|
+
this.documentState = 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
5244
5267
|
this.version = SnapshotVersion.min();
|
|
5245
5268
|
return this;
|
|
5246
5269
|
}
|
|
@@ -5249,25 +5272,25 @@ class MutableDocument {
|
|
|
5249
5272
|
return this;
|
|
5250
5273
|
}
|
|
5251
5274
|
get hasLocalMutations() {
|
|
5252
|
-
return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
|
|
5275
|
+
return this.documentState === 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
5253
5276
|
}
|
|
5254
5277
|
get hasCommittedMutations() {
|
|
5255
|
-
return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
5278
|
+
return this.documentState === 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
5256
5279
|
}
|
|
5257
5280
|
get hasPendingWrites() {
|
|
5258
5281
|
return this.hasLocalMutations || this.hasCommittedMutations;
|
|
5259
5282
|
}
|
|
5260
5283
|
isValidDocument() {
|
|
5261
|
-
return this.documentType !== 0 /* INVALID */;
|
|
5284
|
+
return this.documentType !== 0 /* DocumentType.INVALID */;
|
|
5262
5285
|
}
|
|
5263
5286
|
isFoundDocument() {
|
|
5264
|
-
return this.documentType === 1 /* FOUND_DOCUMENT */;
|
|
5287
|
+
return this.documentType === 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
5265
5288
|
}
|
|
5266
5289
|
isNoDocument() {
|
|
5267
|
-
return this.documentType === 2 /* NO_DOCUMENT */;
|
|
5290
|
+
return this.documentType === 2 /* DocumentType.NO_DOCUMENT */;
|
|
5268
5291
|
}
|
|
5269
5292
|
isUnknownDocument() {
|
|
5270
|
-
return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
|
|
5293
|
+
return this.documentType === 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
5271
5294
|
}
|
|
5272
5295
|
isEqual(other) {
|
|
5273
5296
|
return (other instanceof MutableDocument &&
|
|
@@ -5278,10 +5301,11 @@ class MutableDocument {
|
|
|
5278
5301
|
this.data.isEqual(other.data));
|
|
5279
5302
|
}
|
|
5280
5303
|
mutableCopy() {
|
|
5281
|
-
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
|
|
5304
|
+
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.createTime, this.data.clone(), this.documentState);
|
|
5282
5305
|
}
|
|
5283
5306
|
toString() {
|
|
5284
5307
|
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
5308
|
+
`{createTime: ${this.createTime}}), ` +
|
|
5285
5309
|
`{documentType: ${this.documentType}}), ` +
|
|
5286
5310
|
`{documentState: ${this.documentState}})`);
|
|
5287
5311
|
}
|
|
@@ -5303,7 +5327,7 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
5303
5327
|
|
|
5304
5328
|
/**
|
|
5305
5329
|
* @license
|
|
5306
|
-
* Copyright
|
|
5330
|
+
* Copyright 2022 Google LLC
|
|
5307
5331
|
*
|
|
5308
5332
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5309
5333
|
* you may not use this file except in compliance with the License.
|
|
@@ -5317,453 +5341,353 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
5317
5341
|
* See the License for the specific language governing permissions and
|
|
5318
5342
|
* limitations under the License.
|
|
5319
5343
|
*/
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5344
|
+
/**
|
|
5345
|
+
* Represents a bound of a query.
|
|
5346
|
+
*
|
|
5347
|
+
* The bound is specified with the given components representing a position and
|
|
5348
|
+
* whether it's just before or just after the position (relative to whatever the
|
|
5349
|
+
* query order is).
|
|
5350
|
+
*
|
|
5351
|
+
* The position represents a logical index position for a query. It's a prefix
|
|
5352
|
+
* of values for the (potentially implicit) order by clauses of a query.
|
|
5353
|
+
*
|
|
5354
|
+
* Bound provides a function to determine whether a document comes before or
|
|
5355
|
+
* after a bound. This is influenced by whether the position is just before or
|
|
5356
|
+
* just after the provided values.
|
|
5357
|
+
*/
|
|
5358
|
+
class Bound {
|
|
5359
|
+
constructor(position, inclusive) {
|
|
5360
|
+
this.position = position;
|
|
5361
|
+
this.inclusive = inclusive;
|
|
5331
5362
|
}
|
|
5332
5363
|
}
|
|
5364
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
5365
|
+
let comparison = 0;
|
|
5366
|
+
for (let i = 0; i < bound.position.length; i++) {
|
|
5367
|
+
const orderByComponent = orderBy[i];
|
|
5368
|
+
const component = bound.position[i];
|
|
5369
|
+
if (orderByComponent.field.isKeyField()) {
|
|
5370
|
+
comparison = DocumentKey.comparator(DocumentKey.fromName(component.referenceValue), doc.key);
|
|
5371
|
+
}
|
|
5372
|
+
else {
|
|
5373
|
+
const docValue = doc.data.field(orderByComponent.field);
|
|
5374
|
+
comparison = valueCompare(component, docValue);
|
|
5375
|
+
}
|
|
5376
|
+
if (orderByComponent.dir === "desc" /* Direction.DESCENDING */) {
|
|
5377
|
+
comparison = comparison * -1;
|
|
5378
|
+
}
|
|
5379
|
+
if (comparison !== 0) {
|
|
5380
|
+
break;
|
|
5381
|
+
}
|
|
5382
|
+
}
|
|
5383
|
+
return comparison;
|
|
5384
|
+
}
|
|
5333
5385
|
/**
|
|
5334
|
-
*
|
|
5335
|
-
*
|
|
5386
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5387
|
+
* order.
|
|
5388
|
+
*/
|
|
5389
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5390
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5391
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5392
|
+
}
|
|
5393
|
+
/**
|
|
5394
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5395
|
+
* order.
|
|
5396
|
+
*/
|
|
5397
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5398
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5399
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
5400
|
+
}
|
|
5401
|
+
function boundEquals(left, right) {
|
|
5402
|
+
if (left === null) {
|
|
5403
|
+
return right === null;
|
|
5404
|
+
}
|
|
5405
|
+
else if (right === null) {
|
|
5406
|
+
return false;
|
|
5407
|
+
}
|
|
5408
|
+
if (left.inclusive !== right.inclusive ||
|
|
5409
|
+
left.position.length !== right.position.length) {
|
|
5410
|
+
return false;
|
|
5411
|
+
}
|
|
5412
|
+
for (let i = 0; i < left.position.length; i++) {
|
|
5413
|
+
const leftPosition = left.position[i];
|
|
5414
|
+
const rightPosition = right.position[i];
|
|
5415
|
+
if (!valueEquals(leftPosition, rightPosition)) {
|
|
5416
|
+
return false;
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
return true;
|
|
5420
|
+
}
|
|
5421
|
+
|
|
5422
|
+
/**
|
|
5423
|
+
* @license
|
|
5424
|
+
* Copyright 2022 Google LLC
|
|
5336
5425
|
*
|
|
5337
|
-
*
|
|
5338
|
-
*
|
|
5339
|
-
*
|
|
5426
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5427
|
+
* you may not use this file except in compliance with the License.
|
|
5428
|
+
* You may obtain a copy of the License at
|
|
5429
|
+
*
|
|
5430
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5431
|
+
*
|
|
5432
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5433
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5434
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5435
|
+
* See the License for the specific language governing permissions and
|
|
5436
|
+
* limitations under the License.
|
|
5340
5437
|
*/
|
|
5341
|
-
|
|
5342
|
-
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
5438
|
+
class Filter {
|
|
5343
5439
|
}
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5440
|
+
class FieldFilter extends Filter {
|
|
5441
|
+
constructor(field, op, value) {
|
|
5442
|
+
super();
|
|
5443
|
+
this.field = field;
|
|
5444
|
+
this.op = op;
|
|
5445
|
+
this.value = value;
|
|
5446
|
+
}
|
|
5447
|
+
/**
|
|
5448
|
+
* Creates a filter based on the provided arguments.
|
|
5449
|
+
*/
|
|
5450
|
+
static create(field, op, value) {
|
|
5451
|
+
if (field.isKeyField()) {
|
|
5452
|
+
if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
5453
|
+
return this.createKeyFieldInFilter(field, op, value);
|
|
5454
|
+
}
|
|
5455
|
+
else {
|
|
5456
|
+
return new KeyFieldFilter(field, op, value);
|
|
5457
|
+
}
|
|
5350
5458
|
}
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
str += '|ob:';
|
|
5354
|
-
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
5355
|
-
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
5356
|
-
str += '|l:';
|
|
5357
|
-
str += targetImpl.limit;
|
|
5459
|
+
else if (op === "array-contains" /* Operator.ARRAY_CONTAINS */) {
|
|
5460
|
+
return new ArrayContainsFilter(field, value);
|
|
5358
5461
|
}
|
|
5359
|
-
if (
|
|
5360
|
-
|
|
5361
|
-
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
5362
|
-
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5462
|
+
else if (op === "in" /* Operator.IN */) {
|
|
5463
|
+
return new InFilter(field, value);
|
|
5363
5464
|
}
|
|
5364
|
-
if (
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5465
|
+
else if (op === "not-in" /* Operator.NOT_IN */) {
|
|
5466
|
+
return new NotInFilter(field, value);
|
|
5467
|
+
}
|
|
5468
|
+
else if (op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
5469
|
+
return new ArrayContainsAnyFilter(field, value);
|
|
5470
|
+
}
|
|
5471
|
+
else {
|
|
5472
|
+
return new FieldFilter(field, op, value);
|
|
5368
5473
|
}
|
|
5369
|
-
targetImpl.memoizedCanonicalId = str;
|
|
5370
5474
|
}
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
if (target.collectionGroup !== null) {
|
|
5376
|
-
str += ' collectionGroup=' + target.collectionGroup;
|
|
5475
|
+
static createKeyFieldInFilter(field, op, value) {
|
|
5476
|
+
return op === "in" /* Operator.IN */
|
|
5477
|
+
? new KeyFieldInFilter(field, value)
|
|
5478
|
+
: new KeyFieldNotInFilter(field, value);
|
|
5377
5479
|
}
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5480
|
+
matches(doc) {
|
|
5481
|
+
const other = doc.data.field(this.field);
|
|
5482
|
+
// Types do not have to match in NOT_EQUAL filters.
|
|
5483
|
+
if (this.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
5484
|
+
return (other !== null &&
|
|
5485
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
5486
|
+
}
|
|
5487
|
+
// Only compare types with matching backend order (such as double and int).
|
|
5488
|
+
return (other !== null &&
|
|
5489
|
+
typeOrder(this.value) === typeOrder(other) &&
|
|
5490
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
5382
5491
|
}
|
|
5383
|
-
|
|
5384
|
-
|
|
5492
|
+
matchesComparison(comparison) {
|
|
5493
|
+
switch (this.op) {
|
|
5494
|
+
case "<" /* Operator.LESS_THAN */:
|
|
5495
|
+
return comparison < 0;
|
|
5496
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
5497
|
+
return comparison <= 0;
|
|
5498
|
+
case "==" /* Operator.EQUAL */:
|
|
5499
|
+
return comparison === 0;
|
|
5500
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
5501
|
+
return comparison !== 0;
|
|
5502
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
5503
|
+
return comparison > 0;
|
|
5504
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
5505
|
+
return comparison >= 0;
|
|
5506
|
+
default:
|
|
5507
|
+
return fail();
|
|
5508
|
+
}
|
|
5385
5509
|
}
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
.
|
|
5389
|
-
.
|
|
5510
|
+
isInequality() {
|
|
5511
|
+
return ([
|
|
5512
|
+
"<" /* Operator.LESS_THAN */,
|
|
5513
|
+
"<=" /* Operator.LESS_THAN_OR_EQUAL */,
|
|
5514
|
+
">" /* Operator.GREATER_THAN */,
|
|
5515
|
+
">=" /* Operator.GREATER_THAN_OR_EQUAL */,
|
|
5516
|
+
"!=" /* Operator.NOT_EQUAL */,
|
|
5517
|
+
"not-in" /* Operator.NOT_IN */
|
|
5518
|
+
].indexOf(this.op) >= 0);
|
|
5390
5519
|
}
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
5394
|
-
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5520
|
+
getFlattenedFilters() {
|
|
5521
|
+
return [this];
|
|
5395
5522
|
}
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5523
|
+
getFilters() {
|
|
5524
|
+
return [this];
|
|
5525
|
+
}
|
|
5526
|
+
getFirstInequalityField() {
|
|
5527
|
+
if (this.isInequality()) {
|
|
5528
|
+
return this.field;
|
|
5529
|
+
}
|
|
5530
|
+
return null;
|
|
5400
5531
|
}
|
|
5401
|
-
return `Target(${str})`;
|
|
5402
5532
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5533
|
+
class CompositeFilter extends Filter {
|
|
5534
|
+
constructor(filters, op) {
|
|
5535
|
+
super();
|
|
5536
|
+
this.filters = filters;
|
|
5537
|
+
this.op = op;
|
|
5538
|
+
this.memoizedFlattenedFilters = null;
|
|
5406
5539
|
}
|
|
5407
|
-
|
|
5408
|
-
|
|
5540
|
+
/**
|
|
5541
|
+
* Creates a filter based on the provided arguments.
|
|
5542
|
+
*/
|
|
5543
|
+
static create(filters, op) {
|
|
5544
|
+
return new CompositeFilter(filters, op);
|
|
5409
5545
|
}
|
|
5410
|
-
|
|
5411
|
-
if (
|
|
5412
|
-
return false
|
|
5546
|
+
matches(doc) {
|
|
5547
|
+
if (compositeFilterIsConjunction(this)) {
|
|
5548
|
+
// For conjunctions, all filters must match, so return false if any filter doesn't match.
|
|
5549
|
+
return this.filters.find(filter => !filter.matches(doc)) === undefined;
|
|
5550
|
+
}
|
|
5551
|
+
else {
|
|
5552
|
+
// For disjunctions, at least one filter should match.
|
|
5553
|
+
return this.filters.find(filter => filter.matches(doc)) !== undefined;
|
|
5413
5554
|
}
|
|
5414
5555
|
}
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
for (let i = 0; i < left.filters.length; i++) {
|
|
5419
|
-
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
5420
|
-
return false;
|
|
5556
|
+
getFlattenedFilters() {
|
|
5557
|
+
if (this.memoizedFlattenedFilters !== null) {
|
|
5558
|
+
return this.memoizedFlattenedFilters;
|
|
5421
5559
|
}
|
|
5560
|
+
this.memoizedFlattenedFilters = this.filters.reduce((result, subfilter) => {
|
|
5561
|
+
return result.concat(subfilter.getFlattenedFilters());
|
|
5562
|
+
}, []);
|
|
5563
|
+
return this.memoizedFlattenedFilters;
|
|
5422
5564
|
}
|
|
5423
|
-
|
|
5424
|
-
|
|
5565
|
+
// Returns a mutable copy of `this.filters`
|
|
5566
|
+
getFilters() {
|
|
5567
|
+
return Object.assign([], this.filters);
|
|
5425
5568
|
}
|
|
5426
|
-
|
|
5427
|
-
|
|
5569
|
+
getFirstInequalityField() {
|
|
5570
|
+
const found = this.findFirstMatchingFilter(filter => filter.isInequality());
|
|
5571
|
+
if (found !== null) {
|
|
5572
|
+
return found.field;
|
|
5573
|
+
}
|
|
5574
|
+
return null;
|
|
5428
5575
|
}
|
|
5429
|
-
|
|
5430
|
-
|
|
5576
|
+
// Performs a depth-first search to find and return the first FieldFilter in the composite filter
|
|
5577
|
+
// that satisfies the predicate. Returns `null` if none of the FieldFilters satisfy the
|
|
5578
|
+
// predicate.
|
|
5579
|
+
findFirstMatchingFilter(predicate) {
|
|
5580
|
+
for (const fieldFilter of this.getFlattenedFilters()) {
|
|
5581
|
+
if (predicate(fieldFilter)) {
|
|
5582
|
+
return fieldFilter;
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5585
|
+
return null;
|
|
5431
5586
|
}
|
|
5432
|
-
return boundEquals(left.endAt, right.endAt);
|
|
5433
5587
|
}
|
|
5434
|
-
function
|
|
5435
|
-
return
|
|
5436
|
-
target.collectionGroup === null &&
|
|
5437
|
-
target.filters.length === 0);
|
|
5588
|
+
function compositeFilterIsConjunction(compositeFilter) {
|
|
5589
|
+
return compositeFilter.op === "and" /* CompositeOperator.AND */;
|
|
5438
5590
|
}
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
|
|
5591
|
+
function compositeFilterIsDisjunction(compositeFilter) {
|
|
5592
|
+
return compositeFilter.op === "or" /* CompositeOperator.OR */;
|
|
5442
5593
|
}
|
|
5443
5594
|
/**
|
|
5444
|
-
* Returns
|
|
5445
|
-
* filters. Returns `null` if there are no such filters.
|
|
5595
|
+
* Returns true if this filter is a conjunction of field filters only. Returns false otherwise.
|
|
5446
5596
|
*/
|
|
5447
|
-
function
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
return null;
|
|
5451
|
-
}
|
|
5452
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5453
|
-
switch (fieldFilter.op) {
|
|
5454
|
-
case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
|
|
5455
|
-
return fieldFilter.value.arrayValue.values || [];
|
|
5456
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
5457
|
-
return [fieldFilter.value];
|
|
5458
|
-
// Remaining filters are not array filters.
|
|
5459
|
-
}
|
|
5460
|
-
}
|
|
5461
|
-
return null;
|
|
5597
|
+
function compositeFilterIsFlatConjunction(compositeFilter) {
|
|
5598
|
+
return (compositeFilterIsFlat(compositeFilter) &&
|
|
5599
|
+
compositeFilterIsConjunction(compositeFilter));
|
|
5462
5600
|
}
|
|
5463
5601
|
/**
|
|
5464
|
-
* Returns
|
|
5465
|
-
* `null` if there are no such filters.
|
|
5602
|
+
* Returns true if this filter does not contain any composite filters. Returns false otherwise.
|
|
5466
5603
|
*/
|
|
5467
|
-
function
|
|
5468
|
-
const
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
switch (fieldFilter.op) {
|
|
5472
|
-
case "==" /* EQUAL */:
|
|
5473
|
-
case "in" /* IN */:
|
|
5474
|
-
// Encode equality prefix, which is encoded in the index value before
|
|
5475
|
-
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
|
|
5476
|
-
// `value != 'ab'`).
|
|
5477
|
-
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5478
|
-
break;
|
|
5479
|
-
case "not-in" /* NOT_IN */:
|
|
5480
|
-
case "!=" /* NOT_EQUAL */:
|
|
5481
|
-
// NotIn/NotEqual is always a suffix. There cannot be any remaining
|
|
5482
|
-
// segments and hence we can return early here.
|
|
5483
|
-
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5484
|
-
return Array.from(values.values());
|
|
5485
|
-
// Remaining filters cannot be used as notIn bounds.
|
|
5486
|
-
}
|
|
5604
|
+
function compositeFilterIsFlat(compositeFilter) {
|
|
5605
|
+
for (const filter of compositeFilter.filters) {
|
|
5606
|
+
if (filter instanceof CompositeFilter) {
|
|
5607
|
+
return false;
|
|
5487
5608
|
}
|
|
5488
5609
|
}
|
|
5489
|
-
return
|
|
5610
|
+
return true;
|
|
5490
5611
|
}
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5612
|
+
function canonifyFilter(filter) {
|
|
5613
|
+
if (filter instanceof FieldFilter) {
|
|
5614
|
+
// TODO(b/29183165): Technically, this won't be unique if two values have
|
|
5615
|
+
// the same description, such as the int 3 and the string "3". So we should
|
|
5616
|
+
// add the types in here somehow, too.
|
|
5617
|
+
return (filter.field.canonicalString() +
|
|
5618
|
+
filter.op.toString() +
|
|
5619
|
+
canonicalId(filter.value));
|
|
5620
|
+
}
|
|
5621
|
+
else if (compositeFilterIsFlatConjunction(filter)) {
|
|
5622
|
+
// Older SDK versions use an implicit AND operation between their filters.
|
|
5623
|
+
// In the new SDK versions, the developer may use an explicit AND filter.
|
|
5624
|
+
// To stay consistent with the old usages, we add a special case to ensure
|
|
5625
|
+
// the canonical ID for these two are the same. For example:
|
|
5626
|
+
// `col.whereEquals("a", 1).whereEquals("b", 2)` should have the same
|
|
5627
|
+
// canonical ID as `col.where(and(equals("a",1), equals("b",2)))`.
|
|
5628
|
+
return filter.filters.map(filter => canonifyFilter(filter)).join(',');
|
|
5507
5629
|
}
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
*/
|
|
5515
|
-
function targetGetUpperBound(target, fieldIndex) {
|
|
5516
|
-
const values = [];
|
|
5517
|
-
let inclusive = true;
|
|
5518
|
-
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
5519
|
-
// endAt.
|
|
5520
|
-
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5521
|
-
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
5522
|
-
? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
|
|
5523
|
-
: targetGetAscendingBound(target, segment.fieldPath, target.endAt);
|
|
5524
|
-
values.push(segmentBound.value);
|
|
5525
|
-
inclusive && (inclusive = segmentBound.inclusive);
|
|
5630
|
+
else {
|
|
5631
|
+
// filter instanceof CompositeFilter
|
|
5632
|
+
const canonicalIdsString = filter.filters
|
|
5633
|
+
.map(filter => canonifyFilter(filter))
|
|
5634
|
+
.join(',');
|
|
5635
|
+
return `${filter.op}(${canonicalIdsString})`;
|
|
5526
5636
|
}
|
|
5527
|
-
return new Bound(values, inclusive);
|
|
5528
5637
|
}
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
*/
|
|
5533
|
-
function targetGetAscendingBound(target, fieldPath, bound) {
|
|
5534
|
-
let value = MIN_VALUE;
|
|
5535
|
-
let inclusive = true;
|
|
5536
|
-
// Process all filters to find a value for the current field segment
|
|
5537
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
5538
|
-
let filterValue = MIN_VALUE;
|
|
5539
|
-
let filterInclusive = true;
|
|
5540
|
-
switch (fieldFilter.op) {
|
|
5541
|
-
case "<" /* LESS_THAN */:
|
|
5542
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5543
|
-
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
5544
|
-
break;
|
|
5545
|
-
case "==" /* EQUAL */:
|
|
5546
|
-
case "in" /* IN */:
|
|
5547
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5548
|
-
filterValue = fieldFilter.value;
|
|
5549
|
-
break;
|
|
5550
|
-
case ">" /* GREATER_THAN */:
|
|
5551
|
-
filterValue = fieldFilter.value;
|
|
5552
|
-
filterInclusive = false;
|
|
5553
|
-
break;
|
|
5554
|
-
case "!=" /* NOT_EQUAL */:
|
|
5555
|
-
case "not-in" /* NOT_IN */:
|
|
5556
|
-
filterValue = MIN_VALUE;
|
|
5557
|
-
break;
|
|
5558
|
-
// Remaining filters cannot be used as lower bounds.
|
|
5559
|
-
}
|
|
5560
|
-
if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
|
|
5561
|
-
value = filterValue;
|
|
5562
|
-
inclusive = filterInclusive;
|
|
5563
|
-
}
|
|
5564
|
-
}
|
|
5565
|
-
// If there is an additional bound, compare the values against the existing
|
|
5566
|
-
// range to see if we can narrow the scope.
|
|
5567
|
-
if (bound !== null) {
|
|
5568
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
5569
|
-
const orderBy = target.orderBy[i];
|
|
5570
|
-
if (orderBy.field.isEqual(fieldPath)) {
|
|
5571
|
-
const cursorValue = bound.position[i];
|
|
5572
|
-
if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
|
|
5573
|
-
value = cursorValue;
|
|
5574
|
-
inclusive = bound.inclusive;
|
|
5575
|
-
}
|
|
5576
|
-
break;
|
|
5577
|
-
}
|
|
5578
|
-
}
|
|
5638
|
+
function filterEquals(f1, f2) {
|
|
5639
|
+
if (f1 instanceof FieldFilter) {
|
|
5640
|
+
return fieldFilterEquals(f1, f2);
|
|
5579
5641
|
}
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
/**
|
|
5583
|
-
* Returns the value to use as the upper bound for ascending index segment at
|
|
5584
|
-
* the provided `fieldPath` (or the lower bound for a descending segment).
|
|
5585
|
-
*/
|
|
5586
|
-
function targetGetDescendingBound(target, fieldPath, bound) {
|
|
5587
|
-
let value = MAX_VALUE;
|
|
5588
|
-
let inclusive = true;
|
|
5589
|
-
// Process all filters to find a value for the current field segment
|
|
5590
|
-
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
5591
|
-
let filterValue = MAX_VALUE;
|
|
5592
|
-
let filterInclusive = true;
|
|
5593
|
-
switch (fieldFilter.op) {
|
|
5594
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5595
|
-
case ">" /* GREATER_THAN */:
|
|
5596
|
-
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
5597
|
-
filterInclusive = false;
|
|
5598
|
-
break;
|
|
5599
|
-
case "==" /* EQUAL */:
|
|
5600
|
-
case "in" /* IN */:
|
|
5601
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5602
|
-
filterValue = fieldFilter.value;
|
|
5603
|
-
break;
|
|
5604
|
-
case "<" /* LESS_THAN */:
|
|
5605
|
-
filterValue = fieldFilter.value;
|
|
5606
|
-
filterInclusive = false;
|
|
5607
|
-
break;
|
|
5608
|
-
case "!=" /* NOT_EQUAL */:
|
|
5609
|
-
case "not-in" /* NOT_IN */:
|
|
5610
|
-
filterValue = MAX_VALUE;
|
|
5611
|
-
break;
|
|
5612
|
-
// Remaining filters cannot be used as upper bounds.
|
|
5613
|
-
}
|
|
5614
|
-
if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
|
|
5615
|
-
value = filterValue;
|
|
5616
|
-
inclusive = filterInclusive;
|
|
5617
|
-
}
|
|
5642
|
+
else if (f1 instanceof CompositeFilter) {
|
|
5643
|
+
return compositeFilterEquals(f1, f2);
|
|
5618
5644
|
}
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
if (bound !== null) {
|
|
5622
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
5623
|
-
const orderBy = target.orderBy[i];
|
|
5624
|
-
if (orderBy.field.isEqual(fieldPath)) {
|
|
5625
|
-
const cursorValue = bound.position[i];
|
|
5626
|
-
if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
|
|
5627
|
-
value = cursorValue;
|
|
5628
|
-
inclusive = bound.inclusive;
|
|
5629
|
-
}
|
|
5630
|
-
break;
|
|
5631
|
-
}
|
|
5632
|
-
}
|
|
5645
|
+
else {
|
|
5646
|
+
fail();
|
|
5633
5647
|
}
|
|
5634
|
-
return { value, inclusive };
|
|
5635
5648
|
}
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
// ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
|
|
5649
|
-
// For instance, it is possible to have an index for "a ARRAY a ASC". Even
|
|
5650
|
-
// though these are on the same field, they should be counted as two
|
|
5651
|
-
// separate segments in an index.
|
|
5652
|
-
if (fieldFilter.op === "array-contains" /* ARRAY_CONTAINS */ ||
|
|
5653
|
-
fieldFilter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
5654
|
-
hasArraySegment = true;
|
|
5655
|
-
}
|
|
5656
|
-
else {
|
|
5657
|
-
fields = fields.add(fieldFilter.field);
|
|
5658
|
-
}
|
|
5659
|
-
}
|
|
5660
|
-
for (const orderBy of target.orderBy) {
|
|
5661
|
-
// __name__ is not an explicit segment of any index, so we don't need to
|
|
5662
|
-
// count it.
|
|
5663
|
-
if (!orderBy.field.isKeyField()) {
|
|
5664
|
-
fields = fields.add(orderBy.field);
|
|
5665
|
-
}
|
|
5649
|
+
function fieldFilterEquals(f1, f2) {
|
|
5650
|
+
return (f2 instanceof FieldFilter &&
|
|
5651
|
+
f1.op === f2.op &&
|
|
5652
|
+
f1.field.isEqual(f2.field) &&
|
|
5653
|
+
valueEquals(f1.value, f2.value));
|
|
5654
|
+
}
|
|
5655
|
+
function compositeFilterEquals(f1, f2) {
|
|
5656
|
+
if (f2 instanceof CompositeFilter &&
|
|
5657
|
+
f1.op === f2.op &&
|
|
5658
|
+
f1.filters.length === f2.filters.length) {
|
|
5659
|
+
const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
|
|
5660
|
+
return subFiltersMatch;
|
|
5666
5661
|
}
|
|
5667
|
-
return
|
|
5662
|
+
return false;
|
|
5668
5663
|
}
|
|
5669
|
-
|
|
5664
|
+
/**
|
|
5665
|
+
* Returns a new composite filter that contains all filter from
|
|
5666
|
+
* `compositeFilter` plus all the given filters in `otherFilters`.
|
|
5667
|
+
*/
|
|
5668
|
+
function compositeFilterWithAddedFilters(compositeFilter, otherFilters) {
|
|
5669
|
+
const mergedFilters = compositeFilter.filters.concat(otherFilters);
|
|
5670
|
+
return CompositeFilter.create(mergedFilters, compositeFilter.op);
|
|
5670
5671
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
this.op = op;
|
|
5676
|
-
this.value = value;
|
|
5677
|
-
}
|
|
5678
|
-
/**
|
|
5679
|
-
* Creates a filter based on the provided arguments.
|
|
5680
|
-
*/
|
|
5681
|
-
static create(field, op, value) {
|
|
5682
|
-
if (field.isKeyField()) {
|
|
5683
|
-
if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
5684
|
-
return this.createKeyFieldInFilter(field, op, value);
|
|
5685
|
-
}
|
|
5686
|
-
else {
|
|
5687
|
-
return new KeyFieldFilter(field, op, value);
|
|
5688
|
-
}
|
|
5689
|
-
}
|
|
5690
|
-
else if (op === "array-contains" /* ARRAY_CONTAINS */) {
|
|
5691
|
-
return new ArrayContainsFilter(field, value);
|
|
5692
|
-
}
|
|
5693
|
-
else if (op === "in" /* IN */) {
|
|
5694
|
-
return new InFilter(field, value);
|
|
5695
|
-
}
|
|
5696
|
-
else if (op === "not-in" /* NOT_IN */) {
|
|
5697
|
-
return new NotInFilter(field, value);
|
|
5698
|
-
}
|
|
5699
|
-
else if (op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
5700
|
-
return new ArrayContainsAnyFilter(field, value);
|
|
5701
|
-
}
|
|
5702
|
-
else {
|
|
5703
|
-
return new FieldFilter(field, op, value);
|
|
5704
|
-
}
|
|
5705
|
-
}
|
|
5706
|
-
static createKeyFieldInFilter(field, op, value) {
|
|
5707
|
-
return op === "in" /* IN */
|
|
5708
|
-
? new KeyFieldInFilter(field, value)
|
|
5709
|
-
: new KeyFieldNotInFilter(field, value);
|
|
5710
|
-
}
|
|
5711
|
-
matches(doc) {
|
|
5712
|
-
const other = doc.data.field(this.field);
|
|
5713
|
-
// Types do not have to match in NOT_EQUAL filters.
|
|
5714
|
-
if (this.op === "!=" /* NOT_EQUAL */) {
|
|
5715
|
-
return (other !== null &&
|
|
5716
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
5717
|
-
}
|
|
5718
|
-
// Only compare types with matching backend order (such as double and int).
|
|
5719
|
-
return (other !== null &&
|
|
5720
|
-
typeOrder(this.value) === typeOrder(other) &&
|
|
5721
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
5672
|
+
/** Returns a debug description for `filter`. */
|
|
5673
|
+
function stringifyFilter(filter) {
|
|
5674
|
+
if (filter instanceof FieldFilter) {
|
|
5675
|
+
return stringifyFieldFilter(filter);
|
|
5722
5676
|
}
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
case "<" /* LESS_THAN */:
|
|
5726
|
-
return comparison < 0;
|
|
5727
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
5728
|
-
return comparison <= 0;
|
|
5729
|
-
case "==" /* EQUAL */:
|
|
5730
|
-
return comparison === 0;
|
|
5731
|
-
case "!=" /* NOT_EQUAL */:
|
|
5732
|
-
return comparison !== 0;
|
|
5733
|
-
case ">" /* GREATER_THAN */:
|
|
5734
|
-
return comparison > 0;
|
|
5735
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
5736
|
-
return comparison >= 0;
|
|
5737
|
-
default:
|
|
5738
|
-
return fail();
|
|
5739
|
-
}
|
|
5677
|
+
else if (filter instanceof CompositeFilter) {
|
|
5678
|
+
return stringifyCompositeFilter(filter);
|
|
5740
5679
|
}
|
|
5741
|
-
|
|
5742
|
-
return
|
|
5743
|
-
"<" /* LESS_THAN */,
|
|
5744
|
-
"<=" /* LESS_THAN_OR_EQUAL */,
|
|
5745
|
-
">" /* GREATER_THAN */,
|
|
5746
|
-
">=" /* GREATER_THAN_OR_EQUAL */,
|
|
5747
|
-
"!=" /* NOT_EQUAL */,
|
|
5748
|
-
"not-in" /* NOT_IN */
|
|
5749
|
-
].indexOf(this.op) >= 0);
|
|
5680
|
+
else {
|
|
5681
|
+
return 'Filter';
|
|
5750
5682
|
}
|
|
5751
5683
|
}
|
|
5752
|
-
function
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
filter.op.toString() +
|
|
5758
|
-
canonicalId(filter.value));
|
|
5759
|
-
}
|
|
5760
|
-
function filterEquals(f1, f2) {
|
|
5761
|
-
return (f1.op === f2.op &&
|
|
5762
|
-
f1.field.isEqual(f2.field) &&
|
|
5763
|
-
valueEquals(f1.value, f2.value));
|
|
5684
|
+
function stringifyCompositeFilter(filter) {
|
|
5685
|
+
return (filter.op.toString() +
|
|
5686
|
+
` {` +
|
|
5687
|
+
filter.getFilters().map(stringifyFilter).join(' ,') +
|
|
5688
|
+
'}');
|
|
5764
5689
|
}
|
|
5765
|
-
|
|
5766
|
-
function stringifyFilter(filter) {
|
|
5690
|
+
function stringifyFieldFilter(filter) {
|
|
5767
5691
|
return `${filter.field.canonicalString()} ${filter.op} ${canonicalId(filter.value)}`;
|
|
5768
5692
|
}
|
|
5769
5693
|
/** Filter that matches on key fields (i.e. '__name__'). */
|
|
@@ -5780,8 +5704,8 @@ class KeyFieldFilter extends FieldFilter {
|
|
|
5780
5704
|
/** Filter that matches on key fields within an array. */
|
|
5781
5705
|
class KeyFieldInFilter extends FieldFilter {
|
|
5782
5706
|
constructor(field, value) {
|
|
5783
|
-
super(field, "in" /* IN */, value);
|
|
5784
|
-
this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
|
|
5707
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5708
|
+
this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
|
|
5785
5709
|
}
|
|
5786
5710
|
matches(doc) {
|
|
5787
5711
|
return this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5790,8 +5714,8 @@ class KeyFieldInFilter extends FieldFilter {
|
|
|
5790
5714
|
/** Filter that matches on key fields not present within an array. */
|
|
5791
5715
|
class KeyFieldNotInFilter extends FieldFilter {
|
|
5792
5716
|
constructor(field, value) {
|
|
5793
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5794
|
-
this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
|
|
5717
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5718
|
+
this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
|
|
5795
5719
|
}
|
|
5796
5720
|
matches(doc) {
|
|
5797
5721
|
return !this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5806,7 +5730,7 @@ function extractDocumentKeysFromArrayValue(op, value) {
|
|
|
5806
5730
|
/** A Filter that implements the array-contains operator. */
|
|
5807
5731
|
class ArrayContainsFilter extends FieldFilter {
|
|
5808
5732
|
constructor(field, value) {
|
|
5809
|
-
super(field, "array-contains" /* ARRAY_CONTAINS */, value);
|
|
5733
|
+
super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
|
|
5810
5734
|
}
|
|
5811
5735
|
matches(doc) {
|
|
5812
5736
|
const other = doc.data.field(this.field);
|
|
@@ -5816,7 +5740,7 @@ class ArrayContainsFilter extends FieldFilter {
|
|
|
5816
5740
|
/** A Filter that implements the IN operator. */
|
|
5817
5741
|
class InFilter extends FieldFilter {
|
|
5818
5742
|
constructor(field, value) {
|
|
5819
|
-
super(field, "in" /* IN */, value);
|
|
5743
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5820
5744
|
}
|
|
5821
5745
|
matches(doc) {
|
|
5822
5746
|
const other = doc.data.field(this.field);
|
|
@@ -5826,7 +5750,7 @@ class InFilter extends FieldFilter {
|
|
|
5826
5750
|
/** A Filter that implements the not-in operator. */
|
|
5827
5751
|
class NotInFilter extends FieldFilter {
|
|
5828
5752
|
constructor(field, value) {
|
|
5829
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5753
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5830
5754
|
}
|
|
5831
5755
|
matches(doc) {
|
|
5832
5756
|
if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
|
|
@@ -5839,7 +5763,7 @@ class NotInFilter extends FieldFilter {
|
|
|
5839
5763
|
/** A Filter that implements the array-contains-any operator. */
|
|
5840
5764
|
class ArrayContainsAnyFilter extends FieldFilter {
|
|
5841
5765
|
constructor(field, value) {
|
|
5842
|
-
super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
|
|
5766
|
+
super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
|
|
5843
5767
|
}
|
|
5844
5768
|
matches(doc) {
|
|
5845
5769
|
const other = doc.data.field(this.field);
|
|
@@ -5848,32 +5772,29 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
5848
5772
|
}
|
|
5849
5773
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
5850
5774
|
}
|
|
5851
|
-
}
|
|
5852
|
-
|
|
5853
|
-
|
|
5775
|
+
}
|
|
5776
|
+
|
|
5777
|
+
/**
|
|
5778
|
+
* @license
|
|
5779
|
+
* Copyright 2022 Google LLC
|
|
5854
5780
|
*
|
|
5855
|
-
*
|
|
5856
|
-
*
|
|
5857
|
-
*
|
|
5781
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5782
|
+
* you may not use this file except in compliance with the License.
|
|
5783
|
+
* You may obtain a copy of the License at
|
|
5858
5784
|
*
|
|
5859
|
-
*
|
|
5860
|
-
* of values for the (potentially implicit) order by clauses of a query.
|
|
5785
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5861
5786
|
*
|
|
5862
|
-
*
|
|
5863
|
-
*
|
|
5864
|
-
*
|
|
5787
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5788
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5789
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5790
|
+
* See the License for the specific language governing permissions and
|
|
5791
|
+
* limitations under the License.
|
|
5865
5792
|
*/
|
|
5866
|
-
class Bound {
|
|
5867
|
-
constructor(position, inclusive) {
|
|
5868
|
-
this.position = position;
|
|
5869
|
-
this.inclusive = inclusive;
|
|
5870
|
-
}
|
|
5871
|
-
}
|
|
5872
5793
|
/**
|
|
5873
5794
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
5874
5795
|
*/
|
|
5875
5796
|
class OrderBy {
|
|
5876
|
-
constructor(field, dir = "asc" /* ASCENDING */) {
|
|
5797
|
+
constructor(field, dir = "asc" /* Direction.ASCENDING */) {
|
|
5877
5798
|
this.field = field;
|
|
5878
5799
|
this.dir = dir;
|
|
5879
5800
|
}
|
|
@@ -5887,63 +5808,375 @@ function stringifyOrderBy(orderBy) {
|
|
|
5887
5808
|
}
|
|
5888
5809
|
function orderByEquals(left, right) {
|
|
5889
5810
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
/**
|
|
5814
|
+
* @license
|
|
5815
|
+
* Copyright 2019 Google LLC
|
|
5816
|
+
*
|
|
5817
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5818
|
+
* you may not use this file except in compliance with the License.
|
|
5819
|
+
* You may obtain a copy of the License at
|
|
5820
|
+
*
|
|
5821
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5822
|
+
*
|
|
5823
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5824
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5825
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5826
|
+
* See the License for the specific language governing permissions and
|
|
5827
|
+
* limitations under the License.
|
|
5828
|
+
*/
|
|
5829
|
+
// Visible for testing
|
|
5830
|
+
class TargetImpl {
|
|
5831
|
+
constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5832
|
+
this.path = path;
|
|
5833
|
+
this.collectionGroup = collectionGroup;
|
|
5834
|
+
this.orderBy = orderBy;
|
|
5835
|
+
this.filters = filters;
|
|
5836
|
+
this.limit = limit;
|
|
5837
|
+
this.startAt = startAt;
|
|
5838
|
+
this.endAt = endAt;
|
|
5839
|
+
this.memoizedCanonicalId = null;
|
|
5840
|
+
}
|
|
5890
5841
|
}
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5842
|
+
/**
|
|
5843
|
+
* Initializes a Target with a path and optional additional query constraints.
|
|
5844
|
+
* Path must currently be empty if this is a collection group query.
|
|
5845
|
+
*
|
|
5846
|
+
* NOTE: you should always construct `Target` from `Query.toTarget` instead of
|
|
5847
|
+
* using this factory method, because `Query` provides an implicit `orderBy`
|
|
5848
|
+
* property.
|
|
5849
|
+
*/
|
|
5850
|
+
function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5851
|
+
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
5852
|
+
}
|
|
5853
|
+
function canonifyTarget(target) {
|
|
5854
|
+
const targetImpl = debugCast(target);
|
|
5855
|
+
if (targetImpl.memoizedCanonicalId === null) {
|
|
5856
|
+
let str = targetImpl.path.canonicalString();
|
|
5857
|
+
if (targetImpl.collectionGroup !== null) {
|
|
5858
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
5898
5859
|
}
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5860
|
+
str += '|f:';
|
|
5861
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
5862
|
+
str += '|ob:';
|
|
5863
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
5864
|
+
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
5865
|
+
str += '|l:';
|
|
5866
|
+
str += targetImpl.limit;
|
|
5902
5867
|
}
|
|
5903
|
-
if (
|
|
5904
|
-
|
|
5868
|
+
if (targetImpl.startAt) {
|
|
5869
|
+
str += '|lb:';
|
|
5870
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
5871
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5905
5872
|
}
|
|
5906
|
-
if (
|
|
5907
|
-
|
|
5873
|
+
if (targetImpl.endAt) {
|
|
5874
|
+
str += '|ub:';
|
|
5875
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
5876
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5908
5877
|
}
|
|
5878
|
+
targetImpl.memoizedCanonicalId = str;
|
|
5909
5879
|
}
|
|
5910
|
-
return
|
|
5880
|
+
return targetImpl.memoizedCanonicalId;
|
|
5881
|
+
}
|
|
5882
|
+
function stringifyTarget(target) {
|
|
5883
|
+
let str = target.path.canonicalString();
|
|
5884
|
+
if (target.collectionGroup !== null) {
|
|
5885
|
+
str += ' collectionGroup=' + target.collectionGroup;
|
|
5886
|
+
}
|
|
5887
|
+
if (target.filters.length > 0) {
|
|
5888
|
+
str += `, filters: [${target.filters
|
|
5889
|
+
.map(f => stringifyFilter(f))
|
|
5890
|
+
.join(', ')}]`;
|
|
5891
|
+
}
|
|
5892
|
+
if (!isNullOrUndefined(target.limit)) {
|
|
5893
|
+
str += ', limit: ' + target.limit;
|
|
5894
|
+
}
|
|
5895
|
+
if (target.orderBy.length > 0) {
|
|
5896
|
+
str += `, orderBy: [${target.orderBy
|
|
5897
|
+
.map(o => stringifyOrderBy(o))
|
|
5898
|
+
.join(', ')}]`;
|
|
5899
|
+
}
|
|
5900
|
+
if (target.startAt) {
|
|
5901
|
+
str += ', startAt: ';
|
|
5902
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
5903
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5904
|
+
}
|
|
5905
|
+
if (target.endAt) {
|
|
5906
|
+
str += ', endAt: ';
|
|
5907
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
5908
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5909
|
+
}
|
|
5910
|
+
return `Target(${str})`;
|
|
5911
|
+
}
|
|
5912
|
+
function targetEquals(left, right) {
|
|
5913
|
+
if (left.limit !== right.limit) {
|
|
5914
|
+
return false;
|
|
5915
|
+
}
|
|
5916
|
+
if (left.orderBy.length !== right.orderBy.length) {
|
|
5917
|
+
return false;
|
|
5918
|
+
}
|
|
5919
|
+
for (let i = 0; i < left.orderBy.length; i++) {
|
|
5920
|
+
if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
|
|
5921
|
+
return false;
|
|
5922
|
+
}
|
|
5923
|
+
}
|
|
5924
|
+
if (left.filters.length !== right.filters.length) {
|
|
5925
|
+
return false;
|
|
5926
|
+
}
|
|
5927
|
+
for (let i = 0; i < left.filters.length; i++) {
|
|
5928
|
+
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
5929
|
+
return false;
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
if (left.collectionGroup !== right.collectionGroup) {
|
|
5933
|
+
return false;
|
|
5934
|
+
}
|
|
5935
|
+
if (!left.path.isEqual(right.path)) {
|
|
5936
|
+
return false;
|
|
5937
|
+
}
|
|
5938
|
+
if (!boundEquals(left.startAt, right.startAt)) {
|
|
5939
|
+
return false;
|
|
5940
|
+
}
|
|
5941
|
+
return boundEquals(left.endAt, right.endAt);
|
|
5942
|
+
}
|
|
5943
|
+
function targetIsDocumentTarget(target) {
|
|
5944
|
+
return (DocumentKey.isDocumentKey(target.path) &&
|
|
5945
|
+
target.collectionGroup === null &&
|
|
5946
|
+
target.filters.length === 0);
|
|
5947
|
+
}
|
|
5948
|
+
/** Returns the field filters that target the given field path. */
|
|
5949
|
+
function targetGetFieldFiltersForPath(target, path) {
|
|
5950
|
+
return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
|
|
5911
5951
|
}
|
|
5912
5952
|
/**
|
|
5913
|
-
* Returns
|
|
5914
|
-
*
|
|
5953
|
+
* Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
|
|
5954
|
+
* filters. Returns `null` if there are no such filters.
|
|
5915
5955
|
*/
|
|
5916
|
-
function
|
|
5917
|
-
const
|
|
5918
|
-
|
|
5956
|
+
function targetGetArrayValues(target, fieldIndex) {
|
|
5957
|
+
const segment = fieldIndexGetArraySegment(fieldIndex);
|
|
5958
|
+
if (segment === undefined) {
|
|
5959
|
+
return null;
|
|
5960
|
+
}
|
|
5961
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5962
|
+
switch (fieldFilter.op) {
|
|
5963
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
5964
|
+
return fieldFilter.value.arrayValue.values || [];
|
|
5965
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
5966
|
+
return [fieldFilter.value];
|
|
5967
|
+
// Remaining filters are not array filters.
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
return null;
|
|
5919
5971
|
}
|
|
5920
5972
|
/**
|
|
5921
|
-
* Returns
|
|
5922
|
-
*
|
|
5973
|
+
* Returns the list of values that are used in != or NOT_IN filters. Returns
|
|
5974
|
+
* `null` if there are no such filters.
|
|
5923
5975
|
*/
|
|
5924
|
-
function
|
|
5925
|
-
const
|
|
5926
|
-
|
|
5976
|
+
function targetGetNotInValues(target, fieldIndex) {
|
|
5977
|
+
const values = new Map();
|
|
5978
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5979
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5980
|
+
switch (fieldFilter.op) {
|
|
5981
|
+
case "==" /* Operator.EQUAL */:
|
|
5982
|
+
case "in" /* Operator.IN */:
|
|
5983
|
+
// Encode equality prefix, which is encoded in the index value before
|
|
5984
|
+
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
|
|
5985
|
+
// `value != 'ab'`).
|
|
5986
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5987
|
+
break;
|
|
5988
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
5989
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
5990
|
+
// NotIn/NotEqual is always a suffix. There cannot be any remaining
|
|
5991
|
+
// segments and hence we can return early here.
|
|
5992
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5993
|
+
return Array.from(values.values());
|
|
5994
|
+
// Remaining filters cannot be used as notIn bounds.
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
}
|
|
5998
|
+
return null;
|
|
5927
5999
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
6000
|
+
/**
|
|
6001
|
+
* Returns a lower bound of field values that can be used as a starting point to
|
|
6002
|
+
* scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
|
|
6003
|
+
* exists.
|
|
6004
|
+
*/
|
|
6005
|
+
function targetGetLowerBound(target, fieldIndex) {
|
|
6006
|
+
const values = [];
|
|
6007
|
+
let inclusive = true;
|
|
6008
|
+
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
6009
|
+
// startAt.
|
|
6010
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
6011
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
6012
|
+
? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
|
|
6013
|
+
: targetGetDescendingBound(target, segment.fieldPath, target.startAt);
|
|
6014
|
+
values.push(segmentBound.value);
|
|
6015
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
5931
6016
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
6017
|
+
return new Bound(values, inclusive);
|
|
6018
|
+
}
|
|
6019
|
+
/**
|
|
6020
|
+
* Returns an upper bound of field values that can be used as an ending point
|
|
6021
|
+
* when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
|
|
6022
|
+
* upper bound exists.
|
|
6023
|
+
*/
|
|
6024
|
+
function targetGetUpperBound(target, fieldIndex) {
|
|
6025
|
+
const values = [];
|
|
6026
|
+
let inclusive = true;
|
|
6027
|
+
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
6028
|
+
// endAt.
|
|
6029
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
6030
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
6031
|
+
? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
|
|
6032
|
+
: targetGetAscendingBound(target, segment.fieldPath, target.endAt);
|
|
6033
|
+
values.push(segmentBound.value);
|
|
6034
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
6035
|
+
}
|
|
6036
|
+
return new Bound(values, inclusive);
|
|
6037
|
+
}
|
|
6038
|
+
/**
|
|
6039
|
+
* Returns the value to use as the lower bound for ascending index segment at
|
|
6040
|
+
* the provided `fieldPath` (or the upper bound for an descending segment).
|
|
6041
|
+
*/
|
|
6042
|
+
function targetGetAscendingBound(target, fieldPath, bound) {
|
|
6043
|
+
let value = MIN_VALUE;
|
|
6044
|
+
let inclusive = true;
|
|
6045
|
+
// Process all filters to find a value for the current field segment
|
|
6046
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6047
|
+
let filterValue = MIN_VALUE;
|
|
6048
|
+
let filterInclusive = true;
|
|
6049
|
+
switch (fieldFilter.op) {
|
|
6050
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6051
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6052
|
+
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
6053
|
+
break;
|
|
6054
|
+
case "==" /* Operator.EQUAL */:
|
|
6055
|
+
case "in" /* Operator.IN */:
|
|
6056
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6057
|
+
filterValue = fieldFilter.value;
|
|
6058
|
+
break;
|
|
6059
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6060
|
+
filterValue = fieldFilter.value;
|
|
6061
|
+
filterInclusive = false;
|
|
6062
|
+
break;
|
|
6063
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6064
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6065
|
+
filterValue = MIN_VALUE;
|
|
6066
|
+
break;
|
|
6067
|
+
// Remaining filters cannot be used as lower bounds.
|
|
6068
|
+
}
|
|
6069
|
+
if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
|
|
6070
|
+
value = filterValue;
|
|
6071
|
+
inclusive = filterInclusive;
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
// If there is an additional bound, compare the values against the existing
|
|
6075
|
+
// range to see if we can narrow the scope.
|
|
6076
|
+
if (bound !== null) {
|
|
6077
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6078
|
+
const orderBy = target.orderBy[i];
|
|
6079
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6080
|
+
const cursorValue = bound.position[i];
|
|
6081
|
+
if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
|
|
6082
|
+
value = cursorValue;
|
|
6083
|
+
inclusive = bound.inclusive;
|
|
6084
|
+
}
|
|
6085
|
+
break;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
return { value, inclusive };
|
|
6090
|
+
}
|
|
6091
|
+
/**
|
|
6092
|
+
* Returns the value to use as the upper bound for ascending index segment at
|
|
6093
|
+
* the provided `fieldPath` (or the lower bound for a descending segment).
|
|
6094
|
+
*/
|
|
6095
|
+
function targetGetDescendingBound(target, fieldPath, bound) {
|
|
6096
|
+
let value = MAX_VALUE;
|
|
6097
|
+
let inclusive = true;
|
|
6098
|
+
// Process all filters to find a value for the current field segment
|
|
6099
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6100
|
+
let filterValue = MAX_VALUE;
|
|
6101
|
+
let filterInclusive = true;
|
|
6102
|
+
switch (fieldFilter.op) {
|
|
6103
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6104
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6105
|
+
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
6106
|
+
filterInclusive = false;
|
|
6107
|
+
break;
|
|
6108
|
+
case "==" /* Operator.EQUAL */:
|
|
6109
|
+
case "in" /* Operator.IN */:
|
|
6110
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6111
|
+
filterValue = fieldFilter.value;
|
|
6112
|
+
break;
|
|
6113
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6114
|
+
filterValue = fieldFilter.value;
|
|
6115
|
+
filterInclusive = false;
|
|
6116
|
+
break;
|
|
6117
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6118
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6119
|
+
filterValue = MAX_VALUE;
|
|
6120
|
+
break;
|
|
6121
|
+
// Remaining filters cannot be used as upper bounds.
|
|
6122
|
+
}
|
|
6123
|
+
if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
|
|
6124
|
+
value = filterValue;
|
|
6125
|
+
inclusive = filterInclusive;
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
// If there is an additional bound, compare the values against the existing
|
|
6129
|
+
// range to see if we can narrow the scope.
|
|
6130
|
+
if (bound !== null) {
|
|
6131
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6132
|
+
const orderBy = target.orderBy[i];
|
|
6133
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6134
|
+
const cursorValue = bound.position[i];
|
|
6135
|
+
if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
|
|
6136
|
+
value = cursorValue;
|
|
6137
|
+
inclusive = bound.inclusive;
|
|
6138
|
+
}
|
|
6139
|
+
break;
|
|
6140
|
+
}
|
|
6141
|
+
}
|
|
5934
6142
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
6143
|
+
return { value, inclusive };
|
|
6144
|
+
}
|
|
6145
|
+
/** Returns the number of segments of a perfect index for this target. */
|
|
6146
|
+
function targetGetSegmentCount(target) {
|
|
6147
|
+
let fields = new SortedSet(FieldPath$1.comparator);
|
|
6148
|
+
let hasArraySegment = false;
|
|
6149
|
+
for (const filter of target.filters) {
|
|
6150
|
+
for (const subFilter of filter.getFlattenedFilters()) {
|
|
6151
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6152
|
+
// count it.
|
|
6153
|
+
if (subFilter.field.isKeyField()) {
|
|
6154
|
+
continue;
|
|
6155
|
+
}
|
|
6156
|
+
// ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
|
|
6157
|
+
// For instance, it is possible to have an index for "a ARRAY a ASC". Even
|
|
6158
|
+
// though these are on the same field, they should be counted as two
|
|
6159
|
+
// separate segments in an index.
|
|
6160
|
+
if (subFilter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
6161
|
+
subFilter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
6162
|
+
hasArraySegment = true;
|
|
6163
|
+
}
|
|
6164
|
+
else {
|
|
6165
|
+
fields = fields.add(subFilter.field);
|
|
6166
|
+
}
|
|
6167
|
+
}
|
|
5938
6168
|
}
|
|
5939
|
-
for (
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
if (!
|
|
5943
|
-
|
|
6169
|
+
for (const orderBy of target.orderBy) {
|
|
6170
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6171
|
+
// count it.
|
|
6172
|
+
if (!orderBy.field.isKeyField()) {
|
|
6173
|
+
fields = fields.add(orderBy.field);
|
|
5944
6174
|
}
|
|
5945
6175
|
}
|
|
5946
|
-
return
|
|
6176
|
+
return fields.size + (hasArraySegment ? 1 : 0);
|
|
6177
|
+
}
|
|
6178
|
+
function targetHasLimit(target) {
|
|
6179
|
+
return target.limit !== null;
|
|
5947
6180
|
}
|
|
5948
6181
|
|
|
5949
6182
|
/**
|
|
@@ -5974,7 +6207,7 @@ class QueryImpl {
|
|
|
5974
6207
|
* Initializes a Query with a path and optional additional query constraints.
|
|
5975
6208
|
* Path must currently be empty if this is a collection group query.
|
|
5976
6209
|
*/
|
|
5977
|
-
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
|
|
6210
|
+
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
|
|
5978
6211
|
this.path = path;
|
|
5979
6212
|
this.collectionGroup = collectionGroup;
|
|
5980
6213
|
this.explicitOrderBy = explicitOrderBy;
|
|
@@ -6028,20 +6261,9 @@ function getFirstOrderByField(query) {
|
|
|
6028
6261
|
}
|
|
6029
6262
|
function getInequalityFilterField(query) {
|
|
6030
6263
|
for (const filter of query.filters) {
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
}
|
|
6035
|
-
return null;
|
|
6036
|
-
}
|
|
6037
|
-
/**
|
|
6038
|
-
* Checks if any of the provided Operators are included in the query and
|
|
6039
|
-
* returns the first one that is, or null if none are.
|
|
6040
|
-
*/
|
|
6041
|
-
function findFilterOperator(query, operators) {
|
|
6042
|
-
for (const filter of query.filters) {
|
|
6043
|
-
if (operators.indexOf(filter.op) >= 0) {
|
|
6044
|
-
return filter.op;
|
|
6264
|
+
const result = filter.getFirstInequalityField();
|
|
6265
|
+
if (result !== null) {
|
|
6266
|
+
return result;
|
|
6045
6267
|
}
|
|
6046
6268
|
}
|
|
6047
6269
|
return null;
|
|
@@ -6087,7 +6309,7 @@ function queryOrderBy(query) {
|
|
|
6087
6309
|
if (!inequalityField.isKeyField()) {
|
|
6088
6310
|
queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
|
|
6089
6311
|
}
|
|
6090
|
-
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
|
|
6312
|
+
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
|
|
6091
6313
|
}
|
|
6092
6314
|
else {
|
|
6093
6315
|
let foundKeyOrdering = false;
|
|
@@ -6103,7 +6325,7 @@ function queryOrderBy(query) {
|
|
|
6103
6325
|
const lastDirection = queryImpl.explicitOrderBy.length > 0
|
|
6104
6326
|
? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
|
|
6105
6327
|
.dir
|
|
6106
|
-
: "asc" /* ASCENDING */;
|
|
6328
|
+
: "asc" /* Direction.ASCENDING */;
|
|
6107
6329
|
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
|
|
6108
6330
|
}
|
|
6109
6331
|
}
|
|
@@ -6116,16 +6338,16 @@ function queryOrderBy(query) {
|
|
|
6116
6338
|
function queryToTarget(query) {
|
|
6117
6339
|
const queryImpl = debugCast(query);
|
|
6118
6340
|
if (!queryImpl.memoizedTarget) {
|
|
6119
|
-
if (queryImpl.limitType === "F" /* First */) {
|
|
6341
|
+
if (queryImpl.limitType === "F" /* LimitType.First */) {
|
|
6120
6342
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
|
|
6121
6343
|
}
|
|
6122
6344
|
else {
|
|
6123
6345
|
// Flip the orderBy directions since we want the last results
|
|
6124
6346
|
const orderBys = [];
|
|
6125
6347
|
for (const orderBy of queryOrderBy(queryImpl)) {
|
|
6126
|
-
const dir = orderBy.dir === "desc" /* DESCENDING */
|
|
6127
|
-
? "asc" /* ASCENDING */
|
|
6128
|
-
: "desc" /* DESCENDING */;
|
|
6348
|
+
const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
|
|
6349
|
+
? "asc" /* Direction.ASCENDING */
|
|
6350
|
+
: "desc" /* Direction.DESCENDING */;
|
|
6129
6351
|
orderBys.push(new OrderBy(orderBy.field, dir));
|
|
6130
6352
|
}
|
|
6131
6353
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
@@ -6142,6 +6364,8 @@ function queryToTarget(query) {
|
|
|
6142
6364
|
return queryImpl.memoizedTarget;
|
|
6143
6365
|
}
|
|
6144
6366
|
function queryWithAddedFilter(query, filter) {
|
|
6367
|
+
filter.getFirstInequalityField();
|
|
6368
|
+
getInequalityFilterField(query);
|
|
6145
6369
|
const newFilters = query.filters.concat([filter]);
|
|
6146
6370
|
return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
|
|
6147
6371
|
}
|
|
@@ -6202,7 +6426,13 @@ function queryMatchesPathAndCollectionGroup(query, doc) {
|
|
|
6202
6426
|
* in the results.
|
|
6203
6427
|
*/
|
|
6204
6428
|
function queryMatchesOrderBy(query, doc) {
|
|
6205
|
-
|
|
6429
|
+
// We must use `queryOrderBy()` to get the list of all orderBys (both implicit and explicit).
|
|
6430
|
+
// Note that for OR queries, orderBy applies to all disjunction terms and implicit orderBys must
|
|
6431
|
+
// be taken into account. For example, the query "a > 1 || b==1" has an implicit "orderBy a" due
|
|
6432
|
+
// to the inequality, and is evaluated as "a > 1 orderBy a || b==1 orderBy a".
|
|
6433
|
+
// A document with content of {b:1} matches the filters, but does not match the orderBy because
|
|
6434
|
+
// it's missing the field 'a'.
|
|
6435
|
+
for (const orderBy of queryOrderBy(query)) {
|
|
6206
6436
|
// order by key always matches
|
|
6207
6437
|
if (!orderBy.field.isKeyField() && doc.data.field(orderBy.field) === null) {
|
|
6208
6438
|
return false;
|
|
@@ -6264,9 +6494,9 @@ function compareDocs(orderBy, d1, d2) {
|
|
|
6264
6494
|
? DocumentKey.comparator(d1.key, d2.key)
|
|
6265
6495
|
: compareDocumentsByField(orderBy.field, d1, d2);
|
|
6266
6496
|
switch (orderBy.dir) {
|
|
6267
|
-
case "asc" /* ASCENDING */:
|
|
6497
|
+
case "asc" /* Direction.ASCENDING */:
|
|
6268
6498
|
return comparison;
|
|
6269
|
-
case "desc" /* DESCENDING */:
|
|
6499
|
+
case "desc" /* Direction.DESCENDING */:
|
|
6270
6500
|
return -1 * comparison;
|
|
6271
6501
|
default:
|
|
6272
6502
|
return fail();
|
|
@@ -6952,10 +7182,10 @@ function mutationEquals(left, right) {
|
|
|
6952
7182
|
if (!fieldTransformsAreEqual(left.fieldTransforms, right.fieldTransforms)) {
|
|
6953
7183
|
return false;
|
|
6954
7184
|
}
|
|
6955
|
-
if (left.type === 0 /* Set */) {
|
|
7185
|
+
if (left.type === 0 /* MutationType.Set */) {
|
|
6956
7186
|
return left.value.isEqual(right.value);
|
|
6957
7187
|
}
|
|
6958
|
-
if (left.type === 1 /* Patch */) {
|
|
7188
|
+
if (left.type === 1 /* MutationType.Patch */) {
|
|
6959
7189
|
return (left.data.isEqual(right.data) &&
|
|
6960
7190
|
left.fieldMask.isEqual(right.fieldMask));
|
|
6961
7191
|
}
|
|
@@ -6972,7 +7202,7 @@ class SetMutation extends Mutation {
|
|
|
6972
7202
|
this.value = value;
|
|
6973
7203
|
this.precondition = precondition;
|
|
6974
7204
|
this.fieldTransforms = fieldTransforms;
|
|
6975
|
-
this.type = 0 /* Set */;
|
|
7205
|
+
this.type = 0 /* MutationType.Set */;
|
|
6976
7206
|
}
|
|
6977
7207
|
getFieldMask() {
|
|
6978
7208
|
return null;
|
|
@@ -7024,7 +7254,7 @@ class PatchMutation extends Mutation {
|
|
|
7024
7254
|
this.fieldMask = fieldMask;
|
|
7025
7255
|
this.precondition = precondition;
|
|
7026
7256
|
this.fieldTransforms = fieldTransforms;
|
|
7027
|
-
this.type = 1 /* Patch */;
|
|
7257
|
+
this.type = 1 /* MutationType.Patch */;
|
|
7028
7258
|
}
|
|
7029
7259
|
getFieldMask() {
|
|
7030
7260
|
return this.fieldMask;
|
|
@@ -7126,7 +7356,7 @@ class DeleteMutation extends Mutation {
|
|
|
7126
7356
|
super();
|
|
7127
7357
|
this.key = key;
|
|
7128
7358
|
this.precondition = precondition;
|
|
7129
|
-
this.type = 2 /* Delete */;
|
|
7359
|
+
this.type = 2 /* MutationType.Delete */;
|
|
7130
7360
|
this.fieldTransforms = [];
|
|
7131
7361
|
}
|
|
7132
7362
|
getFieldMask() {
|
|
@@ -7160,7 +7390,7 @@ class VerifyMutation extends Mutation {
|
|
|
7160
7390
|
super();
|
|
7161
7391
|
this.key = key;
|
|
7162
7392
|
this.precondition = precondition;
|
|
7163
|
-
this.type = 3 /* Verify */;
|
|
7393
|
+
this.type = 3 /* MutationType.Verify */;
|
|
7164
7394
|
this.fieldTransforms = [];
|
|
7165
7395
|
}
|
|
7166
7396
|
getFieldMask() {
|
|
@@ -7789,13 +8019,13 @@ class TargetState {
|
|
|
7789
8019
|
let removedDocuments = documentKeySet();
|
|
7790
8020
|
this.documentChanges.forEach((key, changeType) => {
|
|
7791
8021
|
switch (changeType) {
|
|
7792
|
-
case 0 /* Added */:
|
|
8022
|
+
case 0 /* ChangeType.Added */:
|
|
7793
8023
|
addedDocuments = addedDocuments.add(key);
|
|
7794
8024
|
break;
|
|
7795
|
-
case 2 /* Modified */:
|
|
8025
|
+
case 2 /* ChangeType.Modified */:
|
|
7796
8026
|
modifiedDocuments = modifiedDocuments.add(key);
|
|
7797
8027
|
break;
|
|
7798
|
-
case 1 /* Removed */:
|
|
8028
|
+
case 1 /* ChangeType.Removed */:
|
|
7799
8029
|
removedDocuments = removedDocuments.add(key);
|
|
7800
8030
|
break;
|
|
7801
8031
|
default:
|
|
@@ -7871,12 +8101,12 @@ class WatchChangeAggregator {
|
|
|
7871
8101
|
this.forEachTarget(targetChange, targetId => {
|
|
7872
8102
|
const targetState = this.ensureTargetState(targetId);
|
|
7873
8103
|
switch (targetChange.state) {
|
|
7874
|
-
case 0 /* NoChange */:
|
|
8104
|
+
case 0 /* WatchTargetChangeState.NoChange */:
|
|
7875
8105
|
if (this.isActiveTarget(targetId)) {
|
|
7876
8106
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7877
8107
|
}
|
|
7878
8108
|
break;
|
|
7879
|
-
case 1 /* Added */:
|
|
8109
|
+
case 1 /* WatchTargetChangeState.Added */:
|
|
7880
8110
|
// We need to decrement the number of pending acks needed from watch
|
|
7881
8111
|
// for this targetId.
|
|
7882
8112
|
targetState.recordTargetResponse();
|
|
@@ -7888,7 +8118,7 @@ class WatchChangeAggregator {
|
|
|
7888
8118
|
}
|
|
7889
8119
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7890
8120
|
break;
|
|
7891
|
-
case 2 /* Removed */:
|
|
8121
|
+
case 2 /* WatchTargetChangeState.Removed */:
|
|
7892
8122
|
// We need to keep track of removed targets to we can post-filter and
|
|
7893
8123
|
// remove any target changes.
|
|
7894
8124
|
// We need to decrement the number of pending acks needed from watch
|
|
@@ -7898,13 +8128,13 @@ class WatchChangeAggregator {
|
|
|
7898
8128
|
this.removeTarget(targetId);
|
|
7899
8129
|
}
|
|
7900
8130
|
break;
|
|
7901
|
-
case 3 /* Current */:
|
|
8131
|
+
case 3 /* WatchTargetChangeState.Current */:
|
|
7902
8132
|
if (this.isActiveTarget(targetId)) {
|
|
7903
8133
|
targetState.markCurrent();
|
|
7904
8134
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7905
8135
|
}
|
|
7906
8136
|
break;
|
|
7907
|
-
case 4 /* Reset */:
|
|
8137
|
+
case 4 /* WatchTargetChangeState.Reset */:
|
|
7908
8138
|
if (this.isActiveTarget(targetId)) {
|
|
7909
8139
|
// Reset the target and synthesizes removes for all existing
|
|
7910
8140
|
// documents. The backend will re-add any documents that still
|
|
@@ -8014,7 +8244,7 @@ class WatchChangeAggregator {
|
|
|
8014
8244
|
targets.forEachWhile(targetId => {
|
|
8015
8245
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
8016
8246
|
if (targetData &&
|
|
8017
|
-
targetData.purpose !== 2 /* LimboResolution */) {
|
|
8247
|
+
targetData.purpose !== 2 /* TargetPurpose.LimboResolution */) {
|
|
8018
8248
|
isOnlyLimboTarget = false;
|
|
8019
8249
|
return false;
|
|
8020
8250
|
}
|
|
@@ -8041,8 +8271,8 @@ class WatchChangeAggregator {
|
|
|
8041
8271
|
return;
|
|
8042
8272
|
}
|
|
8043
8273
|
const changeType = this.targetContainsDocument(targetId, document.key)
|
|
8044
|
-
? 2 /* Modified */
|
|
8045
|
-
: 0 /* Added */;
|
|
8274
|
+
? 2 /* ChangeType.Modified */
|
|
8275
|
+
: 0 /* ChangeType.Added */;
|
|
8046
8276
|
const targetState = this.ensureTargetState(targetId);
|
|
8047
8277
|
targetState.addDocumentChange(document.key, changeType);
|
|
8048
8278
|
this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(document.key, document);
|
|
@@ -8063,7 +8293,7 @@ class WatchChangeAggregator {
|
|
|
8063
8293
|
}
|
|
8064
8294
|
const targetState = this.ensureTargetState(targetId);
|
|
8065
8295
|
if (this.targetContainsDocument(targetId, key)) {
|
|
8066
|
-
targetState.addDocumentChange(key, 1 /* Removed */);
|
|
8296
|
+
targetState.addDocumentChange(key, 1 /* ChangeType.Removed */);
|
|
8067
8297
|
}
|
|
8068
8298
|
else {
|
|
8069
8299
|
// The document may have entered and left the target before we raised a
|
|
@@ -8188,22 +8418,28 @@ function snapshotChangesMap() {
|
|
|
8188
8418
|
*/
|
|
8189
8419
|
const DIRECTIONS = (() => {
|
|
8190
8420
|
const dirs = {};
|
|
8191
|
-
dirs["asc" /* ASCENDING */] = 'ASCENDING';
|
|
8192
|
-
dirs["desc" /* DESCENDING */] = 'DESCENDING';
|
|
8421
|
+
dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
|
|
8422
|
+
dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
|
|
8193
8423
|
return dirs;
|
|
8194
8424
|
})();
|
|
8195
8425
|
const OPERATORS = (() => {
|
|
8196
8426
|
const ops = {};
|
|
8197
|
-
ops["<" /* LESS_THAN */] = 'LESS_THAN';
|
|
8198
|
-
ops["<=" /* LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
8199
|
-
ops[">" /* GREATER_THAN */] = 'GREATER_THAN';
|
|
8200
|
-
ops[">=" /* GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
8201
|
-
ops["==" /* EQUAL */] = 'EQUAL';
|
|
8202
|
-
ops["!=" /* NOT_EQUAL */] = 'NOT_EQUAL';
|
|
8203
|
-
ops["array-contains" /* ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
8204
|
-
ops["in" /* IN */] = 'IN';
|
|
8205
|
-
ops["not-in" /* NOT_IN */] = 'NOT_IN';
|
|
8206
|
-
ops["array-contains-any" /* ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
8427
|
+
ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
|
|
8428
|
+
ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
8429
|
+
ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
|
|
8430
|
+
ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
8431
|
+
ops["==" /* Operator.EQUAL */] = 'EQUAL';
|
|
8432
|
+
ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
|
|
8433
|
+
ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
8434
|
+
ops["in" /* Operator.IN */] = 'IN';
|
|
8435
|
+
ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
|
|
8436
|
+
ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
8437
|
+
return ops;
|
|
8438
|
+
})();
|
|
8439
|
+
const COMPOSITE_OPERATORS = (() => {
|
|
8440
|
+
const ops = {};
|
|
8441
|
+
ops["and" /* CompositeOperator.AND */] = 'AND';
|
|
8442
|
+
ops["or" /* CompositeOperator.OR */] = 'OR';
|
|
8207
8443
|
return ops;
|
|
8208
8444
|
})();
|
|
8209
8445
|
function assertPresent(value, description) {
|
|
@@ -8397,14 +8633,21 @@ function toDocument(serializer, document) {
|
|
|
8397
8633
|
return {
|
|
8398
8634
|
name: toName(serializer, document.key),
|
|
8399
8635
|
fields: document.data.value.mapValue.fields,
|
|
8400
|
-
updateTime: toTimestamp(serializer, document.version.toTimestamp())
|
|
8636
|
+
updateTime: toTimestamp(serializer, document.version.toTimestamp()),
|
|
8637
|
+
createTime: toTimestamp(serializer, document.createTime.toTimestamp())
|
|
8401
8638
|
};
|
|
8402
8639
|
}
|
|
8403
8640
|
function fromDocument(serializer, document, hasCommittedMutations) {
|
|
8404
8641
|
const key = fromName(serializer, document.name);
|
|
8405
8642
|
const version = fromVersion(document.updateTime);
|
|
8643
|
+
// If we read a document from persistence that is missing createTime, it's due
|
|
8644
|
+
// to older SDK versions not storing this information. In such cases, we'll
|
|
8645
|
+
// set the createTime to zero. This can be removed in the long term.
|
|
8646
|
+
const createTime = document.createTime
|
|
8647
|
+
? fromVersion(document.createTime)
|
|
8648
|
+
: SnapshotVersion.min();
|
|
8406
8649
|
const data = new ObjectValue({ mapValue: { fields: document.fields } });
|
|
8407
|
-
const result = MutableDocument.newFoundDocument(key, version, data);
|
|
8650
|
+
const result = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8408
8651
|
if (hasCommittedMutations) {
|
|
8409
8652
|
result.setHasCommittedMutations();
|
|
8410
8653
|
}
|
|
@@ -8416,8 +8659,11 @@ function fromFound(serializer, doc) {
|
|
|
8416
8659
|
assertPresent(doc.found.updateTime);
|
|
8417
8660
|
const key = fromName(serializer, doc.found.name);
|
|
8418
8661
|
const version = fromVersion(doc.found.updateTime);
|
|
8662
|
+
const createTime = doc.found.createTime
|
|
8663
|
+
? fromVersion(doc.found.createTime)
|
|
8664
|
+
: SnapshotVersion.min();
|
|
8419
8665
|
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
|
|
8420
|
-
return MutableDocument.newFoundDocument(key, version, data);
|
|
8666
|
+
return MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8421
8667
|
}
|
|
8422
8668
|
function fromMissing(serializer, result) {
|
|
8423
8669
|
hardAssert(!!result.missing);
|
|
@@ -8456,10 +8702,13 @@ function fromWatchChange(serializer, change) {
|
|
|
8456
8702
|
assertPresent(entityChange.document.updateTime);
|
|
8457
8703
|
const key = fromName(serializer, entityChange.document.name);
|
|
8458
8704
|
const version = fromVersion(entityChange.document.updateTime);
|
|
8705
|
+
const createTime = entityChange.document.createTime
|
|
8706
|
+
? fromVersion(entityChange.document.createTime)
|
|
8707
|
+
: SnapshotVersion.min();
|
|
8459
8708
|
const data = new ObjectValue({
|
|
8460
8709
|
mapValue: { fields: entityChange.document.fields }
|
|
8461
8710
|
});
|
|
8462
|
-
const doc = MutableDocument.newFoundDocument(key, version, data);
|
|
8711
|
+
const doc = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8463
8712
|
const updatedTargetIds = entityChange.targetIds || [];
|
|
8464
8713
|
const removedTargetIds = entityChange.removedTargetIds || [];
|
|
8465
8714
|
watchChange = new DocumentWatchChange(updatedTargetIds, removedTargetIds, doc.key, doc);
|
|
@@ -8501,19 +8750,19 @@ function fromWatchChange(serializer, change) {
|
|
|
8501
8750
|
}
|
|
8502
8751
|
function fromWatchTargetChangeState(state) {
|
|
8503
8752
|
if (state === 'NO_CHANGE') {
|
|
8504
|
-
return 0 /* NoChange */;
|
|
8753
|
+
return 0 /* WatchTargetChangeState.NoChange */;
|
|
8505
8754
|
}
|
|
8506
8755
|
else if (state === 'ADD') {
|
|
8507
|
-
return 1 /* Added */;
|
|
8756
|
+
return 1 /* WatchTargetChangeState.Added */;
|
|
8508
8757
|
}
|
|
8509
8758
|
else if (state === 'REMOVE') {
|
|
8510
|
-
return 2 /* Removed */;
|
|
8759
|
+
return 2 /* WatchTargetChangeState.Removed */;
|
|
8511
8760
|
}
|
|
8512
8761
|
else if (state === 'CURRENT') {
|
|
8513
|
-
return 3 /* Current */;
|
|
8762
|
+
return 3 /* WatchTargetChangeState.Current */;
|
|
8514
8763
|
}
|
|
8515
8764
|
else if (state === 'RESET') {
|
|
8516
|
-
return 4 /* Reset */;
|
|
8765
|
+
return 4 /* WatchTargetChangeState.Reset */;
|
|
8517
8766
|
}
|
|
8518
8767
|
else {
|
|
8519
8768
|
return fail();
|
|
@@ -8731,7 +8980,7 @@ function toQueryTarget(serializer, target) {
|
|
|
8731
8980
|
result.parent = toQueryPath(serializer, path.popLast());
|
|
8732
8981
|
result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
|
|
8733
8982
|
}
|
|
8734
|
-
const where =
|
|
8983
|
+
const where = toFilters(target.filters);
|
|
8735
8984
|
if (where) {
|
|
8736
8985
|
result.structuredQuery.where = where;
|
|
8737
8986
|
}
|
|
@@ -8783,7 +9032,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8783
9032
|
}
|
|
8784
9033
|
let filterBy = [];
|
|
8785
9034
|
if (query.where) {
|
|
8786
|
-
filterBy =
|
|
9035
|
+
filterBy = fromFilters(query.where);
|
|
8787
9036
|
}
|
|
8788
9037
|
let orderBy = [];
|
|
8789
9038
|
if (query.orderBy) {
|
|
@@ -8801,7 +9050,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8801
9050
|
if (query.endAt) {
|
|
8802
9051
|
endAt = fromEndAtCursor(query.endAt);
|
|
8803
9052
|
}
|
|
8804
|
-
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
9053
|
+
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* LimitType.First */, startAt, endAt);
|
|
8805
9054
|
}
|
|
8806
9055
|
function fromQueryTarget(target) {
|
|
8807
9056
|
return queryToTarget(convertQueryTargetToQuery(target));
|
|
@@ -8819,11 +9068,11 @@ function toListenRequestLabels(serializer, targetData) {
|
|
|
8819
9068
|
}
|
|
8820
9069
|
function toLabel(serializer, purpose) {
|
|
8821
9070
|
switch (purpose) {
|
|
8822
|
-
case 0 /* Listen */:
|
|
9071
|
+
case 0 /* TargetPurpose.Listen */:
|
|
8823
9072
|
return null;
|
|
8824
|
-
case 1 /* ExistenceFilterMismatch */:
|
|
9073
|
+
case 1 /* TargetPurpose.ExistenceFilterMismatch */:
|
|
8825
9074
|
return 'existence-filter-mismatch';
|
|
8826
|
-
case 2 /* LimboResolution */:
|
|
9075
|
+
case 2 /* TargetPurpose.LimboResolution */:
|
|
8827
9076
|
return 'limbo-document';
|
|
8828
9077
|
default:
|
|
8829
9078
|
return fail();
|
|
@@ -8850,32 +9099,29 @@ function toTarget(serializer, targetData) {
|
|
|
8850
9099
|
}
|
|
8851
9100
|
return result;
|
|
8852
9101
|
}
|
|
8853
|
-
function
|
|
9102
|
+
function toFilters(filters) {
|
|
8854
9103
|
if (filters.length === 0) {
|
|
8855
9104
|
return;
|
|
8856
9105
|
}
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
9106
|
+
return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
|
|
9107
|
+
}
|
|
9108
|
+
function fromFilters(filter) {
|
|
9109
|
+
const result = fromFilter(filter);
|
|
9110
|
+
if (result instanceof CompositeFilter &&
|
|
9111
|
+
compositeFilterIsFlatConjunction(result)) {
|
|
9112
|
+
return result.getFilters();
|
|
8862
9113
|
}
|
|
8863
|
-
return
|
|
9114
|
+
return [result];
|
|
8864
9115
|
}
|
|
8865
9116
|
function fromFilter(filter) {
|
|
8866
|
-
if (
|
|
8867
|
-
return
|
|
8868
|
-
}
|
|
8869
|
-
else if (filter.unaryFilter !== undefined) {
|
|
8870
|
-
return [fromUnaryFilter(filter)];
|
|
9117
|
+
if (filter.unaryFilter !== undefined) {
|
|
9118
|
+
return fromUnaryFilter(filter);
|
|
8871
9119
|
}
|
|
8872
9120
|
else if (filter.fieldFilter !== undefined) {
|
|
8873
|
-
return
|
|
9121
|
+
return fromFieldFilter(filter);
|
|
8874
9122
|
}
|
|
8875
9123
|
else if (filter.compositeFilter !== undefined) {
|
|
8876
|
-
return filter
|
|
8877
|
-
.filters.map(f => fromFilter(f))
|
|
8878
|
-
.reduce((accum, current) => accum.concat(current));
|
|
9124
|
+
return fromCompositeFilter(filter);
|
|
8879
9125
|
}
|
|
8880
9126
|
else {
|
|
8881
9127
|
return fail();
|
|
@@ -8920,9 +9166,9 @@ function toDirection(dir) {
|
|
|
8920
9166
|
function fromDirection(dir) {
|
|
8921
9167
|
switch (dir) {
|
|
8922
9168
|
case 'ASCENDING':
|
|
8923
|
-
return "asc" /* ASCENDING */;
|
|
9169
|
+
return "asc" /* Direction.ASCENDING */;
|
|
8924
9170
|
case 'DESCENDING':
|
|
8925
|
-
return "desc" /* DESCENDING */;
|
|
9171
|
+
return "desc" /* Direction.DESCENDING */;
|
|
8926
9172
|
default:
|
|
8927
9173
|
return undefined;
|
|
8928
9174
|
}
|
|
@@ -8931,34 +9177,47 @@ function fromDirection(dir) {
|
|
|
8931
9177
|
function toOperatorName(op) {
|
|
8932
9178
|
return OPERATORS[op];
|
|
8933
9179
|
}
|
|
9180
|
+
function toCompositeOperatorName(op) {
|
|
9181
|
+
return COMPOSITE_OPERATORS[op];
|
|
9182
|
+
}
|
|
8934
9183
|
function fromOperatorName(op) {
|
|
8935
9184
|
switch (op) {
|
|
8936
9185
|
case 'EQUAL':
|
|
8937
|
-
return "==" /* EQUAL */;
|
|
9186
|
+
return "==" /* Operator.EQUAL */;
|
|
8938
9187
|
case 'NOT_EQUAL':
|
|
8939
|
-
return "!=" /* NOT_EQUAL */;
|
|
9188
|
+
return "!=" /* Operator.NOT_EQUAL */;
|
|
8940
9189
|
case 'GREATER_THAN':
|
|
8941
|
-
return ">" /* GREATER_THAN */;
|
|
9190
|
+
return ">" /* Operator.GREATER_THAN */;
|
|
8942
9191
|
case 'GREATER_THAN_OR_EQUAL':
|
|
8943
|
-
return ">=" /* GREATER_THAN_OR_EQUAL */;
|
|
9192
|
+
return ">=" /* Operator.GREATER_THAN_OR_EQUAL */;
|
|
8944
9193
|
case 'LESS_THAN':
|
|
8945
|
-
return "<" /* LESS_THAN */;
|
|
9194
|
+
return "<" /* Operator.LESS_THAN */;
|
|
8946
9195
|
case 'LESS_THAN_OR_EQUAL':
|
|
8947
|
-
return "<=" /* LESS_THAN_OR_EQUAL */;
|
|
9196
|
+
return "<=" /* Operator.LESS_THAN_OR_EQUAL */;
|
|
8948
9197
|
case 'ARRAY_CONTAINS':
|
|
8949
|
-
return "array-contains" /* ARRAY_CONTAINS */;
|
|
9198
|
+
return "array-contains" /* Operator.ARRAY_CONTAINS */;
|
|
8950
9199
|
case 'IN':
|
|
8951
|
-
return "in" /* IN */;
|
|
9200
|
+
return "in" /* Operator.IN */;
|
|
8952
9201
|
case 'NOT_IN':
|
|
8953
|
-
return "not-in" /* NOT_IN */;
|
|
9202
|
+
return "not-in" /* Operator.NOT_IN */;
|
|
8954
9203
|
case 'ARRAY_CONTAINS_ANY':
|
|
8955
|
-
return "array-contains-any" /* ARRAY_CONTAINS_ANY */;
|
|
9204
|
+
return "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
8956
9205
|
case 'OPERATOR_UNSPECIFIED':
|
|
8957
9206
|
return fail();
|
|
8958
9207
|
default:
|
|
8959
9208
|
return fail();
|
|
8960
9209
|
}
|
|
8961
9210
|
}
|
|
9211
|
+
function fromCompositeOperatorName(op) {
|
|
9212
|
+
switch (op) {
|
|
9213
|
+
case 'AND':
|
|
9214
|
+
return "and" /* CompositeOperator.AND */;
|
|
9215
|
+
case 'OR':
|
|
9216
|
+
return "or" /* CompositeOperator.OR */;
|
|
9217
|
+
default:
|
|
9218
|
+
return fail();
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
8962
9221
|
function toFieldPathReference(path) {
|
|
8963
9222
|
return { fieldPath: path.canonicalString() };
|
|
8964
9223
|
}
|
|
@@ -8975,12 +9234,32 @@ function toPropertyOrder(orderBy) {
|
|
|
8975
9234
|
function fromPropertyOrder(orderBy) {
|
|
8976
9235
|
return new OrderBy(fromFieldPathReference(orderBy.field), fromDirection(orderBy.direction));
|
|
8977
9236
|
}
|
|
8978
|
-
function fromFieldFilter(filter) {
|
|
8979
|
-
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
8980
|
-
}
|
|
8981
9237
|
// visible for testing
|
|
9238
|
+
function toFilter(filter) {
|
|
9239
|
+
if (filter instanceof FieldFilter) {
|
|
9240
|
+
return toUnaryOrFieldFilter(filter);
|
|
9241
|
+
}
|
|
9242
|
+
else if (filter instanceof CompositeFilter) {
|
|
9243
|
+
return toCompositeFilter(filter);
|
|
9244
|
+
}
|
|
9245
|
+
else {
|
|
9246
|
+
return fail();
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
function toCompositeFilter(filter) {
|
|
9250
|
+
const protos = filter.getFilters().map(filter => toFilter(filter));
|
|
9251
|
+
if (protos.length === 1) {
|
|
9252
|
+
return protos[0];
|
|
9253
|
+
}
|
|
9254
|
+
return {
|
|
9255
|
+
compositeFilter: {
|
|
9256
|
+
op: toCompositeOperatorName(filter.op),
|
|
9257
|
+
filters: protos
|
|
9258
|
+
}
|
|
9259
|
+
};
|
|
9260
|
+
}
|
|
8982
9261
|
function toUnaryOrFieldFilter(filter) {
|
|
8983
|
-
if (filter.op === "==" /* EQUAL */) {
|
|
9262
|
+
if (filter.op === "==" /* Operator.EQUAL */) {
|
|
8984
9263
|
if (isNanValue(filter.value)) {
|
|
8985
9264
|
return {
|
|
8986
9265
|
unaryFilter: {
|
|
@@ -8998,7 +9277,7 @@ function toUnaryOrFieldFilter(filter) {
|
|
|
8998
9277
|
};
|
|
8999
9278
|
}
|
|
9000
9279
|
}
|
|
9001
|
-
else if (filter.op === "!=" /* NOT_EQUAL */) {
|
|
9280
|
+
else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
9002
9281
|
if (isNanValue(filter.value)) {
|
|
9003
9282
|
return {
|
|
9004
9283
|
unaryFilter: {
|
|
@@ -9028,22 +9307,22 @@ function fromUnaryFilter(filter) {
|
|
|
9028
9307
|
switch (filter.unaryFilter.op) {
|
|
9029
9308
|
case 'IS_NAN':
|
|
9030
9309
|
const nanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9031
|
-
return FieldFilter.create(nanField, "==" /* EQUAL */, {
|
|
9310
|
+
return FieldFilter.create(nanField, "==" /* Operator.EQUAL */, {
|
|
9032
9311
|
doubleValue: NaN
|
|
9033
9312
|
});
|
|
9034
9313
|
case 'IS_NULL':
|
|
9035
9314
|
const nullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9036
|
-
return FieldFilter.create(nullField, "==" /* EQUAL */, {
|
|
9315
|
+
return FieldFilter.create(nullField, "==" /* Operator.EQUAL */, {
|
|
9037
9316
|
nullValue: 'NULL_VALUE'
|
|
9038
9317
|
});
|
|
9039
9318
|
case 'IS_NOT_NAN':
|
|
9040
9319
|
const notNanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9041
|
-
return FieldFilter.create(notNanField, "!=" /* NOT_EQUAL */, {
|
|
9320
|
+
return FieldFilter.create(notNanField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9042
9321
|
doubleValue: NaN
|
|
9043
9322
|
});
|
|
9044
9323
|
case 'IS_NOT_NULL':
|
|
9045
9324
|
const notNullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9046
|
-
return FieldFilter.create(notNullField, "!=" /* NOT_EQUAL */, {
|
|
9325
|
+
return FieldFilter.create(notNullField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9047
9326
|
nullValue: 'NULL_VALUE'
|
|
9048
9327
|
});
|
|
9049
9328
|
case 'OPERATOR_UNSPECIFIED':
|
|
@@ -9052,6 +9331,12 @@ function fromUnaryFilter(filter) {
|
|
|
9052
9331
|
return fail();
|
|
9053
9332
|
}
|
|
9054
9333
|
}
|
|
9334
|
+
function fromFieldFilter(filter) {
|
|
9335
|
+
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
9336
|
+
}
|
|
9337
|
+
function fromCompositeFilter(filter) {
|
|
9338
|
+
return CompositeFilter.create(filter.compositeFilter.filters.map(filter => fromFilter(filter)), fromCompositeOperatorName(filter.compositeFilter.op));
|
|
9339
|
+
}
|
|
9055
9340
|
function toDocumentMask(fieldMask) {
|
|
9056
9341
|
const canonicalFields = [];
|
|
9057
9342
|
fieldMask.fields.forEach(field => canonicalFields.push(field.canonicalString()));
|
|
@@ -9291,7 +9576,7 @@ function fromDbTarget(dbTarget) {
|
|
|
9291
9576
|
else {
|
|
9292
9577
|
target = fromQueryTarget(dbTarget.query);
|
|
9293
9578
|
}
|
|
9294
|
-
return new TargetData(target, dbTarget.targetId, 0 /* Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9579
|
+
return new TargetData(target, dbTarget.targetId, 0 /* TargetPurpose.Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9295
9580
|
}
|
|
9296
9581
|
/** Encodes TargetData into a DbTarget for storage locally. */
|
|
9297
9582
|
function toDbTarget(localSerializer, targetData) {
|
|
@@ -9368,7 +9653,7 @@ function fromBundledQuery(bundledQuery) {
|
|
|
9368
9653
|
structuredQuery: bundledQuery.structuredQuery
|
|
9369
9654
|
});
|
|
9370
9655
|
if (bundledQuery.limitType === 'LAST') {
|
|
9371
|
-
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
9656
|
+
return queryWithLimit(query, query.limit, "L" /* LimitType.Last */);
|
|
9372
9657
|
}
|
|
9373
9658
|
return query;
|
|
9374
9659
|
}
|
|
@@ -10143,7 +10428,7 @@ class IndexByteEncoder {
|
|
|
10143
10428
|
this.orderedCode.seed(encodedBytes);
|
|
10144
10429
|
}
|
|
10145
10430
|
forKind(kind) {
|
|
10146
|
-
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
10431
|
+
return kind === 0 /* IndexKind.ASCENDING */ ? this.ascending : this.descending;
|
|
10147
10432
|
}
|
|
10148
10433
|
encodedBytes() {
|
|
10149
10434
|
return this.orderedCode.encodedBytes();
|
|
@@ -10298,6 +10583,7 @@ class TargetIndexMatcher {
|
|
|
10298
10583
|
* omitted.
|
|
10299
10584
|
*/
|
|
10300
10585
|
servedByIndex(index) {
|
|
10586
|
+
hardAssert(index.collectionGroup === this.collectionId);
|
|
10301
10587
|
// If there is an array element, find a matching filter.
|
|
10302
10588
|
const arraySegment = fieldIndexGetArraySegment(index);
|
|
10303
10589
|
if (arraySegment !== undefined &&
|
|
@@ -10361,19 +10647,268 @@ class TargetIndexMatcher {
|
|
|
10361
10647
|
if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
|
|
10362
10648
|
return false;
|
|
10363
10649
|
}
|
|
10364
|
-
const isArrayOperator = filter.op === "array-contains" /* ARRAY_CONTAINS */ ||
|
|
10365
|
-
filter.op === "array-contains-any" /* ARRAY_CONTAINS_ANY */;
|
|
10366
|
-
return (segment.kind === 2 /* CONTAINS */) === isArrayOperator;
|
|
10650
|
+
const isArrayOperator = filter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
10651
|
+
filter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
10652
|
+
return (segment.kind === 2 /* IndexKind.CONTAINS */) === isArrayOperator;
|
|
10367
10653
|
}
|
|
10368
10654
|
matchesOrderBy(orderBy, segment) {
|
|
10369
10655
|
if (!orderBy.field.isEqual(segment.fieldPath)) {
|
|
10370
10656
|
return false;
|
|
10371
10657
|
}
|
|
10372
|
-
return ((segment.kind === 0 /* ASCENDING */ &&
|
|
10373
|
-
orderBy.dir === "asc" /* ASCENDING */) ||
|
|
10374
|
-
(segment.kind === 1 /* DESCENDING */ &&
|
|
10375
|
-
orderBy.dir === "desc" /* DESCENDING */));
|
|
10658
|
+
return ((segment.kind === 0 /* IndexKind.ASCENDING */ &&
|
|
10659
|
+
orderBy.dir === "asc" /* Direction.ASCENDING */) ||
|
|
10660
|
+
(segment.kind === 1 /* IndexKind.DESCENDING */ &&
|
|
10661
|
+
orderBy.dir === "desc" /* Direction.DESCENDING */));
|
|
10662
|
+
}
|
|
10663
|
+
}
|
|
10664
|
+
|
|
10665
|
+
/**
|
|
10666
|
+
* @license
|
|
10667
|
+
* Copyright 2022 Google LLC
|
|
10668
|
+
*
|
|
10669
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10670
|
+
* you may not use this file except in compliance with the License.
|
|
10671
|
+
* You may obtain a copy of the License at
|
|
10672
|
+
*
|
|
10673
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10674
|
+
*
|
|
10675
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10676
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10677
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10678
|
+
* See the License for the specific language governing permissions and
|
|
10679
|
+
* limitations under the License.
|
|
10680
|
+
*/
|
|
10681
|
+
/**
|
|
10682
|
+
* Provides utility functions that help with boolean logic transformations needed for handling
|
|
10683
|
+
* complex filters used in queries.
|
|
10684
|
+
*/
|
|
10685
|
+
/**
|
|
10686
|
+
* The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in
|
|
10687
|
+
* [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given
|
|
10688
|
+
* input into a disjunction of equality filters and returns the expanded filter.
|
|
10689
|
+
*/
|
|
10690
|
+
function computeInExpansion(filter) {
|
|
10691
|
+
var _a, _b;
|
|
10692
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10693
|
+
if (filter instanceof FieldFilter) {
|
|
10694
|
+
if (filter instanceof InFilter) {
|
|
10695
|
+
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))) || [];
|
|
10696
|
+
return CompositeFilter.create(expandedFilters, "or" /* CompositeOperator.OR */);
|
|
10697
|
+
}
|
|
10698
|
+
else {
|
|
10699
|
+
// We have reached other kinds of field filters.
|
|
10700
|
+
return filter;
|
|
10701
|
+
}
|
|
10702
|
+
}
|
|
10703
|
+
// We have a composite filter.
|
|
10704
|
+
const expandedFilters = filter.filters.map(subfilter => computeInExpansion(subfilter));
|
|
10705
|
+
return CompositeFilter.create(expandedFilters, filter.op);
|
|
10706
|
+
}
|
|
10707
|
+
/**
|
|
10708
|
+
* Given a composite filter, returns the list of terms in its disjunctive normal form.
|
|
10709
|
+
*
|
|
10710
|
+
* <p>Each element in the return value is one term of the resulting DNF. For instance: For the
|
|
10711
|
+
* input: (A || B) && C, the DNF form is: (A && C) || (B && C), and the return value is a list
|
|
10712
|
+
* with two elements: a composite filter that performs (A && C), and a composite filter that
|
|
10713
|
+
* performs (B && C).
|
|
10714
|
+
*
|
|
10715
|
+
* @param filter the composite filter to calculate DNF transform for.
|
|
10716
|
+
* @return the terms in the DNF transform.
|
|
10717
|
+
*/
|
|
10718
|
+
function getDnfTerms(filter) {
|
|
10719
|
+
if (filter.getFilters().length === 0) {
|
|
10720
|
+
return [];
|
|
10721
|
+
}
|
|
10722
|
+
const result = computeDistributedNormalForm(computeInExpansion(filter));
|
|
10723
|
+
hardAssert(isDisjunctiveNormalForm(result));
|
|
10724
|
+
if (isSingleFieldFilter(result) || isFlatConjunction(result)) {
|
|
10725
|
+
return [result];
|
|
10726
|
+
}
|
|
10727
|
+
return result.getFilters();
|
|
10728
|
+
}
|
|
10729
|
+
/** Returns true if the given filter is a single field filter. e.g. (a == 10). */
|
|
10730
|
+
function isSingleFieldFilter(filter) {
|
|
10731
|
+
return filter instanceof FieldFilter;
|
|
10732
|
+
}
|
|
10733
|
+
/**
|
|
10734
|
+
* Returns true if the given filter is the conjunction of one or more field filters. e.g. (a == 10
|
|
10735
|
+
* && b == 20)
|
|
10736
|
+
*/
|
|
10737
|
+
function isFlatConjunction(filter) {
|
|
10738
|
+
return (filter instanceof CompositeFilter &&
|
|
10739
|
+
compositeFilterIsFlatConjunction(filter));
|
|
10740
|
+
}
|
|
10741
|
+
/**
|
|
10742
|
+
* Returns whether or not the given filter is in disjunctive normal form (DNF).
|
|
10743
|
+
*
|
|
10744
|
+
* <p>In boolean logic, a disjunctive normal form (DNF) is a canonical normal form of a logical
|
|
10745
|
+
* formula consisting of a disjunction of conjunctions; it can also be described as an OR of ANDs.
|
|
10746
|
+
*
|
|
10747
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
|
10748
|
+
*/
|
|
10749
|
+
function isDisjunctiveNormalForm(filter) {
|
|
10750
|
+
return (isSingleFieldFilter(filter) ||
|
|
10751
|
+
isFlatConjunction(filter) ||
|
|
10752
|
+
isDisjunctionOfFieldFiltersAndFlatConjunctions(filter));
|
|
10753
|
+
}
|
|
10754
|
+
/**
|
|
10755
|
+
* Returns true if the given filter is the disjunction of one or more "flat conjunctions" and
|
|
10756
|
+
* field filters. e.g. (a == 10) || (b==20 && c==30)
|
|
10757
|
+
*/
|
|
10758
|
+
function isDisjunctionOfFieldFiltersAndFlatConjunctions(filter) {
|
|
10759
|
+
if (filter instanceof CompositeFilter) {
|
|
10760
|
+
if (compositeFilterIsDisjunction(filter)) {
|
|
10761
|
+
for (const subFilter of filter.getFilters()) {
|
|
10762
|
+
if (!isSingleFieldFilter(subFilter) && !isFlatConjunction(subFilter)) {
|
|
10763
|
+
return false;
|
|
10764
|
+
}
|
|
10765
|
+
}
|
|
10766
|
+
return true;
|
|
10767
|
+
}
|
|
10768
|
+
}
|
|
10769
|
+
return false;
|
|
10770
|
+
}
|
|
10771
|
+
function computeDistributedNormalForm(filter) {
|
|
10772
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10773
|
+
if (filter instanceof FieldFilter) {
|
|
10774
|
+
return filter;
|
|
10775
|
+
}
|
|
10776
|
+
if (filter.filters.length === 1) {
|
|
10777
|
+
return computeDistributedNormalForm(filter.filters[0]);
|
|
10778
|
+
}
|
|
10779
|
+
// Compute DNF for each of the subfilters first
|
|
10780
|
+
const result = filter.filters.map(subfilter => computeDistributedNormalForm(subfilter));
|
|
10781
|
+
let newFilter = CompositeFilter.create(result, filter.op);
|
|
10782
|
+
newFilter = applyAssociation(newFilter);
|
|
10783
|
+
if (isDisjunctiveNormalForm(newFilter)) {
|
|
10784
|
+
return newFilter;
|
|
10785
|
+
}
|
|
10786
|
+
hardAssert(newFilter instanceof CompositeFilter);
|
|
10787
|
+
hardAssert(compositeFilterIsConjunction(newFilter));
|
|
10788
|
+
hardAssert(newFilter.filters.length > 1);
|
|
10789
|
+
return newFilter.filters.reduce((runningResult, filter) => applyDistribution(runningResult, filter));
|
|
10790
|
+
}
|
|
10791
|
+
function applyDistribution(lhs, rhs) {
|
|
10792
|
+
hardAssert(lhs instanceof FieldFilter || lhs instanceof CompositeFilter);
|
|
10793
|
+
hardAssert(rhs instanceof FieldFilter || rhs instanceof CompositeFilter);
|
|
10794
|
+
let result;
|
|
10795
|
+
if (lhs instanceof FieldFilter) {
|
|
10796
|
+
if (rhs instanceof FieldFilter) {
|
|
10797
|
+
// FieldFilter FieldFilter
|
|
10798
|
+
result = applyDistributionFieldFilters(lhs, rhs);
|
|
10799
|
+
}
|
|
10800
|
+
else {
|
|
10801
|
+
// FieldFilter CompositeFilter
|
|
10802
|
+
result = applyDistributionFieldAndCompositeFilters(lhs, rhs);
|
|
10803
|
+
}
|
|
10804
|
+
}
|
|
10805
|
+
else {
|
|
10806
|
+
if (rhs instanceof FieldFilter) {
|
|
10807
|
+
// CompositeFilter FieldFilter
|
|
10808
|
+
result = applyDistributionFieldAndCompositeFilters(rhs, lhs);
|
|
10809
|
+
}
|
|
10810
|
+
else {
|
|
10811
|
+
// CompositeFilter CompositeFilter
|
|
10812
|
+
result = applyDistributionCompositeFilters(lhs, rhs);
|
|
10813
|
+
}
|
|
10814
|
+
}
|
|
10815
|
+
return applyAssociation(result);
|
|
10816
|
+
}
|
|
10817
|
+
function applyDistributionFieldFilters(lhs, rhs) {
|
|
10818
|
+
// Conjunction distribution for two field filters is the conjunction of them.
|
|
10819
|
+
return CompositeFilter.create([lhs, rhs], "and" /* CompositeOperator.AND */);
|
|
10820
|
+
}
|
|
10821
|
+
function applyDistributionCompositeFilters(lhs, rhs) {
|
|
10822
|
+
hardAssert(lhs.filters.length > 0 && rhs.filters.length > 0);
|
|
10823
|
+
// There are four cases:
|
|
10824
|
+
// (A & B) & (C & D) --> (A & B & C & D)
|
|
10825
|
+
// (A & B) & (C | D) --> (A & B & C) | (A & B & D)
|
|
10826
|
+
// (A | B) & (C & D) --> (C & D & A) | (C & D & B)
|
|
10827
|
+
// (A | B) & (C | D) --> (A & C) | (A & D) | (B & C) | (B & D)
|
|
10828
|
+
// Case 1 is a merge.
|
|
10829
|
+
if (compositeFilterIsConjunction(lhs) && compositeFilterIsConjunction(rhs)) {
|
|
10830
|
+
return compositeFilterWithAddedFilters(lhs, rhs.getFilters());
|
|
10831
|
+
}
|
|
10832
|
+
// Case 2,3,4 all have at least one side (lhs or rhs) that is a disjunction. In all three cases
|
|
10833
|
+
// we should take each element of the disjunction and distribute it over the other side, and
|
|
10834
|
+
// return the disjunction of the distribution results.
|
|
10835
|
+
const disjunctionSide = compositeFilterIsDisjunction(lhs) ? lhs : rhs;
|
|
10836
|
+
const otherSide = compositeFilterIsDisjunction(lhs) ? rhs : lhs;
|
|
10837
|
+
const results = disjunctionSide.filters.map(subfilter => applyDistribution(subfilter, otherSide));
|
|
10838
|
+
return CompositeFilter.create(results, "or" /* CompositeOperator.OR */);
|
|
10839
|
+
}
|
|
10840
|
+
function applyDistributionFieldAndCompositeFilters(fieldFilter, compositeFilter) {
|
|
10841
|
+
// There are two cases:
|
|
10842
|
+
// A & (B & C) --> (A & B & C)
|
|
10843
|
+
// A & (B | C) --> (A & B) | (A & C)
|
|
10844
|
+
if (compositeFilterIsConjunction(compositeFilter)) {
|
|
10845
|
+
// Case 1
|
|
10846
|
+
return compositeFilterWithAddedFilters(compositeFilter, fieldFilter.getFilters());
|
|
10847
|
+
}
|
|
10848
|
+
else {
|
|
10849
|
+
// Case 2
|
|
10850
|
+
const newFilters = compositeFilter.filters.map(subfilter => applyDistribution(fieldFilter, subfilter));
|
|
10851
|
+
return CompositeFilter.create(newFilters, "or" /* CompositeOperator.OR */);
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10854
|
+
/**
|
|
10855
|
+
* Applies the associativity property to the given filter and returns the resulting filter.
|
|
10856
|
+
*
|
|
10857
|
+
* <ul>
|
|
10858
|
+
* <li>A | (B | C) == (A | B) | C == (A | B | C)
|
|
10859
|
+
* <li>A & (B & C) == (A & B) & C == (A & B & C)
|
|
10860
|
+
* </ul>
|
|
10861
|
+
*
|
|
10862
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Associative_property#Propositional_logic
|
|
10863
|
+
*/
|
|
10864
|
+
function applyAssociation(filter) {
|
|
10865
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10866
|
+
if (filter instanceof FieldFilter) {
|
|
10867
|
+
return filter;
|
|
10868
|
+
}
|
|
10869
|
+
const filters = filter.getFilters();
|
|
10870
|
+
// If the composite filter only contains 1 filter, apply associativity to it.
|
|
10871
|
+
if (filters.length === 1) {
|
|
10872
|
+
return applyAssociation(filters[0]);
|
|
10873
|
+
}
|
|
10874
|
+
// Associativity applied to a flat composite filter results is itself.
|
|
10875
|
+
if (compositeFilterIsFlat(filter)) {
|
|
10876
|
+
return filter;
|
|
10877
|
+
}
|
|
10878
|
+
// First apply associativity to all subfilters. This will in turn recursively apply
|
|
10879
|
+
// associativity to all nested composite filters and field filters.
|
|
10880
|
+
const updatedFilters = filters.map(subfilter => applyAssociation(subfilter));
|
|
10881
|
+
// For composite subfilters that perform the same kind of logical operation as `compositeFilter`
|
|
10882
|
+
// take out their filters and add them to `compositeFilter`. For example:
|
|
10883
|
+
// compositeFilter = (A | (B | C | D))
|
|
10884
|
+
// compositeSubfilter = (B | C | D)
|
|
10885
|
+
// Result: (A | B | C | D)
|
|
10886
|
+
// Note that the `compositeSubfilter` has been eliminated, and its filters (B, C, D) have been
|
|
10887
|
+
// added to the top-level "compositeFilter".
|
|
10888
|
+
const newSubfilters = [];
|
|
10889
|
+
updatedFilters.forEach(subfilter => {
|
|
10890
|
+
if (subfilter instanceof FieldFilter) {
|
|
10891
|
+
newSubfilters.push(subfilter);
|
|
10892
|
+
}
|
|
10893
|
+
else if (subfilter instanceof CompositeFilter) {
|
|
10894
|
+
if (subfilter.op === filter.op) {
|
|
10895
|
+
// compositeFilter: (A | (B | C))
|
|
10896
|
+
// compositeSubfilter: (B | C)
|
|
10897
|
+
// Result: (A | B | C)
|
|
10898
|
+
newSubfilters.push(...subfilter.filters);
|
|
10899
|
+
}
|
|
10900
|
+
else {
|
|
10901
|
+
// compositeFilter: (A | (B & C))
|
|
10902
|
+
// compositeSubfilter: (B & C)
|
|
10903
|
+
// Result: (A | (B & C))
|
|
10904
|
+
newSubfilters.push(subfilter);
|
|
10905
|
+
}
|
|
10906
|
+
}
|
|
10907
|
+
});
|
|
10908
|
+
if (newSubfilters.length === 1) {
|
|
10909
|
+
return newSubfilters[0];
|
|
10376
10910
|
}
|
|
10911
|
+
return CompositeFilter.create(newSubfilters, filter.op);
|
|
10377
10912
|
}
|
|
10378
10913
|
|
|
10379
10914
|
/**
|
|
@@ -10420,7 +10955,7 @@ class MemoryIndexManager {
|
|
|
10420
10955
|
}
|
|
10421
10956
|
getIndexType(transaction, target) {
|
|
10422
10957
|
// Field indices are not supported with memory persistence.
|
|
10423
|
-
return PersistencePromise.resolve(0 /* NONE */);
|
|
10958
|
+
return PersistencePromise.resolve(0 /* IndexType.NONE */);
|
|
10424
10959
|
}
|
|
10425
10960
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10426
10961
|
// Field indices are not supported with memory persistence.
|
|
@@ -10643,8 +11178,14 @@ class IndexedDbIndexManager {
|
|
|
10643
11178
|
if (subTargets) {
|
|
10644
11179
|
return subTargets;
|
|
10645
11180
|
}
|
|
10646
|
-
|
|
10647
|
-
|
|
11181
|
+
if (target.filters.length === 0) {
|
|
11182
|
+
subTargets = [target];
|
|
11183
|
+
}
|
|
11184
|
+
else {
|
|
11185
|
+
// There is an implicit AND operation between all the filters stored in the target
|
|
11186
|
+
const dnf = getDnfTerms(CompositeFilter.create(target.filters, "and" /* CompositeOperator.AND */));
|
|
11187
|
+
subTargets = dnf.map(term => newTarget(target.path, target.collectionGroup, target.orderBy, term.getFilters(), target.limit, target.startAt, target.endAt));
|
|
11188
|
+
}
|
|
10648
11189
|
this.targetToDnfSubTargets.set(target, subTargets);
|
|
10649
11190
|
return subTargets;
|
|
10650
11191
|
}
|
|
@@ -10702,18 +11243,29 @@ class IndexedDbIndexManager {
|
|
|
10702
11243
|
});
|
|
10703
11244
|
}
|
|
10704
11245
|
getIndexType(transaction, target) {
|
|
10705
|
-
let indexType = 2 /* FULL */;
|
|
10706
|
-
|
|
11246
|
+
let indexType = 2 /* IndexType.FULL */;
|
|
11247
|
+
const subTargets = this.getSubTargets(target);
|
|
11248
|
+
return PersistencePromise.forEach(subTargets, (target) => {
|
|
10707
11249
|
return this.getFieldIndex(transaction, target).next(index => {
|
|
10708
11250
|
if (!index) {
|
|
10709
|
-
indexType = 0 /* NONE */;
|
|
11251
|
+
indexType = 0 /* IndexType.NONE */;
|
|
10710
11252
|
}
|
|
10711
|
-
else if (indexType !== 0 /* NONE */ &&
|
|
11253
|
+
else if (indexType !== 0 /* IndexType.NONE */ &&
|
|
10712
11254
|
index.fields.length < targetGetSegmentCount(target)) {
|
|
10713
|
-
indexType = 1 /* PARTIAL */;
|
|
11255
|
+
indexType = 1 /* IndexType.PARTIAL */;
|
|
10714
11256
|
}
|
|
10715
11257
|
});
|
|
10716
|
-
}).next(() =>
|
|
11258
|
+
}).next(() => {
|
|
11259
|
+
// OR queries have more than one sub-target (one sub-target per DNF term). We currently consider
|
|
11260
|
+
// OR queries that have a `limit` to have a partial index. For such queries we perform sorting
|
|
11261
|
+
// and apply the limit in memory as a post-processing step.
|
|
11262
|
+
if (targetHasLimit(target) &&
|
|
11263
|
+
subTargets.length > 1 &&
|
|
11264
|
+
indexType === 2 /* IndexType.FULL */) {
|
|
11265
|
+
return 1 /* IndexType.PARTIAL */;
|
|
11266
|
+
}
|
|
11267
|
+
return indexType;
|
|
11268
|
+
});
|
|
10717
11269
|
}
|
|
10718
11270
|
/**
|
|
10719
11271
|
* Returns the byte encoded form of the directional values in the field index.
|
|
@@ -10735,7 +11287,7 @@ class IndexedDbIndexManager {
|
|
|
10735
11287
|
/** Encodes a single value to the ascending index format. */
|
|
10736
11288
|
encodeSingleElement(value) {
|
|
10737
11289
|
const encoder = new IndexByteEncoder();
|
|
10738
|
-
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
11290
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* IndexKind.ASCENDING */));
|
|
10739
11291
|
return encoder.encodedBytes();
|
|
10740
11292
|
}
|
|
10741
11293
|
/**
|
|
@@ -10751,15 +11303,15 @@ class IndexedDbIndexManager {
|
|
|
10751
11303
|
* Encodes the given field values according to the specification in `target`.
|
|
10752
11304
|
* For IN queries, a list of possible values is returned.
|
|
10753
11305
|
*/
|
|
10754
|
-
encodeValues(fieldIndex, target,
|
|
10755
|
-
if (
|
|
11306
|
+
encodeValues(fieldIndex, target, values) {
|
|
11307
|
+
if (values === null) {
|
|
10756
11308
|
return [];
|
|
10757
11309
|
}
|
|
10758
11310
|
let encoders = [];
|
|
10759
11311
|
encoders.push(new IndexByteEncoder());
|
|
10760
|
-
let
|
|
11312
|
+
let valueIdx = 0;
|
|
10761
11313
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
10762
|
-
const value =
|
|
11314
|
+
const value = values[valueIdx++];
|
|
10763
11315
|
for (const encoder of encoders) {
|
|
10764
11316
|
if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
|
|
10765
11317
|
encoders = this.expandIndexValues(encoders, segment, value);
|
|
@@ -10810,7 +11362,7 @@ class IndexedDbIndexManager {
|
|
|
10810
11362
|
isInFilter(target, fieldPath) {
|
|
10811
11363
|
return !!target.filters.find(f => f instanceof FieldFilter &&
|
|
10812
11364
|
f.field.isEqual(fieldPath) &&
|
|
10813
|
-
(f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
|
|
11365
|
+
(f.op === "in" /* Operator.IN */ || f.op === "not-in" /* Operator.NOT_IN */));
|
|
10814
11366
|
}
|
|
10815
11367
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10816
11368
|
const indexes = indexConfigurationStore(transaction);
|
|
@@ -10999,24 +11551,36 @@ class IndexedDbIndexManager {
|
|
|
10999
11551
|
bounds.push(upper);
|
|
11000
11552
|
const ranges = [];
|
|
11001
11553
|
for (let i = 0; i < bounds.length; i += 2) {
|
|
11002
|
-
|
|
11554
|
+
// If we encounter two bounds that will create an unmatchable key range,
|
|
11555
|
+
// then we return an empty set of key ranges.
|
|
11556
|
+
if (this.isRangeMatchable(bounds[i], bounds[i + 1])) {
|
|
11557
|
+
return [];
|
|
11558
|
+
}
|
|
11559
|
+
const lowerBound = [
|
|
11003
11560
|
bounds[i].indexId,
|
|
11004
11561
|
this.uid,
|
|
11005
11562
|
bounds[i].arrayValue,
|
|
11006
11563
|
bounds[i].directionalValue,
|
|
11007
11564
|
EMPTY_VALUE,
|
|
11008
11565
|
[]
|
|
11009
|
-
]
|
|
11566
|
+
];
|
|
11567
|
+
const upperBound = [
|
|
11010
11568
|
bounds[i + 1].indexId,
|
|
11011
11569
|
this.uid,
|
|
11012
11570
|
bounds[i + 1].arrayValue,
|
|
11013
11571
|
bounds[i + 1].directionalValue,
|
|
11014
11572
|
EMPTY_VALUE,
|
|
11015
11573
|
[]
|
|
11016
|
-
]
|
|
11574
|
+
];
|
|
11575
|
+
ranges.push(IDBKeyRange.bound(lowerBound, upperBound));
|
|
11017
11576
|
}
|
|
11018
11577
|
return ranges;
|
|
11019
11578
|
}
|
|
11579
|
+
isRangeMatchable(lowerBound, upperBound) {
|
|
11580
|
+
// If lower bound is greater than the upper bound, then the key
|
|
11581
|
+
// range can never be matched.
|
|
11582
|
+
return indexEntryComparator(lowerBound, upperBound) > 0;
|
|
11583
|
+
}
|
|
11020
11584
|
getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
|
|
11021
11585
|
return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
|
|
11022
11586
|
}
|
|
@@ -11993,7 +12557,7 @@ class LruScheduler {
|
|
|
11993
12557
|
}
|
|
11994
12558
|
scheduleGC(delay) {
|
|
11995
12559
|
logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
|
|
11996
|
-
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
|
|
12560
|
+
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* TimerId.LruGarbageCollection */, delay, async () => {
|
|
11997
12561
|
this.gcTask = null;
|
|
11998
12562
|
try {
|
|
11999
12563
|
await this.localStore.collectGarbage(this.garbageCollector);
|
|
@@ -12891,7 +13455,7 @@ class LocalDocumentsView {
|
|
|
12891
13455
|
.getOverlay(transaction, key)
|
|
12892
13456
|
.next(value => {
|
|
12893
13457
|
overlay = value;
|
|
12894
|
-
return this.
|
|
13458
|
+
return this.remoteDocumentCache.getEntry(transaction, key);
|
|
12895
13459
|
})
|
|
12896
13460
|
.next(document => {
|
|
12897
13461
|
if (overlay !== null) {
|
|
@@ -13114,7 +13678,9 @@ class LocalDocumentsView {
|
|
|
13114
13678
|
if (originalDocs.get(key)) {
|
|
13115
13679
|
return PersistencePromise.resolve();
|
|
13116
13680
|
}
|
|
13117
|
-
return this.
|
|
13681
|
+
return this.remoteDocumentCache
|
|
13682
|
+
.getEntry(transaction, key)
|
|
13683
|
+
.next(doc => {
|
|
13118
13684
|
modifiedDocs = modifiedDocs.insert(key, doc);
|
|
13119
13685
|
});
|
|
13120
13686
|
})
|
|
@@ -13188,12 +13754,6 @@ class LocalDocumentsView {
|
|
|
13188
13754
|
return results;
|
|
13189
13755
|
});
|
|
13190
13756
|
}
|
|
13191
|
-
/** Returns a base document that can be used to apply `overlay`. */
|
|
13192
|
-
getBaseDocument(transaction, key, overlay) {
|
|
13193
|
-
return overlay === null || overlay.mutation.type === 1 /* Patch */
|
|
13194
|
-
? this.remoteDocumentCache.getEntry(transaction, key)
|
|
13195
|
-
: PersistencePromise.resolve(MutableDocument.newInvalidDocument(key));
|
|
13196
|
-
}
|
|
13197
13757
|
}
|
|
13198
13758
|
|
|
13199
13759
|
/**
|
|
@@ -14943,7 +15503,7 @@ class IndexedDbPersistence {
|
|
|
14943
15503
|
* extend or acquire the primary lease if the client is eligible.
|
|
14944
15504
|
*/
|
|
14945
15505
|
scheduleClientMetadataAndPrimaryLeaseRefreshes() {
|
|
14946
|
-
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
15506
|
+
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* TimerId.ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
14947
15507
|
return this.updateClientMetadataAndTryBecomePrimary()
|
|
14948
15508
|
.then(() => this.maybeGarbageCollectMultiClientState())
|
|
14949
15509
|
.then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());
|
|
@@ -15953,7 +16513,7 @@ function localStoreAllocateTarget(localStore, target) {
|
|
|
15953
16513
|
return localStoreImpl.targetCache
|
|
15954
16514
|
.allocateTargetId(txn)
|
|
15955
16515
|
.next(targetId => {
|
|
15956
|
-
targetData = new TargetData(target, targetId, 0 /* Listen */, txn.currentSequenceNumber);
|
|
16516
|
+
targetData = new TargetData(target, targetId, 0 /* TargetPurpose.Listen */, txn.currentSequenceNumber);
|
|
15957
16517
|
return localStoreImpl.targetCache
|
|
15958
16518
|
.addTargetData(txn, targetData)
|
|
15959
16519
|
.next(() => targetData);
|
|
@@ -16375,11 +16935,11 @@ class QueryEngine {
|
|
|
16375
16935
|
return this.indexManager
|
|
16376
16936
|
.getIndexType(transaction, target)
|
|
16377
16937
|
.next(indexType => {
|
|
16378
|
-
if (indexType === 0 /* NONE */) {
|
|
16938
|
+
if (indexType === 0 /* IndexType.NONE */) {
|
|
16379
16939
|
// The target cannot be served from any index.
|
|
16380
16940
|
return null;
|
|
16381
16941
|
}
|
|
16382
|
-
if (query.limit !== null && indexType === 1 /* PARTIAL */) {
|
|
16942
|
+
if (query.limit !== null && indexType === 1 /* IndexType.PARTIAL */) {
|
|
16383
16943
|
// We cannot apply a limit for targets that are served using a partial
|
|
16384
16944
|
// index. If a partial index will be used to serve the target, the
|
|
16385
16945
|
// query may return a superset of documents that match the target
|
|
@@ -16387,7 +16947,7 @@ class QueryEngine {
|
|
|
16387
16947
|
// may return the correct set of documents in the wrong order (e.g. if
|
|
16388
16948
|
// the index doesn't include a segment for one of the orderBys).
|
|
16389
16949
|
// Therefore, a limit should not be applied in such cases.
|
|
16390
|
-
query = queryWithLimit(query, null, "F" /* First */);
|
|
16950
|
+
query = queryWithLimit(query, null, "F" /* LimitType.First */);
|
|
16391
16951
|
target = queryToTarget(query);
|
|
16392
16952
|
}
|
|
16393
16953
|
return this.indexManager
|
|
@@ -16408,7 +16968,7 @@ class QueryEngine {
|
|
|
16408
16968
|
// query's filters are included in the result set. The SDK
|
|
16409
16969
|
// can then apply the limit once all local edits are
|
|
16410
16970
|
// incorporated.
|
|
16411
|
-
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* First */));
|
|
16971
|
+
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* LimitType.First */));
|
|
16412
16972
|
}
|
|
16413
16973
|
return this.appendRemainingResults(transaction, previousResults, query, offset);
|
|
16414
16974
|
});
|
|
@@ -16487,7 +17047,7 @@ class QueryEngine {
|
|
|
16487
17047
|
// the boundary of the limit itself did not change and documents from cache
|
|
16488
17048
|
// will continue to be "rejected" by this boundary. Therefore, we can ignore
|
|
16489
17049
|
// any modifications that don't affect the last document.
|
|
16490
|
-
const docAtLimitEdge = query.limitType === "F" /* First */
|
|
17050
|
+
const docAtLimitEdge = query.limitType === "F" /* LimitType.First */
|
|
16491
17051
|
? sortedPreviousResults.last()
|
|
16492
17052
|
: sortedPreviousResults.first();
|
|
16493
17053
|
if (!docAtLimitEdge) {
|
|
@@ -17408,7 +17968,7 @@ function nodePromise(action) {
|
|
|
17408
17968
|
*/
|
|
17409
17969
|
// TODO: Fetch runtime version from grpc-js/package.json instead
|
|
17410
17970
|
// when there's a cleaner way to dynamic require JSON in both Node ESM and CJS
|
|
17411
|
-
const grpcVersion = '1.3
|
|
17971
|
+
const grpcVersion = '1.7.3';
|
|
17412
17972
|
const LOG_TAG$9 = 'Connection';
|
|
17413
17973
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
17414
17974
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
@@ -19856,7 +20416,8 @@ const nested = {
|
|
|
19856
20416
|
Operator: {
|
|
19857
20417
|
values: {
|
|
19858
20418
|
OPERATOR_UNSPECIFIED: 0,
|
|
19859
|
-
AND: 1
|
|
20419
|
+
AND: 1,
|
|
20420
|
+
OR: 2
|
|
19860
20421
|
}
|
|
19861
20422
|
}
|
|
19862
20423
|
}
|
|
@@ -20732,7 +21293,7 @@ class PersistentStream {
|
|
|
20732
21293
|
this.authCredentialsProvider = authCredentialsProvider;
|
|
20733
21294
|
this.appCheckCredentialsProvider = appCheckCredentialsProvider;
|
|
20734
21295
|
this.listener = listener;
|
|
20735
|
-
this.state = 0 /* Initial */;
|
|
21296
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20736
21297
|
/**
|
|
20737
21298
|
* A close count that's incremented every time the stream is closed; used by
|
|
20738
21299
|
* getCloseGuardedDispatcher() to invalidate callbacks that happen after
|
|
@@ -20752,8 +21313,8 @@ class PersistentStream {
|
|
|
20752
21313
|
* outbound requests.
|
|
20753
21314
|
*/
|
|
20754
21315
|
isStarted() {
|
|
20755
|
-
return (this.state === 1 /* Starting */ ||
|
|
20756
|
-
this.state === 5 /* Backoff */ ||
|
|
21316
|
+
return (this.state === 1 /* PersistentStreamState.Starting */ ||
|
|
21317
|
+
this.state === 5 /* PersistentStreamState.Backoff */ ||
|
|
20757
21318
|
this.isOpen());
|
|
20758
21319
|
}
|
|
20759
21320
|
/**
|
|
@@ -20761,8 +21322,8 @@ class PersistentStream {
|
|
|
20761
21322
|
* called) and the stream is ready for outbound requests.
|
|
20762
21323
|
*/
|
|
20763
21324
|
isOpen() {
|
|
20764
|
-
return (this.state === 2 /* Open */ ||
|
|
20765
|
-
this.state === 3 /* Healthy */);
|
|
21325
|
+
return (this.state === 2 /* PersistentStreamState.Open */ ||
|
|
21326
|
+
this.state === 3 /* PersistentStreamState.Healthy */);
|
|
20766
21327
|
}
|
|
20767
21328
|
/**
|
|
20768
21329
|
* Starts the RPC. Only allowed if isStarted() returns false. The stream is
|
|
@@ -20772,7 +21333,7 @@ class PersistentStream {
|
|
|
20772
21333
|
* When start returns, isStarted() will return true.
|
|
20773
21334
|
*/
|
|
20774
21335
|
start() {
|
|
20775
|
-
if (this.state === 4 /* Error */) {
|
|
21336
|
+
if (this.state === 4 /* PersistentStreamState.Error */) {
|
|
20776
21337
|
this.performBackoff();
|
|
20777
21338
|
return;
|
|
20778
21339
|
}
|
|
@@ -20786,7 +21347,7 @@ class PersistentStream {
|
|
|
20786
21347
|
*/
|
|
20787
21348
|
async stop() {
|
|
20788
21349
|
if (this.isStarted()) {
|
|
20789
|
-
await this.close(0 /* Initial */);
|
|
21350
|
+
await this.close(0 /* PersistentStreamState.Initial */);
|
|
20790
21351
|
}
|
|
20791
21352
|
}
|
|
20792
21353
|
/**
|
|
@@ -20798,7 +21359,7 @@ class PersistentStream {
|
|
|
20798
21359
|
* inhibit backoff if required.
|
|
20799
21360
|
*/
|
|
20800
21361
|
inhibitBackoff() {
|
|
20801
|
-
this.state = 0 /* Initial */;
|
|
21362
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20802
21363
|
this.backoff.reset();
|
|
20803
21364
|
}
|
|
20804
21365
|
/**
|
|
@@ -20828,7 +21389,7 @@ class PersistentStream {
|
|
|
20828
21389
|
if (this.isOpen()) {
|
|
20829
21390
|
// When timing out an idle stream there's no reason to force the stream into backoff when
|
|
20830
21391
|
// it restarts so set the stream state to Initial instead of Error.
|
|
20831
|
-
return this.close(0 /* Initial */);
|
|
21392
|
+
return this.close(0 /* PersistentStreamState.Initial */);
|
|
20832
21393
|
}
|
|
20833
21394
|
}
|
|
20834
21395
|
/** Marks the stream as active again. */
|
|
@@ -20866,7 +21427,7 @@ class PersistentStream {
|
|
|
20866
21427
|
// Invalidates any stream-related callbacks (e.g. from auth or the
|
|
20867
21428
|
// underlying stream), guaranteeing they won't execute.
|
|
20868
21429
|
this.closeCount++;
|
|
20869
|
-
if (finalState !== 4 /* Error */) {
|
|
21430
|
+
if (finalState !== 4 /* PersistentStreamState.Error */) {
|
|
20870
21431
|
// If this is an intentional close ensure we don't delay our next connection attempt.
|
|
20871
21432
|
this.backoff.reset();
|
|
20872
21433
|
}
|
|
@@ -20878,7 +21439,7 @@ class PersistentStream {
|
|
|
20878
21439
|
}
|
|
20879
21440
|
else if (error &&
|
|
20880
21441
|
error.code === Code.UNAUTHENTICATED &&
|
|
20881
|
-
this.state !== 3 /* Healthy */) {
|
|
21442
|
+
this.state !== 3 /* PersistentStreamState.Healthy */) {
|
|
20882
21443
|
// "unauthenticated" error means the token was rejected. This should rarely
|
|
20883
21444
|
// happen since both Auth and AppCheck ensure a sufficient TTL when we
|
|
20884
21445
|
// request a token. If a user manually resets their system clock this can
|
|
@@ -20906,7 +21467,7 @@ class PersistentStream {
|
|
|
20906
21467
|
*/
|
|
20907
21468
|
tearDown() { }
|
|
20908
21469
|
auth() {
|
|
20909
|
-
this.state = 1 /* Starting */;
|
|
21470
|
+
this.state = 1 /* PersistentStreamState.Starting */;
|
|
20910
21471
|
const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);
|
|
20911
21472
|
// TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.
|
|
20912
21473
|
const closeCount = this.closeCount;
|
|
@@ -20936,10 +21497,10 @@ class PersistentStream {
|
|
|
20936
21497
|
this.stream = this.startRpc(authToken, appCheckToken);
|
|
20937
21498
|
this.stream.onOpen(() => {
|
|
20938
21499
|
dispatchIfNotClosed(() => {
|
|
20939
|
-
this.state = 2 /* Open */;
|
|
21500
|
+
this.state = 2 /* PersistentStreamState.Open */;
|
|
20940
21501
|
this.healthCheck = this.queue.enqueueAfterDelay(this.healthTimerId, HEALTHY_TIMEOUT_MS, () => {
|
|
20941
21502
|
if (this.isOpen()) {
|
|
20942
|
-
this.state = 3 /* Healthy */;
|
|
21503
|
+
this.state = 3 /* PersistentStreamState.Healthy */;
|
|
20943
21504
|
}
|
|
20944
21505
|
return Promise.resolve();
|
|
20945
21506
|
});
|
|
@@ -20958,9 +21519,9 @@ class PersistentStream {
|
|
|
20958
21519
|
});
|
|
20959
21520
|
}
|
|
20960
21521
|
performBackoff() {
|
|
20961
|
-
this.state = 5 /* Backoff */;
|
|
21522
|
+
this.state = 5 /* PersistentStreamState.Backoff */;
|
|
20962
21523
|
this.backoff.backoffAndRun(async () => {
|
|
20963
|
-
this.state = 0 /* Initial */;
|
|
21524
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20964
21525
|
this.start();
|
|
20965
21526
|
});
|
|
20966
21527
|
}
|
|
@@ -20972,7 +21533,7 @@ class PersistentStream {
|
|
|
20972
21533
|
// we never expect this to happen because if we stop a stream ourselves,
|
|
20973
21534
|
// this callback will never be called. To prevent cases where we retry
|
|
20974
21535
|
// without a backoff accidentally, we set the stream to error in all cases.
|
|
20975
|
-
return this.close(4 /* Error */, error);
|
|
21536
|
+
return this.close(4 /* PersistentStreamState.Error */, error);
|
|
20976
21537
|
}
|
|
20977
21538
|
/**
|
|
20978
21539
|
* Returns a "dispatcher" function that dispatches operations onto the
|
|
@@ -21003,7 +21564,7 @@ class PersistentStream {
|
|
|
21003
21564
|
*/
|
|
21004
21565
|
class PersistentListenStream extends PersistentStream {
|
|
21005
21566
|
constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
|
|
21006
|
-
super(queue, "listen_stream_connection_backoff" /* ListenStreamConnectionBackoff */, "listen_stream_idle" /* ListenStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21567
|
+
super(queue, "listen_stream_connection_backoff" /* TimerId.ListenStreamConnectionBackoff */, "listen_stream_idle" /* TimerId.ListenStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21007
21568
|
this.serializer = serializer;
|
|
21008
21569
|
}
|
|
21009
21570
|
startRpc(authToken, appCheckToken) {
|
|
@@ -21062,7 +21623,7 @@ class PersistentListenStream extends PersistentStream {
|
|
|
21062
21623
|
*/
|
|
21063
21624
|
class PersistentWriteStream extends PersistentStream {
|
|
21064
21625
|
constructor(queue, connection, authCredentials, appCheckCredentials, serializer, listener) {
|
|
21065
|
-
super(queue, "write_stream_connection_backoff" /* WriteStreamConnectionBackoff */, "write_stream_idle" /* WriteStreamIdle */, "health_check_timeout" /* HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21626
|
+
super(queue, "write_stream_connection_backoff" /* TimerId.WriteStreamConnectionBackoff */, "write_stream_idle" /* TimerId.WriteStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21066
21627
|
this.serializer = serializer;
|
|
21067
21628
|
this.handshakeComplete_ = false;
|
|
21068
21629
|
}
|
|
@@ -21321,7 +21882,7 @@ class OnlineStateTracker {
|
|
|
21321
21882
|
this.asyncQueue = asyncQueue;
|
|
21322
21883
|
this.onlineStateHandler = onlineStateHandler;
|
|
21323
21884
|
/** The current OnlineState. */
|
|
21324
|
-
this.state = "Unknown" /* Unknown */;
|
|
21885
|
+
this.state = "Unknown" /* OnlineState.Unknown */;
|
|
21325
21886
|
/**
|
|
21326
21887
|
* A count of consecutive failures to open the stream. If it reaches the
|
|
21327
21888
|
* maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to
|
|
@@ -21350,12 +21911,12 @@ class OnlineStateTracker {
|
|
|
21350
21911
|
*/
|
|
21351
21912
|
handleWatchStreamStart() {
|
|
21352
21913
|
if (this.watchStreamFailures === 0) {
|
|
21353
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21354
|
-
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21914
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21915
|
+
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* TimerId.OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21355
21916
|
this.onlineStateTimer = null;
|
|
21356
21917
|
this.logClientOfflineWarningIfNecessary(`Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +
|
|
21357
21918
|
`seconds.`);
|
|
21358
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21919
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21359
21920
|
// NOTE: handleWatchStreamFailure() will continue to increment
|
|
21360
21921
|
// watchStreamFailures even though we are already marked Offline,
|
|
21361
21922
|
// but this is non-harmful.
|
|
@@ -21370,8 +21931,8 @@ class OnlineStateTracker {
|
|
|
21370
21931
|
* actually transition to the 'Offline' state.
|
|
21371
21932
|
*/
|
|
21372
21933
|
handleWatchStreamFailure(error) {
|
|
21373
|
-
if (this.state === "Online" /* Online */) {
|
|
21374
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21934
|
+
if (this.state === "Online" /* OnlineState.Online */) {
|
|
21935
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21375
21936
|
}
|
|
21376
21937
|
else {
|
|
21377
21938
|
this.watchStreamFailures++;
|
|
@@ -21379,7 +21940,7 @@ class OnlineStateTracker {
|
|
|
21379
21940
|
this.clearOnlineStateTimer();
|
|
21380
21941
|
this.logClientOfflineWarningIfNecessary(`Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +
|
|
21381
21942
|
`times. Most recent error: ${error.toString()}`);
|
|
21382
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21943
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21383
21944
|
}
|
|
21384
21945
|
}
|
|
21385
21946
|
}
|
|
@@ -21393,7 +21954,7 @@ class OnlineStateTracker {
|
|
|
21393
21954
|
set(newState) {
|
|
21394
21955
|
this.clearOnlineStateTimer();
|
|
21395
21956
|
this.watchStreamFailures = 0;
|
|
21396
|
-
if (newState === "Online" /* Online */) {
|
|
21957
|
+
if (newState === "Online" /* OnlineState.Online */) {
|
|
21397
21958
|
// We've connected to watch at least once. Don't warn the developer
|
|
21398
21959
|
// about being offline going forward.
|
|
21399
21960
|
this.shouldWarnClientIsOffline = false;
|
|
@@ -21520,7 +22081,7 @@ function newRemoteStore(localStore, datastore, asyncQueue, onlineStateHandler, c
|
|
|
21520
22081
|
/** Re-enables the network. Idempotent. */
|
|
21521
22082
|
function remoteStoreEnableNetwork(remoteStore) {
|
|
21522
22083
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21523
|
-
remoteStoreImpl.offlineCauses.delete(0 /* UserDisabled */);
|
|
22084
|
+
remoteStoreImpl.offlineCauses.delete(0 /* OfflineCause.UserDisabled */);
|
|
21524
22085
|
return enableNetworkInternal(remoteStoreImpl);
|
|
21525
22086
|
}
|
|
21526
22087
|
async function enableNetworkInternal(remoteStoreImpl) {
|
|
@@ -21536,10 +22097,10 @@ async function enableNetworkInternal(remoteStoreImpl) {
|
|
|
21536
22097
|
*/
|
|
21537
22098
|
async function remoteStoreDisableNetwork(remoteStore) {
|
|
21538
22099
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21539
|
-
remoteStoreImpl.offlineCauses.add(0 /* UserDisabled */);
|
|
22100
|
+
remoteStoreImpl.offlineCauses.add(0 /* OfflineCause.UserDisabled */);
|
|
21540
22101
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21541
22102
|
// Set the OnlineState to Offline so get()s return from cache, etc.
|
|
21542
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22103
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21543
22104
|
}
|
|
21544
22105
|
async function disableNetworkInternal(remoteStoreImpl) {
|
|
21545
22106
|
for (const networkStatusHandler of remoteStoreImpl.onNetworkStatusChange) {
|
|
@@ -21549,12 +22110,12 @@ async function disableNetworkInternal(remoteStoreImpl) {
|
|
|
21549
22110
|
async function remoteStoreShutdown(remoteStore) {
|
|
21550
22111
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21551
22112
|
logDebug(LOG_TAG$5, 'RemoteStore shutting down.');
|
|
21552
|
-
remoteStoreImpl.offlineCauses.add(5 /* Shutdown */);
|
|
22113
|
+
remoteStoreImpl.offlineCauses.add(5 /* OfflineCause.Shutdown */);
|
|
21553
22114
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21554
22115
|
remoteStoreImpl.connectivityMonitor.shutdown();
|
|
21555
22116
|
// Set the OnlineState to Unknown (rather than Offline) to avoid potentially
|
|
21556
22117
|
// triggering spurious listener events with cached data, etc.
|
|
21557
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22118
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21558
22119
|
}
|
|
21559
22120
|
/**
|
|
21560
22121
|
* Starts new listen for the given target. Uses resume token if provided. It
|
|
@@ -21594,7 +22155,7 @@ function remoteStoreUnlisten(remoteStore, targetId) {
|
|
|
21594
22155
|
// Revert to OnlineState.Unknown if the watch stream is not open and we
|
|
21595
22156
|
// have no listeners, since without any listens to send we cannot
|
|
21596
22157
|
// confirm if the stream is healthy and upgrade to OnlineState.Online.
|
|
21597
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22158
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21598
22159
|
}
|
|
21599
22160
|
}
|
|
21600
22161
|
}
|
|
@@ -21655,14 +22216,14 @@ async function onWatchStreamClose(remoteStoreImpl, error) {
|
|
|
21655
22216
|
// No need to restart watch stream because there are no active targets.
|
|
21656
22217
|
// The online state is set to unknown because there is no active attempt
|
|
21657
22218
|
// at establishing a connection
|
|
21658
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22219
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21659
22220
|
}
|
|
21660
22221
|
}
|
|
21661
22222
|
async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion) {
|
|
21662
22223
|
// Mark the client as online since we got a message from the server
|
|
21663
|
-
remoteStoreImpl.onlineStateTracker.set("Online" /* Online */);
|
|
22224
|
+
remoteStoreImpl.onlineStateTracker.set("Online" /* OnlineState.Online */);
|
|
21664
22225
|
if (watchChange instanceof WatchTargetChange &&
|
|
21665
|
-
watchChange.state === 2 /* Removed */ &&
|
|
22226
|
+
watchChange.state === 2 /* WatchTargetChangeState.Removed */ &&
|
|
21666
22227
|
watchChange.cause) {
|
|
21667
22228
|
// There was an error on a target, don't wait for a consistent snapshot
|
|
21668
22229
|
// to raise events
|
|
@@ -21710,10 +22271,10 @@ async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion
|
|
|
21710
22271
|
*/
|
|
21711
22272
|
async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
21712
22273
|
if (isIndexedDbTransactionError(e)) {
|
|
21713
|
-
remoteStoreImpl.offlineCauses.add(1 /* IndexedDbFailed */);
|
|
22274
|
+
remoteStoreImpl.offlineCauses.add(1 /* OfflineCause.IndexedDbFailed */);
|
|
21714
22275
|
// Disable network and raise offline snapshots
|
|
21715
22276
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21716
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22277
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21717
22278
|
if (!op) {
|
|
21718
22279
|
// Use a simple read operation to determine if IndexedDB recovered.
|
|
21719
22280
|
// Ideally, we would expose a health check directly on SimpleDb, but
|
|
@@ -21724,7 +22285,7 @@ async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
|
21724
22285
|
remoteStoreImpl.asyncQueue.enqueueRetryable(async () => {
|
|
21725
22286
|
logDebug(LOG_TAG$5, 'Retrying IndexedDB access');
|
|
21726
22287
|
await op();
|
|
21727
|
-
remoteStoreImpl.offlineCauses.delete(1 /* IndexedDbFailed */);
|
|
22288
|
+
remoteStoreImpl.offlineCauses.delete(1 /* OfflineCause.IndexedDbFailed */);
|
|
21728
22289
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21729
22290
|
});
|
|
21730
22291
|
}
|
|
@@ -21775,7 +22336,7 @@ function raiseWatchSnapshot(remoteStoreImpl, snapshotVersion) {
|
|
|
21775
22336
|
// mismatch, but don't actually retain that in listenTargets. This ensures
|
|
21776
22337
|
// that we flag the first re-listen this way without impacting future
|
|
21777
22338
|
// listens of this target (that might happen e.g. on reconnect).
|
|
21778
|
-
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
22339
|
+
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* TargetPurpose.ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
21779
22340
|
sendWatchRequest(remoteStoreImpl, requestTargetData);
|
|
21780
22341
|
});
|
|
21781
22342
|
return remoteStoreImpl.remoteSyncer.applyRemoteEvent(remoteEvent);
|
|
@@ -21906,10 +22467,10 @@ async function handleWriteError(remoteStoreImpl, error) {
|
|
|
21906
22467
|
}
|
|
21907
22468
|
async function restartNetwork(remoteStore) {
|
|
21908
22469
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21909
|
-
remoteStoreImpl.offlineCauses.add(4 /* ConnectivityChange */);
|
|
22470
|
+
remoteStoreImpl.offlineCauses.add(4 /* OfflineCause.ConnectivityChange */);
|
|
21910
22471
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21911
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
21912
|
-
remoteStoreImpl.offlineCauses.delete(4 /* ConnectivityChange */);
|
|
22472
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
22473
|
+
remoteStoreImpl.offlineCauses.delete(4 /* OfflineCause.ConnectivityChange */);
|
|
21913
22474
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21914
22475
|
}
|
|
21915
22476
|
async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
@@ -21920,14 +22481,14 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21920
22481
|
// Tear down and re-create our network streams. This will ensure we get a
|
|
21921
22482
|
// fresh auth token for the new user and re-fill the write pipeline with
|
|
21922
22483
|
// new mutations from the LocalStore (since mutations are per-user).
|
|
21923
|
-
remoteStoreImpl.offlineCauses.add(3 /* CredentialChange */);
|
|
22484
|
+
remoteStoreImpl.offlineCauses.add(3 /* OfflineCause.CredentialChange */);
|
|
21924
22485
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21925
22486
|
if (usesNetwork) {
|
|
21926
22487
|
// Don't set the network status to Unknown if we are offline.
|
|
21927
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22488
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21928
22489
|
}
|
|
21929
22490
|
await remoteStoreImpl.remoteSyncer.handleCredentialChange(user);
|
|
21930
|
-
remoteStoreImpl.offlineCauses.delete(3 /* CredentialChange */);
|
|
22491
|
+
remoteStoreImpl.offlineCauses.delete(3 /* OfflineCause.CredentialChange */);
|
|
21931
22492
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21932
22493
|
}
|
|
21933
22494
|
/**
|
|
@@ -21936,13 +22497,13 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21936
22497
|
async function remoteStoreApplyPrimaryState(remoteStore, isPrimary) {
|
|
21937
22498
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21938
22499
|
if (isPrimary) {
|
|
21939
|
-
remoteStoreImpl.offlineCauses.delete(2 /* IsSecondary */);
|
|
22500
|
+
remoteStoreImpl.offlineCauses.delete(2 /* OfflineCause.IsSecondary */);
|
|
21940
22501
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21941
22502
|
}
|
|
21942
22503
|
else if (!isPrimary) {
|
|
21943
|
-
remoteStoreImpl.offlineCauses.add(2 /* IsSecondary */);
|
|
22504
|
+
remoteStoreImpl.offlineCauses.add(2 /* OfflineCause.IsSecondary */);
|
|
21944
22505
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21945
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22506
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21946
22507
|
}
|
|
21947
22508
|
}
|
|
21948
22509
|
/**
|
|
@@ -21968,7 +22529,7 @@ function ensureWatchStream(remoteStoreImpl) {
|
|
|
21968
22529
|
startWatchStream(remoteStoreImpl);
|
|
21969
22530
|
}
|
|
21970
22531
|
else {
|
|
21971
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22532
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21972
22533
|
}
|
|
21973
22534
|
}
|
|
21974
22535
|
else {
|
|
@@ -22299,46 +22860,46 @@ class DocumentChangeSet {
|
|
|
22299
22860
|
return;
|
|
22300
22861
|
}
|
|
22301
22862
|
// Merge the new change with the existing change.
|
|
22302
|
-
if (change.type !== 0 /* Added */ &&
|
|
22303
|
-
oldChange.type === 3 /* Metadata */) {
|
|
22863
|
+
if (change.type !== 0 /* ChangeType.Added */ &&
|
|
22864
|
+
oldChange.type === 3 /* ChangeType.Metadata */) {
|
|
22304
22865
|
this.changeMap = this.changeMap.insert(key, change);
|
|
22305
22866
|
}
|
|
22306
|
-
else if (change.type === 3 /* Metadata */ &&
|
|
22307
|
-
oldChange.type !== 1 /* Removed */) {
|
|
22867
|
+
else if (change.type === 3 /* ChangeType.Metadata */ &&
|
|
22868
|
+
oldChange.type !== 1 /* ChangeType.Removed */) {
|
|
22308
22869
|
this.changeMap = this.changeMap.insert(key, {
|
|
22309
22870
|
type: oldChange.type,
|
|
22310
22871
|
doc: change.doc
|
|
22311
22872
|
});
|
|
22312
22873
|
}
|
|
22313
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22314
|
-
oldChange.type === 2 /* Modified */) {
|
|
22874
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22875
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
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
|
}
|
|
22320
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22321
|
-
oldChange.type === 0 /* Added */) {
|
|
22881
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22882
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22322
22883
|
this.changeMap = this.changeMap.insert(key, {
|
|
22323
|
-
type: 0 /* Added */,
|
|
22884
|
+
type: 0 /* ChangeType.Added */,
|
|
22324
22885
|
doc: change.doc
|
|
22325
22886
|
});
|
|
22326
22887
|
}
|
|
22327
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22328
|
-
oldChange.type === 0 /* Added */) {
|
|
22888
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22889
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22329
22890
|
this.changeMap = this.changeMap.remove(key);
|
|
22330
22891
|
}
|
|
22331
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22332
|
-
oldChange.type === 2 /* Modified */) {
|
|
22892
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22893
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
22333
22894
|
this.changeMap = this.changeMap.insert(key, {
|
|
22334
|
-
type: 1 /* Removed */,
|
|
22895
|
+
type: 1 /* ChangeType.Removed */,
|
|
22335
22896
|
doc: oldChange.doc
|
|
22336
22897
|
});
|
|
22337
22898
|
}
|
|
22338
|
-
else if (change.type === 0 /* Added */ &&
|
|
22339
|
-
oldChange.type === 1 /* Removed */) {
|
|
22899
|
+
else if (change.type === 0 /* ChangeType.Added */ &&
|
|
22900
|
+
oldChange.type === 1 /* ChangeType.Removed */) {
|
|
22340
22901
|
this.changeMap = this.changeMap.insert(key, {
|
|
22341
|
-
type: 2 /* Modified */,
|
|
22902
|
+
type: 2 /* ChangeType.Modified */,
|
|
22342
22903
|
doc: change.doc
|
|
22343
22904
|
});
|
|
22344
22905
|
}
|
|
@@ -22377,7 +22938,7 @@ class ViewSnapshot {
|
|
|
22377
22938
|
static fromInitialDocuments(query, documents, mutatedKeys, fromCache, hasCachedResults) {
|
|
22378
22939
|
const changes = [];
|
|
22379
22940
|
documents.forEach(doc => {
|
|
22380
|
-
changes.push({ type: 0 /* Added */, doc });
|
|
22941
|
+
changes.push({ type: 0 /* ChangeType.Added */, doc });
|
|
22381
22942
|
});
|
|
22382
22943
|
return new ViewSnapshot(query, documents, DocumentSet.emptySet(documents), changes, mutatedKeys, fromCache,
|
|
22383
22944
|
/* syncStateChanged= */ true,
|
|
@@ -22443,7 +23004,7 @@ function newEventManager() {
|
|
|
22443
23004
|
class EventManagerImpl {
|
|
22444
23005
|
constructor() {
|
|
22445
23006
|
this.queries = new ObjectMap(q => canonifyQuery(q), queryEquals);
|
|
22446
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23007
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22447
23008
|
this.snapshotsInSyncListeners = new Set();
|
|
22448
23009
|
}
|
|
22449
23010
|
}
|
|
@@ -22574,7 +23135,7 @@ class QueryListener {
|
|
|
22574
23135
|
*/
|
|
22575
23136
|
this.raisedInitialEvent = false;
|
|
22576
23137
|
this.snap = null;
|
|
22577
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23138
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22578
23139
|
this.options = options || {};
|
|
22579
23140
|
}
|
|
22580
23141
|
/**
|
|
@@ -22588,7 +23149,7 @@ class QueryListener {
|
|
|
22588
23149
|
// Remove the metadata only changes.
|
|
22589
23150
|
const docChanges = [];
|
|
22590
23151
|
for (const docChange of snap.docChanges) {
|
|
22591
|
-
if (docChange.type !== 3 /* Metadata */) {
|
|
23152
|
+
if (docChange.type !== 3 /* ChangeType.Metadata */) {
|
|
22592
23153
|
docChanges.push(docChange);
|
|
22593
23154
|
}
|
|
22594
23155
|
}
|
|
@@ -22631,7 +23192,7 @@ class QueryListener {
|
|
|
22631
23192
|
}
|
|
22632
23193
|
// NOTE: We consider OnlineState.Unknown as online (it should become Offline
|
|
22633
23194
|
// or Online if we wait long enough).
|
|
22634
|
-
const maybeOnline = onlineState !== "Offline" /* Offline */;
|
|
23195
|
+
const maybeOnline = onlineState !== "Offline" /* OnlineState.Offline */;
|
|
22635
23196
|
// Don't raise the event if we're online, aren't synced yet (checked
|
|
22636
23197
|
// above) and are waiting for a sync.
|
|
22637
23198
|
if (this.options.waitForSyncWhenOnline && maybeOnline) {
|
|
@@ -22641,7 +23202,7 @@ class QueryListener {
|
|
|
22641
23202
|
// or we are offline.
|
|
22642
23203
|
return (!snap.docs.isEmpty() ||
|
|
22643
23204
|
snap.hasCachedResults ||
|
|
22644
|
-
onlineState === "Offline" /* Offline */);
|
|
23205
|
+
onlineState === "Offline" /* OnlineState.Offline */);
|
|
22645
23206
|
}
|
|
22646
23207
|
shouldRaiseEvent(snap) {
|
|
22647
23208
|
// We don't need to handle includeDocumentMetadataChanges here because
|
|
@@ -22700,10 +23261,10 @@ class LocalViewChanges {
|
|
|
22700
23261
|
let removedKeys = documentKeySet();
|
|
22701
23262
|
for (const docChange of viewSnapshot.docChanges) {
|
|
22702
23263
|
switch (docChange.type) {
|
|
22703
|
-
case 0 /* Added */:
|
|
23264
|
+
case 0 /* ChangeType.Added */:
|
|
22704
23265
|
addedKeys = addedKeys.add(docChange.doc.key);
|
|
22705
23266
|
break;
|
|
22706
|
-
case 1 /* Removed */:
|
|
23267
|
+
case 1 /* ChangeType.Removed */:
|
|
22707
23268
|
removedKeys = removedKeys.add(docChange.doc.key);
|
|
22708
23269
|
break;
|
|
22709
23270
|
// do nothing
|
|
@@ -22951,11 +23512,11 @@ class View {
|
|
|
22951
23512
|
//
|
|
22952
23513
|
// Note that this should never get used in a refill (when previousChanges is
|
|
22953
23514
|
// set), because there will only be adds -- no deletes or updates.
|
|
22954
|
-
const lastDocInLimit = this.query.limitType === "F" /* First */ &&
|
|
23515
|
+
const lastDocInLimit = this.query.limitType === "F" /* LimitType.First */ &&
|
|
22955
23516
|
oldDocumentSet.size === this.query.limit
|
|
22956
23517
|
? oldDocumentSet.last()
|
|
22957
23518
|
: null;
|
|
22958
|
-
const firstDocInLimit = this.query.limitType === "L" /* Last */ &&
|
|
23519
|
+
const firstDocInLimit = this.query.limitType === "L" /* LimitType.Last */ &&
|
|
22959
23520
|
oldDocumentSet.size === this.query.limit
|
|
22960
23521
|
? oldDocumentSet.first()
|
|
22961
23522
|
: null;
|
|
@@ -22978,7 +23539,7 @@ class View {
|
|
|
22978
23539
|
if (!docsEqual) {
|
|
22979
23540
|
if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {
|
|
22980
23541
|
changeSet.track({
|
|
22981
|
-
type: 2 /* Modified */,
|
|
23542
|
+
type: 2 /* ChangeType.Modified */,
|
|
22982
23543
|
doc: newDoc
|
|
22983
23544
|
});
|
|
22984
23545
|
changeApplied = true;
|
|
@@ -22994,16 +23555,16 @@ class View {
|
|
|
22994
23555
|
}
|
|
22995
23556
|
}
|
|
22996
23557
|
else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {
|
|
22997
|
-
changeSet.track({ type: 3 /* Metadata */, doc: newDoc });
|
|
23558
|
+
changeSet.track({ type: 3 /* ChangeType.Metadata */, doc: newDoc });
|
|
22998
23559
|
changeApplied = true;
|
|
22999
23560
|
}
|
|
23000
23561
|
}
|
|
23001
23562
|
else if (!oldDoc && newDoc) {
|
|
23002
|
-
changeSet.track({ type: 0 /* Added */, doc: newDoc });
|
|
23563
|
+
changeSet.track({ type: 0 /* ChangeType.Added */, doc: newDoc });
|
|
23003
23564
|
changeApplied = true;
|
|
23004
23565
|
}
|
|
23005
23566
|
else if (oldDoc && !newDoc) {
|
|
23006
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23567
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
23007
23568
|
changeApplied = true;
|
|
23008
23569
|
if (lastDocInLimit || firstDocInLimit) {
|
|
23009
23570
|
// A doc was removed from a full limit query. We'll need to
|
|
@@ -23031,12 +23592,12 @@ class View {
|
|
|
23031
23592
|
// Drop documents out to meet limit/limitToLast requirement.
|
|
23032
23593
|
if (this.query.limit !== null) {
|
|
23033
23594
|
while (newDocumentSet.size > this.query.limit) {
|
|
23034
|
-
const oldDoc = this.query.limitType === "F" /* First */
|
|
23595
|
+
const oldDoc = this.query.limitType === "F" /* LimitType.First */
|
|
23035
23596
|
? newDocumentSet.last()
|
|
23036
23597
|
: newDocumentSet.first();
|
|
23037
23598
|
newDocumentSet = newDocumentSet.delete(oldDoc.key);
|
|
23038
23599
|
newMutatedKeys = newMutatedKeys.delete(oldDoc.key);
|
|
23039
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23600
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
23040
23601
|
}
|
|
23041
23602
|
}
|
|
23042
23603
|
return {
|
|
@@ -23084,7 +23645,7 @@ class View {
|
|
|
23084
23645
|
? this.updateLimboDocuments()
|
|
23085
23646
|
: [];
|
|
23086
23647
|
const synced = this.limboDocuments.size === 0 && this.current;
|
|
23087
|
-
const newSyncState = synced ? 1 /* Synced */ : 0 /* Local */;
|
|
23648
|
+
const newSyncState = synced ? 1 /* SyncState.Synced */ : 0 /* SyncState.Local */;
|
|
23088
23649
|
const syncStateChanged = newSyncState !== this.syncState;
|
|
23089
23650
|
this.syncState = newSyncState;
|
|
23090
23651
|
if (changes.length === 0 && !syncStateChanged) {
|
|
@@ -23092,7 +23653,7 @@ class View {
|
|
|
23092
23653
|
return { limboChanges };
|
|
23093
23654
|
}
|
|
23094
23655
|
else {
|
|
23095
|
-
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* Local */, syncStateChanged,
|
|
23656
|
+
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* SyncState.Local */, syncStateChanged,
|
|
23096
23657
|
/* excludesMetadataChanges= */ false, targetChange
|
|
23097
23658
|
? targetChange.resumeToken.approximateByteSize() > 0
|
|
23098
23659
|
: false);
|
|
@@ -23107,7 +23668,7 @@ class View {
|
|
|
23107
23668
|
* ViewChange if the view's syncState changes as a result.
|
|
23108
23669
|
*/
|
|
23109
23670
|
applyOnlineStateChange(onlineState) {
|
|
23110
|
-
if (this.current && onlineState === "Offline" /* Offline */) {
|
|
23671
|
+
if (this.current && onlineState === "Offline" /* OnlineState.Offline */) {
|
|
23111
23672
|
// If we're offline, set `current` to false and then call applyChanges()
|
|
23112
23673
|
// to refresh our syncState and generate a ViewChange as appropriate. We
|
|
23113
23674
|
// are guaranteed to get a new TargetChange that sets `current` back to
|
|
@@ -23222,22 +23783,22 @@ class View {
|
|
|
23222
23783
|
*/
|
|
23223
23784
|
// PORTING NOTE: Multi-tab only.
|
|
23224
23785
|
computeInitialSnapshot() {
|
|
23225
|
-
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* Local */, this.hasCachedResults);
|
|
23786
|
+
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* SyncState.Local */, this.hasCachedResults);
|
|
23226
23787
|
}
|
|
23227
23788
|
}
|
|
23228
23789
|
function compareChangeType(c1, c2) {
|
|
23229
23790
|
const order = (change) => {
|
|
23230
23791
|
switch (change) {
|
|
23231
|
-
case 0 /* Added */:
|
|
23792
|
+
case 0 /* ChangeType.Added */:
|
|
23232
23793
|
return 1;
|
|
23233
|
-
case 2 /* Modified */:
|
|
23794
|
+
case 2 /* ChangeType.Modified */:
|
|
23234
23795
|
return 2;
|
|
23235
|
-
case 3 /* Metadata */:
|
|
23796
|
+
case 3 /* ChangeType.Metadata */:
|
|
23236
23797
|
// A metadata change is converted to a modified change at the public
|
|
23237
23798
|
// api layer. Since we sort by document key and then change type,
|
|
23238
23799
|
// metadata and modified changes must be sorted equivalently.
|
|
23239
23800
|
return 2;
|
|
23240
|
-
case 1 /* Removed */:
|
|
23801
|
+
case 1 /* ChangeType.Removed */:
|
|
23241
23802
|
return 0;
|
|
23242
23803
|
default:
|
|
23243
23804
|
return fail();
|
|
@@ -23355,7 +23916,7 @@ class SyncEngineImpl {
|
|
|
23355
23916
|
/** Stores user callbacks waiting for all pending writes to be acknowledged. */
|
|
23356
23917
|
this.pendingWritesCallbacks = new Map();
|
|
23357
23918
|
this.limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();
|
|
23358
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23919
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
23359
23920
|
// The primary state is set to `true` or `false` immediately after Firestore
|
|
23360
23921
|
// startup. In the interim, a client should only be considered primary if
|
|
23361
23922
|
// `isPrimary` is true.
|
|
@@ -23419,7 +23980,7 @@ async function initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId,
|
|
|
23419
23980
|
/* usePreviousResults= */ true);
|
|
23420
23981
|
const view = new View(query, queryResult.remoteKeys);
|
|
23421
23982
|
const viewDocChanges = view.computeDocChanges(queryResult.documents);
|
|
23422
|
-
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* Offline */, resumeToken);
|
|
23983
|
+
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* OnlineState.Offline */, resumeToken);
|
|
23423
23984
|
const viewChange = view.applyChanges(viewDocChanges,
|
|
23424
23985
|
/* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, synthesizedTargetChange);
|
|
23425
23986
|
updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
|
|
@@ -23545,9 +24106,9 @@ function syncEngineApplyOnlineStateChange(syncEngine, onlineState, source) {
|
|
|
23545
24106
|
// tab remains online) and only apply the primary tab's online state from
|
|
23546
24107
|
// SharedClientState.
|
|
23547
24108
|
if ((syncEngineImpl.isPrimaryClient &&
|
|
23548
|
-
source === 0 /* RemoteStore */) ||
|
|
24109
|
+
source === 0 /* OnlineStateSource.RemoteStore */) ||
|
|
23549
24110
|
(!syncEngineImpl.isPrimaryClient &&
|
|
23550
|
-
source === 1 /* SharedClientState */)) {
|
|
24111
|
+
source === 1 /* OnlineStateSource.SharedClientState */)) {
|
|
23551
24112
|
const newViewSnapshots = [];
|
|
23552
24113
|
syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => {
|
|
23553
24114
|
const viewChange = queryView.view.applyOnlineStateChange(onlineState);
|
|
@@ -23815,7 +24376,7 @@ function pumpEnqueuedLimboResolutions(syncEngineImpl) {
|
|
|
23815
24376
|
syncEngineImpl.activeLimboResolutionsByTarget.set(limboTargetId, new LimboResolution(key));
|
|
23816
24377
|
syncEngineImpl.activeLimboTargetsByKey =
|
|
23817
24378
|
syncEngineImpl.activeLimboTargetsByKey.insert(key, limboTargetId);
|
|
23818
|
-
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* LimboResolution */, ListenSequence.INVALID));
|
|
24379
|
+
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* TargetPurpose.LimboResolution */, ListenSequence.INVALID));
|
|
23819
24380
|
}
|
|
23820
24381
|
}
|
|
23821
24382
|
async function syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, changes, remoteEvent) {
|
|
@@ -24068,7 +24629,7 @@ async function synchronizeQueryViewsAndRaiseSnapshots(syncEngine, targets, trans
|
|
|
24068
24629
|
*/
|
|
24069
24630
|
// PORTING NOTE: Multi-Tab only.
|
|
24070
24631
|
function synthesizeTargetToQuery(target) {
|
|
24071
|
-
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* First */, target.startAt, target.endAt);
|
|
24632
|
+
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* LimitType.First */, target.startAt, target.endAt);
|
|
24072
24633
|
}
|
|
24073
24634
|
/** Returns the IDs of the clients that are currently active. */
|
|
24074
24635
|
// PORTING NOTE: Multi-Tab only.
|
|
@@ -24399,7 +24960,7 @@ class OnlineComponentProvider {
|
|
|
24399
24960
|
this.eventManager = this.createEventManager(cfg);
|
|
24400
24961
|
this.syncEngine = this.createSyncEngine(cfg,
|
|
24401
24962
|
/* startAsPrimary=*/ !offlineComponentProvider.synchronizeTabs);
|
|
24402
|
-
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* SharedClientState */);
|
|
24963
|
+
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* OnlineStateSource.SharedClientState */);
|
|
24403
24964
|
this.remoteStore.remoteSyncer.handleCredentialChange =
|
|
24404
24965
|
syncEngineHandleCredentialChange.bind(null, this.syncEngine);
|
|
24405
24966
|
await remoteStoreApplyPrimaryState(this.remoteStore, this.syncEngine.isPrimaryClient);
|
|
@@ -24413,7 +24974,7 @@ class OnlineComponentProvider {
|
|
|
24413
24974
|
return newDatastore(cfg.authCredentials, cfg.appCheckCredentials, connection, serializer);
|
|
24414
24975
|
}
|
|
24415
24976
|
createRemoteStore(cfg) {
|
|
24416
|
-
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* RemoteStore */), newConnectivityMonitor());
|
|
24977
|
+
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* OnlineStateSource.RemoteStore */), newConnectivityMonitor());
|
|
24417
24978
|
}
|
|
24418
24979
|
createSyncEngine(cfg, startAsPrimary) {
|
|
24419
24980
|
return newSyncEngine(this.localStore, this.remoteStore, this.eventManager, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, startAsPrimary);
|
|
@@ -25170,7 +25731,7 @@ class TransactionRunner {
|
|
|
25170
25731
|
this.updateFunction = updateFunction;
|
|
25171
25732
|
this.deferred = deferred;
|
|
25172
25733
|
this.attemptsRemaining = options.maxAttempts;
|
|
25173
|
-
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
|
|
25734
|
+
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
|
|
25174
25735
|
}
|
|
25175
25736
|
/** Runs the transaction and sets the result on deferred. */
|
|
25176
25737
|
run() {
|
|
@@ -26203,7 +26764,7 @@ class AsyncQueueImpl {
|
|
|
26203
26764
|
// List of TimerIds to fast-forward delays for.
|
|
26204
26765
|
this.timerIdsToSkip = [];
|
|
26205
26766
|
// Backoff timer used to schedule retries for retryable operations
|
|
26206
|
-
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
|
|
26767
|
+
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
|
|
26207
26768
|
// Visibility handler that triggers an immediate retry of all retryable
|
|
26208
26769
|
// operations. Meant to speed up recovery when we regain file system access
|
|
26209
26770
|
// after page comes into foreground.
|
|
@@ -26373,7 +26934,7 @@ class AsyncQueueImpl {
|
|
|
26373
26934
|
this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
|
|
26374
26935
|
for (const op of this.delayedOperations) {
|
|
26375
26936
|
op.skipDelay();
|
|
26376
|
-
if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
|
|
26937
|
+
if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
|
|
26377
26938
|
break;
|
|
26378
26939
|
}
|
|
26379
26940
|
}
|
|
@@ -27286,12 +27847,12 @@ class ParsedUpdateData {
|
|
|
27286
27847
|
}
|
|
27287
27848
|
function isWrite(dataSource) {
|
|
27288
27849
|
switch (dataSource) {
|
|
27289
|
-
case 0 /* Set */: // fall through
|
|
27290
|
-
case 2 /* MergeSet */: // fall through
|
|
27291
|
-
case 1 /* Update */:
|
|
27850
|
+
case 0 /* UserDataSource.Set */: // fall through
|
|
27851
|
+
case 2 /* UserDataSource.MergeSet */: // fall through
|
|
27852
|
+
case 1 /* UserDataSource.Update */:
|
|
27292
27853
|
return true;
|
|
27293
|
-
case 3 /* Argument */:
|
|
27294
|
-
case 4 /* ArrayArgument */:
|
|
27854
|
+
case 3 /* UserDataSource.Argument */:
|
|
27855
|
+
case 4 /* UserDataSource.ArrayArgument */:
|
|
27295
27856
|
return false;
|
|
27296
27857
|
default:
|
|
27297
27858
|
throw fail();
|
|
@@ -27416,8 +27977,8 @@ function newUserDataReader(firestore) {
|
|
|
27416
27977
|
/** Parse document data from a set() call. */
|
|
27417
27978
|
function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
|
|
27418
27979
|
const context = userDataReader.createContext(options.merge || options.mergeFields
|
|
27419
|
-
? 2 /* MergeSet */
|
|
27420
|
-
: 0 /* Set */, methodName, targetDoc, hasConverter);
|
|
27980
|
+
? 2 /* UserDataSource.MergeSet */
|
|
27981
|
+
: 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
|
|
27421
27982
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27422
27983
|
const updateData = parseObject(input, context);
|
|
27423
27984
|
let fieldMask;
|
|
@@ -27448,12 +28009,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
|
|
|
27448
28009
|
}
|
|
27449
28010
|
class DeleteFieldValueImpl extends FieldValue {
|
|
27450
28011
|
_toFieldTransform(context) {
|
|
27451
|
-
if (context.dataSource === 2 /* MergeSet */) {
|
|
28012
|
+
if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
|
|
27452
28013
|
// No transform to add for a delete, but we need to add it to our
|
|
27453
28014
|
// fieldMask so it gets deleted.
|
|
27454
28015
|
context.fieldMask.push(context.path);
|
|
27455
28016
|
}
|
|
27456
|
-
else if (context.dataSource === 1 /* Update */) {
|
|
28017
|
+
else if (context.dataSource === 1 /* UserDataSource.Update */) {
|
|
27457
28018
|
throw context.createError(`${this._methodName}() can only appear at the top level ` +
|
|
27458
28019
|
'of your update data');
|
|
27459
28020
|
}
|
|
@@ -27486,7 +28047,7 @@ class DeleteFieldValueImpl extends FieldValue {
|
|
|
27486
28047
|
*/
|
|
27487
28048
|
function createSentinelChildContext(fieldValue, context, arrayElement) {
|
|
27488
28049
|
return new ParseContextImpl({
|
|
27489
|
-
dataSource: 3 /* Argument */,
|
|
28050
|
+
dataSource: 3 /* UserDataSource.Argument */,
|
|
27490
28051
|
targetDoc: context.settings.targetDoc,
|
|
27491
28052
|
methodName: fieldValue._methodName,
|
|
27492
28053
|
arrayElement
|
|
@@ -27550,7 +28111,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
|
|
|
27550
28111
|
}
|
|
27551
28112
|
/** Parse update data from an update() call. */
|
|
27552
28113
|
function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
27553
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28114
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27554
28115
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27555
28116
|
const fieldMaskPaths = [];
|
|
27556
28117
|
const updateData = ObjectValue.empty();
|
|
@@ -27577,7 +28138,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
|
27577
28138
|
}
|
|
27578
28139
|
/** Parse update data from a list of field/value arguments. */
|
|
27579
28140
|
function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
|
|
27580
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28141
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27581
28142
|
const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
|
|
27582
28143
|
const values = [value];
|
|
27583
28144
|
if (moreFieldsAndValues.length % 2 !== 0) {
|
|
@@ -27624,7 +28185,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
|
|
|
27624
28185
|
* contain additional arrays (e.g. the operand of an `in` query).
|
|
27625
28186
|
*/
|
|
27626
28187
|
function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
|
|
27627
|
-
const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
|
|
28188
|
+
const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
|
|
27628
28189
|
const parsed = parseData(input, context);
|
|
27629
28190
|
return parsed;
|
|
27630
28191
|
}
|
|
@@ -27674,7 +28235,7 @@ function parseData(input, context) {
|
|
|
27674
28235
|
// contain additional arrays (each representing an individual field
|
|
27675
28236
|
// value), so we disable this validation.
|
|
27676
28237
|
if (context.settings.arrayElement &&
|
|
27677
|
-
context.dataSource !== 4 /* ArrayArgument */) {
|
|
28238
|
+
context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
|
|
27678
28239
|
throw context.createError('Nested arrays are not supported');
|
|
27679
28240
|
}
|
|
27680
28241
|
return parseArray(input, context);
|
|
@@ -28056,164 +28617,336 @@ function fieldPathFromArgument(methodName, arg) {
|
|
|
28056
28617
|
* limitations under the License.
|
|
28057
28618
|
*/
|
|
28058
28619
|
function validateHasExplicitOrderByForLimitToLast(query) {
|
|
28059
|
-
if (query.limitType === "L" /* Last */ &&
|
|
28620
|
+
if (query.limitType === "L" /* LimitType.Last */ &&
|
|
28060
28621
|
query.explicitOrderBy.length === 0) {
|
|
28061
28622
|
throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
|
|
28062
28623
|
}
|
|
28063
28624
|
}
|
|
28625
|
+
/**
|
|
28626
|
+
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
|
|
28627
|
+
* to a Firestore query.
|
|
28628
|
+
*/
|
|
28629
|
+
class AppliableConstraint {
|
|
28630
|
+
}
|
|
28064
28631
|
/**
|
|
28065
28632
|
* A `QueryConstraint` is used to narrow the set of documents returned by a
|
|
28066
28633
|
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
|
|
28067
|
-
* {@link orderBy}, {@link
|
|
28068
|
-
* endBefore
|
|
28634
|
+
* {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
|
|
28635
|
+
* endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
|
|
28069
28636
|
* can then be passed to {@link query} to create a new query instance that
|
|
28070
28637
|
* also contains this `QueryConstraint`.
|
|
28071
28638
|
*/
|
|
28072
|
-
class QueryConstraint {
|
|
28639
|
+
class QueryConstraint extends AppliableConstraint {
|
|
28073
28640
|
}
|
|
28074
|
-
|
|
28075
|
-
|
|
28076
|
-
|
|
28077
|
-
|
|
28078
|
-
|
|
28079
|
-
|
|
28080
|
-
|
|
28081
|
-
* existing or new constraints.
|
|
28082
|
-
*/
|
|
28083
|
-
function query(query, ...queryConstraints) {
|
|
28641
|
+
function query(query, queryConstraint, ...additionalQueryConstraints) {
|
|
28642
|
+
let queryConstraints = [];
|
|
28643
|
+
if (queryConstraint instanceof AppliableConstraint) {
|
|
28644
|
+
queryConstraints.push(queryConstraint);
|
|
28645
|
+
}
|
|
28646
|
+
queryConstraints = queryConstraints.concat(additionalQueryConstraints);
|
|
28647
|
+
validateQueryConstraintArray(queryConstraints);
|
|
28084
28648
|
for (const constraint of queryConstraints) {
|
|
28085
28649
|
query = constraint._apply(query);
|
|
28086
28650
|
}
|
|
28087
28651
|
return query;
|
|
28088
28652
|
}
|
|
28089
|
-
|
|
28653
|
+
/**
|
|
28654
|
+
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
|
|
28655
|
+
* a Firestore query by filtering on one or more document fields.
|
|
28656
|
+
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
|
|
28657
|
+
* be passed to {@link query} to create a new query instance that also contains
|
|
28658
|
+
* this `QueryFieldFilterConstraint`.
|
|
28659
|
+
*/
|
|
28660
|
+
class QueryFieldFilterConstraint extends QueryConstraint {
|
|
28661
|
+
/**
|
|
28662
|
+
* @internal
|
|
28663
|
+
*/
|
|
28090
28664
|
constructor(_field, _op, _value) {
|
|
28091
28665
|
super();
|
|
28092
28666
|
this._field = _field;
|
|
28093
28667
|
this._op = _op;
|
|
28094
28668
|
this._value = _value;
|
|
28669
|
+
/** The type of this query constraint */
|
|
28095
28670
|
this.type = 'where';
|
|
28096
28671
|
}
|
|
28672
|
+
static _create(_field, _op, _value) {
|
|
28673
|
+
return new QueryFieldFilterConstraint(_field, _op, _value);
|
|
28674
|
+
}
|
|
28097
28675
|
_apply(query) {
|
|
28676
|
+
const filter = this._parse(query);
|
|
28677
|
+
validateNewFieldFilter(query._query, filter);
|
|
28678
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
|
|
28679
|
+
}
|
|
28680
|
+
_parse(query) {
|
|
28098
28681
|
const reader = newUserDataReader(query.firestore);
|
|
28099
28682
|
const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
|
|
28100
|
-
return
|
|
28683
|
+
return filter;
|
|
28101
28684
|
}
|
|
28102
28685
|
}
|
|
28103
28686
|
/**
|
|
28104
|
-
* Creates a {@link
|
|
28105
|
-
* specified field and that the value should satisfy the
|
|
28106
|
-
* provided.
|
|
28687
|
+
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents
|
|
28688
|
+
* must contain the specified field and that the value should satisfy the
|
|
28689
|
+
* relation constraint provided.
|
|
28107
28690
|
*
|
|
28108
28691
|
* @param fieldPath - The path to compare
|
|
28109
28692
|
* @param opStr - The operation string (e.g "<", "<=", "==", "<",
|
|
28110
28693
|
* "<=", "!=").
|
|
28111
28694
|
* @param value - The value for comparison
|
|
28112
|
-
* @returns The created {@link
|
|
28695
|
+
* @returns The created {@link QueryFieldFilterConstraint}.
|
|
28113
28696
|
*/
|
|
28114
28697
|
function where(fieldPath, opStr, value) {
|
|
28115
28698
|
const op = opStr;
|
|
28116
28699
|
const field = fieldPathFromArgument('where', fieldPath);
|
|
28117
|
-
return
|
|
28700
|
+
return QueryFieldFilterConstraint._create(field, op, value);
|
|
28701
|
+
}
|
|
28702
|
+
/**
|
|
28703
|
+
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
|
|
28704
|
+
* returned by a Firestore query by performing the logical OR or AND of multiple
|
|
28705
|
+
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
|
|
28706
|
+
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
|
|
28707
|
+
* {@link and} and can then be passed to {@link query} to create a new query
|
|
28708
|
+
* instance that also contains the `QueryCompositeFilterConstraint`.
|
|
28709
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28710
|
+
*/
|
|
28711
|
+
class QueryCompositeFilterConstraint extends AppliableConstraint {
|
|
28712
|
+
/**
|
|
28713
|
+
* @internal
|
|
28714
|
+
*/
|
|
28715
|
+
constructor(
|
|
28716
|
+
/** The type of this query constraint */
|
|
28717
|
+
type, _queryConstraints) {
|
|
28718
|
+
super();
|
|
28719
|
+
this.type = type;
|
|
28720
|
+
this._queryConstraints = _queryConstraints;
|
|
28721
|
+
}
|
|
28722
|
+
static _create(type, _queryConstraints) {
|
|
28723
|
+
return new QueryCompositeFilterConstraint(type, _queryConstraints);
|
|
28724
|
+
}
|
|
28725
|
+
_parse(query) {
|
|
28726
|
+
const parsedFilters = this._queryConstraints
|
|
28727
|
+
.map(queryConstraint => {
|
|
28728
|
+
return queryConstraint._parse(query);
|
|
28729
|
+
})
|
|
28730
|
+
.filter(parsedFilter => parsedFilter.getFilters().length > 0);
|
|
28731
|
+
if (parsedFilters.length === 1) {
|
|
28732
|
+
return parsedFilters[0];
|
|
28733
|
+
}
|
|
28734
|
+
return CompositeFilter.create(parsedFilters, this._getOperator());
|
|
28735
|
+
}
|
|
28736
|
+
_apply(query) {
|
|
28737
|
+
const parsedFilter = this._parse(query);
|
|
28738
|
+
if (parsedFilter.getFilters().length === 0) {
|
|
28739
|
+
// Return the existing query if not adding any more filters (e.g. an empty
|
|
28740
|
+
// composite filter).
|
|
28741
|
+
return query;
|
|
28742
|
+
}
|
|
28743
|
+
validateNewFilter(query._query, parsedFilter);
|
|
28744
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
|
|
28745
|
+
}
|
|
28746
|
+
_getQueryConstraints() {
|
|
28747
|
+
return this._queryConstraints;
|
|
28748
|
+
}
|
|
28749
|
+
_getOperator() {
|
|
28750
|
+
return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
|
|
28751
|
+
}
|
|
28752
|
+
}
|
|
28753
|
+
/**
|
|
28754
|
+
* Creates a new {@link QueryCompositeFilterConstraint} that is a disjunction of
|
|
28755
|
+
* the given filter constraints. A disjunction filter includes a document if it
|
|
28756
|
+
* satisfies any of the given filters.
|
|
28757
|
+
*
|
|
28758
|
+
* @param queryConstraints - Optional. The list of
|
|
28759
|
+
* {@link QueryFilterConstraint}s to perform a disjunction for. These must be
|
|
28760
|
+
* created with calls to {@link where}, {@link or}, or {@link and}.
|
|
28761
|
+
* @returns The newly created {@link QueryCompositeFilterConstraint}.
|
|
28762
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28763
|
+
*/
|
|
28764
|
+
function or(...queryConstraints) {
|
|
28765
|
+
// Only support QueryFilterConstraints
|
|
28766
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
|
|
28767
|
+
return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
|
|
28768
|
+
}
|
|
28769
|
+
/**
|
|
28770
|
+
* Creates a new {@link QueryCompositeFilterConstraint} that is a conjunction of
|
|
28771
|
+
* the given filter constraints. A conjunction filter includes a document if it
|
|
28772
|
+
* satisfies all of the given filters.
|
|
28773
|
+
*
|
|
28774
|
+
* @param queryConstraints - Optional. The list of
|
|
28775
|
+
* {@link QueryFilterConstraint}s to perform a conjunction for. These must be
|
|
28776
|
+
* created with calls to {@link where}, {@link or}, or {@link and}.
|
|
28777
|
+
* @returns The newly created {@link QueryCompositeFilterConstraint}.
|
|
28778
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28779
|
+
*/
|
|
28780
|
+
function and(...queryConstraints) {
|
|
28781
|
+
// Only support QueryFilterConstraints
|
|
28782
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
|
|
28783
|
+
return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
|
|
28118
28784
|
}
|
|
28785
|
+
/**
|
|
28786
|
+
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a
|
|
28787
|
+
* Firestore query. `QueryOrderByConstraint`s are created by invoking
|
|
28788
|
+
* {@link orderBy} and can then be passed to {@link query} to create a new query
|
|
28789
|
+
* instance that also contains this `QueryOrderByConstraint`.
|
|
28790
|
+
*
|
|
28791
|
+
* Note: Documents that do not contain the orderBy field will not be present in
|
|
28792
|
+
* the query result.
|
|
28793
|
+
*/
|
|
28119
28794
|
class QueryOrderByConstraint extends QueryConstraint {
|
|
28795
|
+
/**
|
|
28796
|
+
* @internal
|
|
28797
|
+
*/
|
|
28120
28798
|
constructor(_field, _direction) {
|
|
28121
28799
|
super();
|
|
28122
28800
|
this._field = _field;
|
|
28123
28801
|
this._direction = _direction;
|
|
28802
|
+
/** The type of this query constraint */
|
|
28124
28803
|
this.type = 'orderBy';
|
|
28125
28804
|
}
|
|
28805
|
+
static _create(_field, _direction) {
|
|
28806
|
+
return new QueryOrderByConstraint(_field, _direction);
|
|
28807
|
+
}
|
|
28126
28808
|
_apply(query) {
|
|
28127
28809
|
const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
|
|
28128
28810
|
return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
|
|
28129
28811
|
}
|
|
28130
28812
|
}
|
|
28131
28813
|
/**
|
|
28132
|
-
* Creates a {@link
|
|
28814
|
+
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the
|
|
28133
28815
|
* specified field, optionally in descending order instead of ascending.
|
|
28134
28816
|
*
|
|
28817
|
+
* Note: Documents that do not contain the specified field will not be present
|
|
28818
|
+
* in the query result.
|
|
28819
|
+
*
|
|
28135
28820
|
* @param fieldPath - The field to sort by.
|
|
28136
28821
|
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
|
|
28137
28822
|
* not specified, order will be ascending.
|
|
28138
|
-
* @returns The created {@link
|
|
28823
|
+
* @returns The created {@link QueryOrderByConstraint}.
|
|
28139
28824
|
*/
|
|
28140
28825
|
function orderBy(fieldPath, directionStr = 'asc') {
|
|
28141
28826
|
const direction = directionStr;
|
|
28142
28827
|
const path = fieldPathFromArgument('orderBy', fieldPath);
|
|
28143
|
-
return
|
|
28828
|
+
return QueryOrderByConstraint._create(path, direction);
|
|
28144
28829
|
}
|
|
28830
|
+
/**
|
|
28831
|
+
* A `QueryLimitConstraint` is used to limit the number of documents returned by
|
|
28832
|
+
* a Firestore query.
|
|
28833
|
+
* `QueryLimitConstraint`s are created by invoking {@link limit} or
|
|
28834
|
+
* {@link limitToLast} and can then be passed to {@link query} to create a new
|
|
28835
|
+
* query instance that also contains this `QueryLimitConstraint`.
|
|
28836
|
+
*/
|
|
28145
28837
|
class QueryLimitConstraint extends QueryConstraint {
|
|
28146
|
-
|
|
28838
|
+
/**
|
|
28839
|
+
* @internal
|
|
28840
|
+
*/
|
|
28841
|
+
constructor(
|
|
28842
|
+
/** The type of this query constraint */
|
|
28843
|
+
type, _limit, _limitType) {
|
|
28147
28844
|
super();
|
|
28148
28845
|
this.type = type;
|
|
28149
28846
|
this._limit = _limit;
|
|
28150
28847
|
this._limitType = _limitType;
|
|
28151
28848
|
}
|
|
28849
|
+
static _create(type, _limit, _limitType) {
|
|
28850
|
+
return new QueryLimitConstraint(type, _limit, _limitType);
|
|
28851
|
+
}
|
|
28152
28852
|
_apply(query) {
|
|
28153
28853
|
return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
|
|
28154
28854
|
}
|
|
28155
28855
|
}
|
|
28156
28856
|
/**
|
|
28157
|
-
* Creates a {@link
|
|
28857
|
+
* Creates a {@link QueryLimitConstraint} that only returns the first matching
|
|
28858
|
+
* documents.
|
|
28158
28859
|
*
|
|
28159
28860
|
* @param limit - The maximum number of items to return.
|
|
28160
|
-
* @returns The created {@link
|
|
28861
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28161
28862
|
*/
|
|
28162
28863
|
function limit(limit) {
|
|
28163
28864
|
validatePositiveNumber('limit', limit);
|
|
28164
|
-
return
|
|
28865
|
+
return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
|
|
28165
28866
|
}
|
|
28166
28867
|
/**
|
|
28167
|
-
* Creates a {@link
|
|
28868
|
+
* Creates a {@link QueryLimitConstraint} that only returns the last matching
|
|
28869
|
+
* documents.
|
|
28168
28870
|
*
|
|
28169
28871
|
* You must specify at least one `orderBy` clause for `limitToLast` queries,
|
|
28170
28872
|
* otherwise an exception will be thrown during execution.
|
|
28171
28873
|
*
|
|
28172
28874
|
* @param limit - The maximum number of items to return.
|
|
28173
|
-
* @returns The created {@link
|
|
28875
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28174
28876
|
*/
|
|
28175
28877
|
function limitToLast(limit) {
|
|
28176
28878
|
validatePositiveNumber('limitToLast', limit);
|
|
28177
|
-
return
|
|
28879
|
+
return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
|
|
28178
28880
|
}
|
|
28881
|
+
/**
|
|
28882
|
+
* A `QueryStartAtConstraint` is used to exclude documents from the start of a
|
|
28883
|
+
* result set returned by a Firestore query.
|
|
28884
|
+
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
|
|
28885
|
+
* {@link (startAfter:1)} and can then be passed to {@link query} to create a
|
|
28886
|
+
* new query instance that also contains this `QueryStartAtConstraint`.
|
|
28887
|
+
*/
|
|
28179
28888
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
28180
|
-
|
|
28889
|
+
/**
|
|
28890
|
+
* @internal
|
|
28891
|
+
*/
|
|
28892
|
+
constructor(
|
|
28893
|
+
/** The type of this query constraint */
|
|
28894
|
+
type, _docOrFields, _inclusive) {
|
|
28181
28895
|
super();
|
|
28182
28896
|
this.type = type;
|
|
28183
28897
|
this._docOrFields = _docOrFields;
|
|
28184
28898
|
this._inclusive = _inclusive;
|
|
28185
28899
|
}
|
|
28900
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28901
|
+
return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
|
|
28902
|
+
}
|
|
28186
28903
|
_apply(query) {
|
|
28187
28904
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28188
28905
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
28189
28906
|
}
|
|
28190
28907
|
}
|
|
28191
28908
|
function startAt(...docOrFields) {
|
|
28192
|
-
return
|
|
28909
|
+
return QueryStartAtConstraint._create('startAt', docOrFields,
|
|
28193
28910
|
/*inclusive=*/ true);
|
|
28194
28911
|
}
|
|
28195
28912
|
function startAfter(...docOrFields) {
|
|
28196
|
-
return
|
|
28913
|
+
return QueryStartAtConstraint._create('startAfter', docOrFields,
|
|
28197
28914
|
/*inclusive=*/ false);
|
|
28198
28915
|
}
|
|
28916
|
+
/**
|
|
28917
|
+
* A `QueryEndAtConstraint` is used to exclude documents from the end of a
|
|
28918
|
+
* result set returned by a Firestore query.
|
|
28919
|
+
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
|
|
28920
|
+
* {@link (endBefore:1)} and can then be passed to {@link query} to create a new
|
|
28921
|
+
* query instance that also contains this `QueryEndAtConstraint`.
|
|
28922
|
+
*/
|
|
28199
28923
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
28200
|
-
|
|
28924
|
+
/**
|
|
28925
|
+
* @internal
|
|
28926
|
+
*/
|
|
28927
|
+
constructor(
|
|
28928
|
+
/** The type of this query constraint */
|
|
28929
|
+
type, _docOrFields, _inclusive) {
|
|
28201
28930
|
super();
|
|
28202
28931
|
this.type = type;
|
|
28203
28932
|
this._docOrFields = _docOrFields;
|
|
28204
28933
|
this._inclusive = _inclusive;
|
|
28205
28934
|
}
|
|
28935
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28936
|
+
return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
|
|
28937
|
+
}
|
|
28206
28938
|
_apply(query) {
|
|
28207
28939
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28208
28940
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
28209
28941
|
}
|
|
28210
28942
|
}
|
|
28211
28943
|
function endBefore(...docOrFields) {
|
|
28212
|
-
return
|
|
28944
|
+
return QueryEndAtConstraint._create('endBefore', docOrFields,
|
|
28213
28945
|
/*inclusive=*/ false);
|
|
28214
28946
|
}
|
|
28215
28947
|
function endAt(...docOrFields) {
|
|
28216
|
-
return
|
|
28948
|
+
return QueryEndAtConstraint._create('endAt', docOrFields,
|
|
28949
|
+
/*inclusive=*/ true);
|
|
28217
28950
|
}
|
|
28218
28951
|
/** Helper function to create a bound from a document or fields */
|
|
28219
28952
|
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
@@ -28229,10 +28962,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
|
|
|
28229
28962
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
28230
28963
|
let fieldValue;
|
|
28231
28964
|
if (fieldPath.isKeyField()) {
|
|
28232
|
-
if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28965
|
+
if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28233
28966
|
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
|
|
28234
28967
|
}
|
|
28235
|
-
else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
28968
|
+
else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
28236
28969
|
validateDisjunctiveFilterElements(value, op);
|
|
28237
28970
|
const referenceList = [];
|
|
28238
28971
|
for (const arrayValue of value) {
|
|
@@ -28245,16 +28978,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
|
|
|
28245
28978
|
}
|
|
28246
28979
|
}
|
|
28247
28980
|
else {
|
|
28248
|
-
if (op === "in" /* IN */ ||
|
|
28249
|
-
op === "not-in" /* NOT_IN */ ||
|
|
28250
|
-
op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28981
|
+
if (op === "in" /* Operator.IN */ ||
|
|
28982
|
+
op === "not-in" /* Operator.NOT_IN */ ||
|
|
28983
|
+
op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28251
28984
|
validateDisjunctiveFilterElements(value, op);
|
|
28252
28985
|
}
|
|
28253
28986
|
fieldValue = parseQueryValue(dataReader, methodName, value,
|
|
28254
|
-
/* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
|
|
28987
|
+
/* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
|
|
28255
28988
|
}
|
|
28256
28989
|
const filter = FieldFilter.create(fieldPath, op, fieldValue);
|
|
28257
|
-
validateNewFilter(query, filter);
|
|
28258
28990
|
return filter;
|
|
28259
28991
|
}
|
|
28260
28992
|
function newQueryOrderBy(query, fieldPath, direction) {
|
|
@@ -28424,62 +29156,84 @@ function validateDisjunctiveFilterElements(value, operator) {
|
|
|
28424
29156
|
*/
|
|
28425
29157
|
function conflictingOps(op) {
|
|
28426
29158
|
switch (op) {
|
|
28427
|
-
case "!=" /* NOT_EQUAL */:
|
|
28428
|
-
return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
|
|
28429
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
29159
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
29160
|
+
return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
|
|
29161
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
28430
29162
|
return [
|
|
28431
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28432
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28433
|
-
"not-in" /* NOT_IN */
|
|
29163
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29164
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29165
|
+
"not-in" /* Operator.NOT_IN */
|
|
28434
29166
|
];
|
|
28435
|
-
case "in" /* IN */:
|
|
28436
|
-
return ["array-contains-any" /* ARRAY_CONTAINS_ANY */, "in" /* IN */, "not-in" /* NOT_IN */];
|
|
28437
|
-
case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
|
|
29167
|
+
case "in" /* Operator.IN */:
|
|
29168
|
+
return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
|
|
29169
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
28438
29170
|
return [
|
|
28439
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28440
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28441
|
-
"in" /* IN */,
|
|
28442
|
-
"not-in" /* NOT_IN */
|
|
29171
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29172
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29173
|
+
"in" /* Operator.IN */,
|
|
29174
|
+
"not-in" /* Operator.NOT_IN */
|
|
28443
29175
|
];
|
|
28444
|
-
case "not-in" /* NOT_IN */:
|
|
29176
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
28445
29177
|
return [
|
|
28446
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28447
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28448
|
-
"in" /* IN */,
|
|
28449
|
-
"not-in" /* NOT_IN */,
|
|
28450
|
-
"!=" /* NOT_EQUAL */
|
|
29178
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29179
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29180
|
+
"in" /* Operator.IN */,
|
|
29181
|
+
"not-in" /* Operator.NOT_IN */,
|
|
29182
|
+
"!=" /* Operator.NOT_EQUAL */
|
|
28451
29183
|
];
|
|
28452
29184
|
default:
|
|
28453
29185
|
return [];
|
|
28454
29186
|
}
|
|
28455
29187
|
}
|
|
28456
|
-
function
|
|
28457
|
-
if (
|
|
28458
|
-
const
|
|
28459
|
-
|
|
29188
|
+
function validateNewFieldFilter(query, fieldFilter) {
|
|
29189
|
+
if (fieldFilter.isInequality()) {
|
|
29190
|
+
const existingInequality = getInequalityFilterField(query);
|
|
29191
|
+
const newInequality = fieldFilter.field;
|
|
29192
|
+
if (existingInequality !== null &&
|
|
29193
|
+
!existingInequality.isEqual(newInequality)) {
|
|
28460
29194
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
|
|
28461
29195
|
' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
|
|
28462
|
-
` inequality filters on '${
|
|
28463
|
-
` and '${
|
|
29196
|
+
` inequality filters on '${existingInequality.toString()}'` +
|
|
29197
|
+
` and '${newInequality.toString()}'`);
|
|
28464
29198
|
}
|
|
28465
29199
|
const firstOrderByField = getFirstOrderByField(query);
|
|
28466
29200
|
if (firstOrderByField !== null) {
|
|
28467
|
-
validateOrderByAndInequalityMatch(query,
|
|
29201
|
+
validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
|
|
28468
29202
|
}
|
|
28469
29203
|
}
|
|
28470
|
-
const conflictingOp =
|
|
29204
|
+
const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
|
|
28471
29205
|
if (conflictingOp !== null) {
|
|
28472
29206
|
// Special case when it's a duplicate op to give a slightly clearer error message.
|
|
28473
|
-
if (conflictingOp ===
|
|
29207
|
+
if (conflictingOp === fieldFilter.op) {
|
|
28474
29208
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
|
|
28475
|
-
`'${
|
|
29209
|
+
`'${fieldFilter.op.toString()}' filter.`);
|
|
28476
29210
|
}
|
|
28477
29211
|
else {
|
|
28478
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${
|
|
29212
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
|
|
28479
29213
|
`with '${conflictingOp.toString()}' filters.`);
|
|
28480
29214
|
}
|
|
28481
29215
|
}
|
|
28482
29216
|
}
|
|
29217
|
+
function validateNewFilter(query, filter) {
|
|
29218
|
+
let testQuery = query;
|
|
29219
|
+
const subFilters = filter.getFlattenedFilters();
|
|
29220
|
+
for (const subFilter of subFilters) {
|
|
29221
|
+
validateNewFieldFilter(testQuery, subFilter);
|
|
29222
|
+
testQuery = queryWithAddedFilter(testQuery, subFilter);
|
|
29223
|
+
}
|
|
29224
|
+
}
|
|
29225
|
+
// Checks if any of the provided filter operators are included in the given list of filters and
|
|
29226
|
+
// returns the first one that is, or null if none are.
|
|
29227
|
+
function findOpInsideFilters(filters, operators) {
|
|
29228
|
+
for (const filter of filters) {
|
|
29229
|
+
for (const fieldFilter of filter.getFlattenedFilters()) {
|
|
29230
|
+
if (operators.indexOf(fieldFilter.op) >= 0) {
|
|
29231
|
+
return fieldFilter.op;
|
|
29232
|
+
}
|
|
29233
|
+
}
|
|
29234
|
+
}
|
|
29235
|
+
return null;
|
|
29236
|
+
}
|
|
28483
29237
|
function validateNewOrderBy(query, orderBy) {
|
|
28484
29238
|
if (getFirstOrderByField(query) === null) {
|
|
28485
29239
|
// This is the first order by. It must match any inequality.
|
|
@@ -28497,6 +29251,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28497
29251
|
`as your first argument to orderBy(), but your first orderBy() ` +
|
|
28498
29252
|
`is on field '${orderBy.toString()}' instead.`);
|
|
28499
29253
|
}
|
|
29254
|
+
}
|
|
29255
|
+
function validateQueryFilterConstraint(functionName, queryConstraint) {
|
|
29256
|
+
if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
|
|
29257
|
+
!(queryConstraint instanceof QueryCompositeFilterConstraint)) {
|
|
29258
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
|
|
29259
|
+
}
|
|
29260
|
+
}
|
|
29261
|
+
function validateQueryConstraintArray(queryConstraint) {
|
|
29262
|
+
const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
|
|
29263
|
+
const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
|
|
29264
|
+
if (compositeFilterCount > 1 ||
|
|
29265
|
+
(compositeFilterCount > 0 && fieldFilterCount > 0)) {
|
|
29266
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
|
|
29267
|
+
'more than one filter at the top level. Consider nesting the multiple ' +
|
|
29268
|
+
'filters within an `and(...)` statement. For example: ' +
|
|
29269
|
+
'change `query(query, where(...), or(...))` to ' +
|
|
29270
|
+
'`query(query, and(where(...), or(...)))`.');
|
|
29271
|
+
}
|
|
28500
29272
|
}
|
|
28501
29273
|
|
|
28502
29274
|
/**
|
|
@@ -28524,27 +29296,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28524
29296
|
class AbstractUserDataWriter {
|
|
28525
29297
|
convertValue(value, serverTimestampBehavior = 'none') {
|
|
28526
29298
|
switch (typeOrder(value)) {
|
|
28527
|
-
case 0 /* NullValue */:
|
|
29299
|
+
case 0 /* TypeOrder.NullValue */:
|
|
28528
29300
|
return null;
|
|
28529
|
-
case 1 /* BooleanValue */:
|
|
29301
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
28530
29302
|
return value.booleanValue;
|
|
28531
|
-
case 2 /* NumberValue */:
|
|
29303
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
28532
29304
|
return normalizeNumber(value.integerValue || value.doubleValue);
|
|
28533
|
-
case 3 /* TimestampValue */:
|
|
29305
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
28534
29306
|
return this.convertTimestamp(value.timestampValue);
|
|
28535
|
-
case 4 /* ServerTimestampValue */:
|
|
29307
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
28536
29308
|
return this.convertServerTimestamp(value, serverTimestampBehavior);
|
|
28537
|
-
case 5 /* StringValue */:
|
|
29309
|
+
case 5 /* TypeOrder.StringValue */:
|
|
28538
29310
|
return value.stringValue;
|
|
28539
|
-
case 6 /* BlobValue */:
|
|
29311
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
28540
29312
|
return this.convertBytes(normalizeByteString(value.bytesValue));
|
|
28541
|
-
case 7 /* RefValue */:
|
|
29313
|
+
case 7 /* TypeOrder.RefValue */:
|
|
28542
29314
|
return this.convertReference(value.referenceValue);
|
|
28543
|
-
case 8 /* GeoPointValue */:
|
|
29315
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
28544
29316
|
return this.convertGeoPoint(value.geoPointValue);
|
|
28545
|
-
case 9 /* ArrayValue */:
|
|
29317
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
28546
29318
|
return this.convertArray(value.arrayValue, serverTimestampBehavior);
|
|
28547
|
-
case 10 /* ObjectValue */:
|
|
29319
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
28548
29320
|
return this.convertObject(value.mapValue, serverTimestampBehavior);
|
|
28549
29321
|
default:
|
|
28550
29322
|
throw fail();
|
|
@@ -28886,16 +29658,16 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28886
29658
|
// to lookup the index of a document.
|
|
28887
29659
|
let indexTracker = querySnapshot._snapshot.oldDocs;
|
|
28888
29660
|
return querySnapshot._snapshot.docChanges
|
|
28889
|
-
.filter(change => includeMetadataChanges || change.type !== 3 /* Metadata */)
|
|
29661
|
+
.filter(change => includeMetadataChanges || change.type !== 3 /* ChangeType.Metadata */)
|
|
28890
29662
|
.map(change => {
|
|
28891
29663
|
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);
|
|
28892
29664
|
let oldIndex = -1;
|
|
28893
29665
|
let newIndex = -1;
|
|
28894
|
-
if (change.type !== 0 /* Added */) {
|
|
29666
|
+
if (change.type !== 0 /* ChangeType.Added */) {
|
|
28895
29667
|
oldIndex = indexTracker.indexOf(change.doc.key);
|
|
28896
29668
|
indexTracker = indexTracker.delete(change.doc.key);
|
|
28897
29669
|
}
|
|
28898
|
-
if (change.type !== 1 /* Removed */) {
|
|
29670
|
+
if (change.type !== 1 /* ChangeType.Removed */) {
|
|
28899
29671
|
indexTracker = indexTracker.add(change.doc);
|
|
28900
29672
|
newIndex = indexTracker.indexOf(change.doc.key);
|
|
28901
29673
|
}
|
|
@@ -28910,12 +29682,12 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28910
29682
|
}
|
|
28911
29683
|
function resultChangeType(type) {
|
|
28912
29684
|
switch (type) {
|
|
28913
|
-
case 0 /* Added */:
|
|
29685
|
+
case 0 /* ChangeType.Added */:
|
|
28914
29686
|
return 'added';
|
|
28915
|
-
case 2 /* Modified */:
|
|
28916
|
-
case 3 /* Metadata */:
|
|
29687
|
+
case 2 /* ChangeType.Modified */:
|
|
29688
|
+
case 3 /* ChangeType.Metadata */:
|
|
28917
29689
|
return 'modified';
|
|
28918
|
-
case 1 /* Removed */:
|
|
29690
|
+
case 1 /* ChangeType.Removed */:
|
|
28919
29691
|
return 'removed';
|
|
28920
29692
|
default:
|
|
28921
29693
|
return fail();
|
|
@@ -29045,7 +29817,8 @@ function getDocs(query) {
|
|
|
29045
29817
|
}
|
|
29046
29818
|
/**
|
|
29047
29819
|
* Executes the query and returns the results as a `QuerySnapshot` from cache.
|
|
29048
|
-
* Returns an
|
|
29820
|
+
* Returns an empty result set if no documents matching the query are currently
|
|
29821
|
+
* cached.
|
|
29049
29822
|
*
|
|
29050
29823
|
* @returns A `Promise` that will be resolved with the results of the query.
|
|
29051
29824
|
*/
|
|
@@ -29749,13 +30522,13 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29749
30522
|
const fieldPathString = tryGetString(field, 'fieldPath');
|
|
29750
30523
|
const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
|
|
29751
30524
|
if (field.arrayConfig === 'CONTAINS') {
|
|
29752
|
-
segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
|
|
30525
|
+
segments.push(new IndexSegment(fieldPath, 2 /* IndexKind.CONTAINS */));
|
|
29753
30526
|
}
|
|
29754
30527
|
else if (field.order === 'ASCENDING') {
|
|
29755
|
-
segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
|
|
30528
|
+
segments.push(new IndexSegment(fieldPath, 0 /* IndexKind.ASCENDING */));
|
|
29756
30529
|
}
|
|
29757
30530
|
else if (field.order === 'DESCENDING') {
|
|
29758
|
-
segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
|
|
30531
|
+
segments.push(new IndexSegment(fieldPath, 1 /* IndexKind.DESCENDING */));
|
|
29759
30532
|
}
|
|
29760
30533
|
}
|
|
29761
30534
|
}
|
|
@@ -29765,12 +30538,11 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29765
30538
|
return parsedIndexes;
|
|
29766
30539
|
}
|
|
29767
30540
|
function tryParseJson(json) {
|
|
29768
|
-
var _a;
|
|
29769
30541
|
try {
|
|
29770
30542
|
return JSON.parse(json);
|
|
29771
30543
|
}
|
|
29772
30544
|
catch (e) {
|
|
29773
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (
|
|
30545
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (e === null || e === void 0 ? void 0 : e.message));
|
|
29774
30546
|
}
|
|
29775
30547
|
}
|
|
29776
30548
|
function tryGetString(data, property) {
|
|
@@ -29813,9 +30585,15 @@ exports.FirestoreError = FirestoreError;
|
|
|
29813
30585
|
exports.GeoPoint = GeoPoint;
|
|
29814
30586
|
exports.LoadBundleTask = LoadBundleTask;
|
|
29815
30587
|
exports.Query = Query;
|
|
30588
|
+
exports.QueryCompositeFilterConstraint = QueryCompositeFilterConstraint;
|
|
29816
30589
|
exports.QueryConstraint = QueryConstraint;
|
|
29817
30590
|
exports.QueryDocumentSnapshot = QueryDocumentSnapshot;
|
|
30591
|
+
exports.QueryEndAtConstraint = QueryEndAtConstraint;
|
|
30592
|
+
exports.QueryFieldFilterConstraint = QueryFieldFilterConstraint;
|
|
30593
|
+
exports.QueryLimitConstraint = QueryLimitConstraint;
|
|
30594
|
+
exports.QueryOrderByConstraint = QueryOrderByConstraint;
|
|
29818
30595
|
exports.QuerySnapshot = QuerySnapshot;
|
|
30596
|
+
exports.QueryStartAtConstraint = QueryStartAtConstraint;
|
|
29819
30597
|
exports.SnapshotMetadata = SnapshotMetadata;
|
|
29820
30598
|
exports.Timestamp = Timestamp;
|
|
29821
30599
|
exports.Transaction = Transaction;
|
|
@@ -29832,6 +30610,7 @@ exports._logWarn = logWarn;
|
|
|
29832
30610
|
exports._validateIsNotUsedTogether = validateIsNotUsedTogether;
|
|
29833
30611
|
exports.addDoc = addDoc;
|
|
29834
30612
|
exports.aggregateQuerySnapshotEqual = aggregateQuerySnapshotEqual;
|
|
30613
|
+
exports.and = and;
|
|
29835
30614
|
exports.arrayRemove = arrayRemove;
|
|
29836
30615
|
exports.arrayUnion = arrayUnion;
|
|
29837
30616
|
exports.clearIndexedDbPersistence = clearIndexedDbPersistence;
|
|
@@ -29866,6 +30645,7 @@ exports.loadBundle = loadBundle;
|
|
|
29866
30645
|
exports.namedQuery = namedQuery;
|
|
29867
30646
|
exports.onSnapshot = onSnapshot;
|
|
29868
30647
|
exports.onSnapshotsInSync = onSnapshotsInSync;
|
|
30648
|
+
exports.or = or;
|
|
29869
30649
|
exports.orderBy = orderBy;
|
|
29870
30650
|
exports.query = query;
|
|
29871
30651
|
exports.queryEqual = queryEqual;
|