@firebase/firestore 3.7.3 → 3.8.0
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.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 +273 -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 +138 -62
- package/dist/index.esm2017.js +4295 -3742
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4981 -4410
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1657 -889
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1650 -890
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4520 -3967
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +328 -68
- 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.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 +273 -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 +2091 -1793
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +2239 -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 +2212 -1802
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +2205 -1803
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +2077 -1778
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +328 -66
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +322 -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.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 +273 -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 +368 -208
- 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.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 +273 -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 +240 -68
- package/package.json +13 -11
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";
|
|
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";
|
|
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,344 @@ 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
|
-
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
5500
|
-
// startAt.
|
|
5501
|
-
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5502
|
-
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
5503
|
-
? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
|
|
5504
|
-
: targetGetDescendingBound(target, segment.fieldPath, target.startAt);
|
|
5505
|
-
values.push(segmentBound.value);
|
|
5506
|
-
inclusive && (inclusive = segmentBound.inclusive);
|
|
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));
|
|
5507
5620
|
}
|
|
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);
|
|
5621
|
+
else {
|
|
5622
|
+
// filter instanceof CompositeFilter
|
|
5623
|
+
const canonicalIdsString = filter.filters
|
|
5624
|
+
.map(filter => canonifyFilter(filter))
|
|
5625
|
+
.join(',');
|
|
5626
|
+
return `${filter.op}(${canonicalIdsString})`;
|
|
5526
5627
|
}
|
|
5527
|
-
return new Bound(values, inclusive);
|
|
5528
5628
|
}
|
|
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
|
-
}
|
|
5629
|
+
function filterEquals(f1, f2) {
|
|
5630
|
+
if (f1 instanceof FieldFilter) {
|
|
5631
|
+
return fieldFilterEquals(f1, f2);
|
|
5579
5632
|
}
|
|
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
|
-
}
|
|
5633
|
+
else if (f1 instanceof CompositeFilter) {
|
|
5634
|
+
return compositeFilterEquals(f1, f2);
|
|
5618
5635
|
}
|
|
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
|
-
}
|
|
5636
|
+
else {
|
|
5637
|
+
fail();
|
|
5633
5638
|
}
|
|
5634
|
-
return { value, inclusive };
|
|
5635
5639
|
}
|
|
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
|
-
}
|
|
5640
|
+
function fieldFilterEquals(f1, f2) {
|
|
5641
|
+
return (f2 instanceof FieldFilter &&
|
|
5642
|
+
f1.op === f2.op &&
|
|
5643
|
+
f1.field.isEqual(f2.field) &&
|
|
5644
|
+
valueEquals(f1.value, f2.value));
|
|
5645
|
+
}
|
|
5646
|
+
function compositeFilterEquals(f1, f2) {
|
|
5647
|
+
if (f2 instanceof CompositeFilter &&
|
|
5648
|
+
f1.op === f2.op &&
|
|
5649
|
+
f1.filters.length === f2.filters.length) {
|
|
5650
|
+
const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
|
|
5651
|
+
return subFiltersMatch;
|
|
5666
5652
|
}
|
|
5667
|
-
return
|
|
5653
|
+
return false;
|
|
5668
5654
|
}
|
|
5669
|
-
|
|
5655
|
+
/**
|
|
5656
|
+
* Returns a new composite filter that contains all filter from
|
|
5657
|
+
* `compositeFilter` plus all the given filters in `otherFilters`.
|
|
5658
|
+
*/
|
|
5659
|
+
function compositeFilterWithAddedFilters(compositeFilter, otherFilters) {
|
|
5660
|
+
const mergedFilters = compositeFilter.filters.concat(otherFilters);
|
|
5661
|
+
return CompositeFilter.create(mergedFilters, compositeFilter.op);
|
|
5670
5662
|
}
|
|
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)));
|
|
5663
|
+
/** Returns a debug description for `filter`. */
|
|
5664
|
+
function stringifyFilter(filter) {
|
|
5665
|
+
if (filter instanceof FieldFilter) {
|
|
5666
|
+
return stringifyFieldFilter(filter);
|
|
5722
5667
|
}
|
|
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
|
-
}
|
|
5668
|
+
else if (filter instanceof CompositeFilter) {
|
|
5669
|
+
return stringifyCompositeFilter(filter);
|
|
5740
5670
|
}
|
|
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);
|
|
5671
|
+
else {
|
|
5672
|
+
return 'Filter';
|
|
5750
5673
|
}
|
|
5751
5674
|
}
|
|
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));
|
|
5675
|
+
function stringifyCompositeFilter(filter) {
|
|
5676
|
+
return (filter.op.toString() +
|
|
5677
|
+
` {` +
|
|
5678
|
+
filter.getFilters().map(stringifyFilter).join(' ,') +
|
|
5679
|
+
'}');
|
|
5764
5680
|
}
|
|
5765
|
-
|
|
5766
|
-
function stringifyFilter(filter) {
|
|
5681
|
+
function stringifyFieldFilter(filter) {
|
|
5767
5682
|
return `${filter.field.canonicalString()} ${filter.op} ${canonicalId(filter.value)}`;
|
|
5768
5683
|
}
|
|
5769
5684
|
/** Filter that matches on key fields (i.e. '__name__'). */
|
|
@@ -5780,8 +5695,8 @@ class KeyFieldFilter extends FieldFilter {
|
|
|
5780
5695
|
/** Filter that matches on key fields within an array. */
|
|
5781
5696
|
class KeyFieldInFilter extends FieldFilter {
|
|
5782
5697
|
constructor(field, value) {
|
|
5783
|
-
super(field, "in" /* IN */, value);
|
|
5784
|
-
this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
|
|
5698
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5699
|
+
this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
|
|
5785
5700
|
}
|
|
5786
5701
|
matches(doc) {
|
|
5787
5702
|
return this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5790,8 +5705,8 @@ class KeyFieldInFilter extends FieldFilter {
|
|
|
5790
5705
|
/** Filter that matches on key fields not present within an array. */
|
|
5791
5706
|
class KeyFieldNotInFilter extends FieldFilter {
|
|
5792
5707
|
constructor(field, value) {
|
|
5793
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5794
|
-
this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
|
|
5708
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5709
|
+
this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
|
|
5795
5710
|
}
|
|
5796
5711
|
matches(doc) {
|
|
5797
5712
|
return !this.keys.some(key => key.isEqual(doc.key));
|
|
@@ -5806,7 +5721,7 @@ function extractDocumentKeysFromArrayValue(op, value) {
|
|
|
5806
5721
|
/** A Filter that implements the array-contains operator. */
|
|
5807
5722
|
class ArrayContainsFilter extends FieldFilter {
|
|
5808
5723
|
constructor(field, value) {
|
|
5809
|
-
super(field, "array-contains" /* ARRAY_CONTAINS */, value);
|
|
5724
|
+
super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
|
|
5810
5725
|
}
|
|
5811
5726
|
matches(doc) {
|
|
5812
5727
|
const other = doc.data.field(this.field);
|
|
@@ -5816,7 +5731,7 @@ class ArrayContainsFilter extends FieldFilter {
|
|
|
5816
5731
|
/** A Filter that implements the IN operator. */
|
|
5817
5732
|
class InFilter extends FieldFilter {
|
|
5818
5733
|
constructor(field, value) {
|
|
5819
|
-
super(field, "in" /* IN */, value);
|
|
5734
|
+
super(field, "in" /* Operator.IN */, value);
|
|
5820
5735
|
}
|
|
5821
5736
|
matches(doc) {
|
|
5822
5737
|
const other = doc.data.field(this.field);
|
|
@@ -5826,7 +5741,7 @@ class InFilter extends FieldFilter {
|
|
|
5826
5741
|
/** A Filter that implements the not-in operator. */
|
|
5827
5742
|
class NotInFilter extends FieldFilter {
|
|
5828
5743
|
constructor(field, value) {
|
|
5829
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
5744
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
5830
5745
|
}
|
|
5831
5746
|
matches(doc) {
|
|
5832
5747
|
if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
|
|
@@ -5839,7 +5754,7 @@ class NotInFilter extends FieldFilter {
|
|
|
5839
5754
|
/** A Filter that implements the array-contains-any operator. */
|
|
5840
5755
|
class ArrayContainsAnyFilter extends FieldFilter {
|
|
5841
5756
|
constructor(field, value) {
|
|
5842
|
-
super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
|
|
5757
|
+
super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
|
|
5843
5758
|
}
|
|
5844
5759
|
matches(doc) {
|
|
5845
5760
|
const other = doc.data.field(this.field);
|
|
@@ -5848,32 +5763,29 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
5848
5763
|
}
|
|
5849
5764
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
5850
5765
|
}
|
|
5851
|
-
}
|
|
5852
|
-
|
|
5853
|
-
|
|
5766
|
+
}
|
|
5767
|
+
|
|
5768
|
+
/**
|
|
5769
|
+
* @license
|
|
5770
|
+
* Copyright 2022 Google LLC
|
|
5854
5771
|
*
|
|
5855
|
-
*
|
|
5856
|
-
*
|
|
5857
|
-
*
|
|
5772
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5773
|
+
* you may not use this file except in compliance with the License.
|
|
5774
|
+
* You may obtain a copy of the License at
|
|
5858
5775
|
*
|
|
5859
|
-
*
|
|
5860
|
-
* of values for the (potentially implicit) order by clauses of a query.
|
|
5776
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5861
5777
|
*
|
|
5862
|
-
*
|
|
5863
|
-
*
|
|
5864
|
-
*
|
|
5778
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5779
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5780
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5781
|
+
* See the License for the specific language governing permissions and
|
|
5782
|
+
* limitations under the License.
|
|
5865
5783
|
*/
|
|
5866
|
-
class Bound {
|
|
5867
|
-
constructor(position, inclusive) {
|
|
5868
|
-
this.position = position;
|
|
5869
|
-
this.inclusive = inclusive;
|
|
5870
|
-
}
|
|
5871
|
-
}
|
|
5872
5784
|
/**
|
|
5873
5785
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
5874
5786
|
*/
|
|
5875
5787
|
class OrderBy {
|
|
5876
|
-
constructor(field, dir = "asc" /* ASCENDING */) {
|
|
5788
|
+
constructor(field, dir = "asc" /* Direction.ASCENDING */) {
|
|
5877
5789
|
this.field = field;
|
|
5878
5790
|
this.dir = dir;
|
|
5879
5791
|
}
|
|
@@ -5887,63 +5799,375 @@ function stringifyOrderBy(orderBy) {
|
|
|
5887
5799
|
}
|
|
5888
5800
|
function orderByEquals(left, right) {
|
|
5889
5801
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
5802
|
+
}
|
|
5803
|
+
|
|
5804
|
+
/**
|
|
5805
|
+
* @license
|
|
5806
|
+
* Copyright 2019 Google LLC
|
|
5807
|
+
*
|
|
5808
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5809
|
+
* you may not use this file except in compliance with the License.
|
|
5810
|
+
* You may obtain a copy of the License at
|
|
5811
|
+
*
|
|
5812
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
5813
|
+
*
|
|
5814
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
5815
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
5816
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
5817
|
+
* See the License for the specific language governing permissions and
|
|
5818
|
+
* limitations under the License.
|
|
5819
|
+
*/
|
|
5820
|
+
// Visible for testing
|
|
5821
|
+
class TargetImpl {
|
|
5822
|
+
constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5823
|
+
this.path = path;
|
|
5824
|
+
this.collectionGroup = collectionGroup;
|
|
5825
|
+
this.orderBy = orderBy;
|
|
5826
|
+
this.filters = filters;
|
|
5827
|
+
this.limit = limit;
|
|
5828
|
+
this.startAt = startAt;
|
|
5829
|
+
this.endAt = endAt;
|
|
5830
|
+
this.memoizedCanonicalId = null;
|
|
5831
|
+
}
|
|
5890
5832
|
}
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5833
|
+
/**
|
|
5834
|
+
* Initializes a Target with a path and optional additional query constraints.
|
|
5835
|
+
* Path must currently be empty if this is a collection group query.
|
|
5836
|
+
*
|
|
5837
|
+
* NOTE: you should always construct `Target` from `Query.toTarget` instead of
|
|
5838
|
+
* using this factory method, because `Query` provides an implicit `orderBy`
|
|
5839
|
+
* property.
|
|
5840
|
+
*/
|
|
5841
|
+
function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
5842
|
+
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
5843
|
+
}
|
|
5844
|
+
function canonifyTarget(target) {
|
|
5845
|
+
const targetImpl = debugCast(target);
|
|
5846
|
+
if (targetImpl.memoizedCanonicalId === null) {
|
|
5847
|
+
let str = targetImpl.path.canonicalString();
|
|
5848
|
+
if (targetImpl.collectionGroup !== null) {
|
|
5849
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
5898
5850
|
}
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5851
|
+
str += '|f:';
|
|
5852
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
5853
|
+
str += '|ob:';
|
|
5854
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
5855
|
+
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
5856
|
+
str += '|l:';
|
|
5857
|
+
str += targetImpl.limit;
|
|
5902
5858
|
}
|
|
5903
|
-
if (
|
|
5904
|
-
|
|
5859
|
+
if (targetImpl.startAt) {
|
|
5860
|
+
str += '|lb:';
|
|
5861
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
5862
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5905
5863
|
}
|
|
5906
|
-
if (
|
|
5907
|
-
|
|
5864
|
+
if (targetImpl.endAt) {
|
|
5865
|
+
str += '|ub:';
|
|
5866
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
5867
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5908
5868
|
}
|
|
5869
|
+
targetImpl.memoizedCanonicalId = str;
|
|
5909
5870
|
}
|
|
5910
|
-
return
|
|
5871
|
+
return targetImpl.memoizedCanonicalId;
|
|
5872
|
+
}
|
|
5873
|
+
function stringifyTarget(target) {
|
|
5874
|
+
let str = target.path.canonicalString();
|
|
5875
|
+
if (target.collectionGroup !== null) {
|
|
5876
|
+
str += ' collectionGroup=' + target.collectionGroup;
|
|
5877
|
+
}
|
|
5878
|
+
if (target.filters.length > 0) {
|
|
5879
|
+
str += `, filters: [${target.filters
|
|
5880
|
+
.map(f => stringifyFilter(f))
|
|
5881
|
+
.join(', ')}]`;
|
|
5882
|
+
}
|
|
5883
|
+
if (!isNullOrUndefined(target.limit)) {
|
|
5884
|
+
str += ', limit: ' + target.limit;
|
|
5885
|
+
}
|
|
5886
|
+
if (target.orderBy.length > 0) {
|
|
5887
|
+
str += `, orderBy: [${target.orderBy
|
|
5888
|
+
.map(o => stringifyOrderBy(o))
|
|
5889
|
+
.join(', ')}]`;
|
|
5890
|
+
}
|
|
5891
|
+
if (target.startAt) {
|
|
5892
|
+
str += ', startAt: ';
|
|
5893
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
5894
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
5895
|
+
}
|
|
5896
|
+
if (target.endAt) {
|
|
5897
|
+
str += ', endAt: ';
|
|
5898
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
5899
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
5900
|
+
}
|
|
5901
|
+
return `Target(${str})`;
|
|
5902
|
+
}
|
|
5903
|
+
function targetEquals(left, right) {
|
|
5904
|
+
if (left.limit !== right.limit) {
|
|
5905
|
+
return false;
|
|
5906
|
+
}
|
|
5907
|
+
if (left.orderBy.length !== right.orderBy.length) {
|
|
5908
|
+
return false;
|
|
5909
|
+
}
|
|
5910
|
+
for (let i = 0; i < left.orderBy.length; i++) {
|
|
5911
|
+
if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
|
|
5912
|
+
return false;
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
if (left.filters.length !== right.filters.length) {
|
|
5916
|
+
return false;
|
|
5917
|
+
}
|
|
5918
|
+
for (let i = 0; i < left.filters.length; i++) {
|
|
5919
|
+
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
5920
|
+
return false;
|
|
5921
|
+
}
|
|
5922
|
+
}
|
|
5923
|
+
if (left.collectionGroup !== right.collectionGroup) {
|
|
5924
|
+
return false;
|
|
5925
|
+
}
|
|
5926
|
+
if (!left.path.isEqual(right.path)) {
|
|
5927
|
+
return false;
|
|
5928
|
+
}
|
|
5929
|
+
if (!boundEquals(left.startAt, right.startAt)) {
|
|
5930
|
+
return false;
|
|
5931
|
+
}
|
|
5932
|
+
return boundEquals(left.endAt, right.endAt);
|
|
5933
|
+
}
|
|
5934
|
+
function targetIsDocumentTarget(target) {
|
|
5935
|
+
return (DocumentKey.isDocumentKey(target.path) &&
|
|
5936
|
+
target.collectionGroup === null &&
|
|
5937
|
+
target.filters.length === 0);
|
|
5938
|
+
}
|
|
5939
|
+
/** Returns the field filters that target the given field path. */
|
|
5940
|
+
function targetGetFieldFiltersForPath(target, path) {
|
|
5941
|
+
return target.filters.filter(f => f instanceof FieldFilter && f.field.isEqual(path));
|
|
5911
5942
|
}
|
|
5912
5943
|
/**
|
|
5913
|
-
* Returns
|
|
5914
|
-
*
|
|
5944
|
+
* Returns the values that are used in ARRAY_CONTAINS or ARRAY_CONTAINS_ANY
|
|
5945
|
+
* filters. Returns `null` if there are no such filters.
|
|
5915
5946
|
*/
|
|
5916
|
-
function
|
|
5917
|
-
const
|
|
5918
|
-
|
|
5947
|
+
function targetGetArrayValues(target, fieldIndex) {
|
|
5948
|
+
const segment = fieldIndexGetArraySegment(fieldIndex);
|
|
5949
|
+
if (segment === undefined) {
|
|
5950
|
+
return null;
|
|
5951
|
+
}
|
|
5952
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5953
|
+
switch (fieldFilter.op) {
|
|
5954
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
5955
|
+
return fieldFilter.value.arrayValue.values || [];
|
|
5956
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
5957
|
+
return [fieldFilter.value];
|
|
5958
|
+
// Remaining filters are not array filters.
|
|
5959
|
+
}
|
|
5960
|
+
}
|
|
5961
|
+
return null;
|
|
5919
5962
|
}
|
|
5920
5963
|
/**
|
|
5921
|
-
* Returns
|
|
5922
|
-
*
|
|
5964
|
+
* Returns the list of values that are used in != or NOT_IN filters. Returns
|
|
5965
|
+
* `null` if there are no such filters.
|
|
5923
5966
|
*/
|
|
5924
|
-
function
|
|
5925
|
-
const
|
|
5926
|
-
|
|
5967
|
+
function targetGetNotInValues(target, fieldIndex) {
|
|
5968
|
+
const values = new Map();
|
|
5969
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
5970
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, segment.fieldPath)) {
|
|
5971
|
+
switch (fieldFilter.op) {
|
|
5972
|
+
case "==" /* Operator.EQUAL */:
|
|
5973
|
+
case "in" /* Operator.IN */:
|
|
5974
|
+
// Encode equality prefix, which is encoded in the index value before
|
|
5975
|
+
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
|
|
5976
|
+
// `value != 'ab'`).
|
|
5977
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5978
|
+
break;
|
|
5979
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
5980
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
5981
|
+
// NotIn/NotEqual is always a suffix. There cannot be any remaining
|
|
5982
|
+
// segments and hence we can return early here.
|
|
5983
|
+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
|
|
5984
|
+
return Array.from(values.values());
|
|
5985
|
+
// Remaining filters cannot be used as notIn bounds.
|
|
5986
|
+
}
|
|
5987
|
+
}
|
|
5988
|
+
}
|
|
5989
|
+
return null;
|
|
5927
5990
|
}
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5991
|
+
/**
|
|
5992
|
+
* Returns a lower bound of field values that can be used as a starting point to
|
|
5993
|
+
* scan the index defined by `fieldIndex`. Returns `MIN_VALUE` if no lower bound
|
|
5994
|
+
* exists.
|
|
5995
|
+
*/
|
|
5996
|
+
function targetGetLowerBound(target, fieldIndex) {
|
|
5997
|
+
const values = [];
|
|
5998
|
+
let inclusive = true;
|
|
5999
|
+
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
6000
|
+
// startAt.
|
|
6001
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
6002
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
6003
|
+
? targetGetAscendingBound(target, segment.fieldPath, target.startAt)
|
|
6004
|
+
: targetGetDescendingBound(target, segment.fieldPath, target.startAt);
|
|
6005
|
+
values.push(segmentBound.value);
|
|
6006
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
5931
6007
|
}
|
|
5932
|
-
|
|
5933
|
-
|
|
6008
|
+
return new Bound(values, inclusive);
|
|
6009
|
+
}
|
|
6010
|
+
/**
|
|
6011
|
+
* Returns an upper bound of field values that can be used as an ending point
|
|
6012
|
+
* when scanning the index defined by `fieldIndex`. Returns `MAX_VALUE` if no
|
|
6013
|
+
* upper bound exists.
|
|
6014
|
+
*/
|
|
6015
|
+
function targetGetUpperBound(target, fieldIndex) {
|
|
6016
|
+
const values = [];
|
|
6017
|
+
let inclusive = true;
|
|
6018
|
+
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
6019
|
+
// endAt.
|
|
6020
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
6021
|
+
const segmentBound = segment.kind === 0 /* IndexKind.ASCENDING */
|
|
6022
|
+
? targetGetDescendingBound(target, segment.fieldPath, target.endAt)
|
|
6023
|
+
: targetGetAscendingBound(target, segment.fieldPath, target.endAt);
|
|
6024
|
+
values.push(segmentBound.value);
|
|
6025
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
6026
|
+
}
|
|
6027
|
+
return new Bound(values, inclusive);
|
|
6028
|
+
}
|
|
6029
|
+
/**
|
|
6030
|
+
* Returns the value to use as the lower bound for ascending index segment at
|
|
6031
|
+
* the provided `fieldPath` (or the upper bound for an descending segment).
|
|
6032
|
+
*/
|
|
6033
|
+
function targetGetAscendingBound(target, fieldPath, bound) {
|
|
6034
|
+
let value = MIN_VALUE;
|
|
6035
|
+
let inclusive = true;
|
|
6036
|
+
// Process all filters to find a value for the current field segment
|
|
6037
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6038
|
+
let filterValue = MIN_VALUE;
|
|
6039
|
+
let filterInclusive = true;
|
|
6040
|
+
switch (fieldFilter.op) {
|
|
6041
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6042
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6043
|
+
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
6044
|
+
break;
|
|
6045
|
+
case "==" /* Operator.EQUAL */:
|
|
6046
|
+
case "in" /* Operator.IN */:
|
|
6047
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6048
|
+
filterValue = fieldFilter.value;
|
|
6049
|
+
break;
|
|
6050
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6051
|
+
filterValue = fieldFilter.value;
|
|
6052
|
+
filterInclusive = false;
|
|
6053
|
+
break;
|
|
6054
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6055
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6056
|
+
filterValue = MIN_VALUE;
|
|
6057
|
+
break;
|
|
6058
|
+
// Remaining filters cannot be used as lower bounds.
|
|
6059
|
+
}
|
|
6060
|
+
if (lowerBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) < 0) {
|
|
6061
|
+
value = filterValue;
|
|
6062
|
+
inclusive = filterInclusive;
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
// If there is an additional bound, compare the values against the existing
|
|
6066
|
+
// range to see if we can narrow the scope.
|
|
6067
|
+
if (bound !== null) {
|
|
6068
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6069
|
+
const orderBy = target.orderBy[i];
|
|
6070
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6071
|
+
const cursorValue = bound.position[i];
|
|
6072
|
+
if (lowerBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) < 0) {
|
|
6073
|
+
value = cursorValue;
|
|
6074
|
+
inclusive = bound.inclusive;
|
|
6075
|
+
}
|
|
6076
|
+
break;
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
return { value, inclusive };
|
|
6081
|
+
}
|
|
6082
|
+
/**
|
|
6083
|
+
* Returns the value to use as the upper bound for ascending index segment at
|
|
6084
|
+
* the provided `fieldPath` (or the lower bound for a descending segment).
|
|
6085
|
+
*/
|
|
6086
|
+
function targetGetDescendingBound(target, fieldPath, bound) {
|
|
6087
|
+
let value = MAX_VALUE;
|
|
6088
|
+
let inclusive = true;
|
|
6089
|
+
// Process all filters to find a value for the current field segment
|
|
6090
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
6091
|
+
let filterValue = MAX_VALUE;
|
|
6092
|
+
let filterInclusive = true;
|
|
6093
|
+
switch (fieldFilter.op) {
|
|
6094
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
6095
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
6096
|
+
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
6097
|
+
filterInclusive = false;
|
|
6098
|
+
break;
|
|
6099
|
+
case "==" /* Operator.EQUAL */:
|
|
6100
|
+
case "in" /* Operator.IN */:
|
|
6101
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
6102
|
+
filterValue = fieldFilter.value;
|
|
6103
|
+
break;
|
|
6104
|
+
case "<" /* Operator.LESS_THAN */:
|
|
6105
|
+
filterValue = fieldFilter.value;
|
|
6106
|
+
filterInclusive = false;
|
|
6107
|
+
break;
|
|
6108
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
6109
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
6110
|
+
filterValue = MAX_VALUE;
|
|
6111
|
+
break;
|
|
6112
|
+
// Remaining filters cannot be used as upper bounds.
|
|
6113
|
+
}
|
|
6114
|
+
if (upperBoundCompare({ value, inclusive }, { value: filterValue, inclusive: filterInclusive }) > 0) {
|
|
6115
|
+
value = filterValue;
|
|
6116
|
+
inclusive = filterInclusive;
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
// If there is an additional bound, compare the values against the existing
|
|
6120
|
+
// range to see if we can narrow the scope.
|
|
6121
|
+
if (bound !== null) {
|
|
6122
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
6123
|
+
const orderBy = target.orderBy[i];
|
|
6124
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
6125
|
+
const cursorValue = bound.position[i];
|
|
6126
|
+
if (upperBoundCompare({ value, inclusive }, { value: cursorValue, inclusive: bound.inclusive }) > 0) {
|
|
6127
|
+
value = cursorValue;
|
|
6128
|
+
inclusive = bound.inclusive;
|
|
6129
|
+
}
|
|
6130
|
+
break;
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
5934
6133
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
6134
|
+
return { value, inclusive };
|
|
6135
|
+
}
|
|
6136
|
+
/** Returns the number of segments of a perfect index for this target. */
|
|
6137
|
+
function targetGetSegmentCount(target) {
|
|
6138
|
+
let fields = new SortedSet(FieldPath$1.comparator);
|
|
6139
|
+
let hasArraySegment = false;
|
|
6140
|
+
for (const filter of target.filters) {
|
|
6141
|
+
for (const subFilter of filter.getFlattenedFilters()) {
|
|
6142
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6143
|
+
// count it.
|
|
6144
|
+
if (subFilter.field.isKeyField()) {
|
|
6145
|
+
continue;
|
|
6146
|
+
}
|
|
6147
|
+
// ARRAY_CONTAINS or ARRAY_CONTAINS_ANY filters must be counted separately.
|
|
6148
|
+
// For instance, it is possible to have an index for "a ARRAY a ASC". Even
|
|
6149
|
+
// though these are on the same field, they should be counted as two
|
|
6150
|
+
// separate segments in an index.
|
|
6151
|
+
if (subFilter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
6152
|
+
subFilter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
6153
|
+
hasArraySegment = true;
|
|
6154
|
+
}
|
|
6155
|
+
else {
|
|
6156
|
+
fields = fields.add(subFilter.field);
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
5938
6159
|
}
|
|
5939
|
-
for (
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
if (!
|
|
5943
|
-
|
|
6160
|
+
for (const orderBy of target.orderBy) {
|
|
6161
|
+
// __name__ is not an explicit segment of any index, so we don't need to
|
|
6162
|
+
// count it.
|
|
6163
|
+
if (!orderBy.field.isKeyField()) {
|
|
6164
|
+
fields = fields.add(orderBy.field);
|
|
5944
6165
|
}
|
|
5945
6166
|
}
|
|
5946
|
-
return
|
|
6167
|
+
return fields.size + (hasArraySegment ? 1 : 0);
|
|
6168
|
+
}
|
|
6169
|
+
function targetHasLimit(target) {
|
|
6170
|
+
return target.limit !== null;
|
|
5947
6171
|
}
|
|
5948
6172
|
|
|
5949
6173
|
/**
|
|
@@ -5974,7 +6198,7 @@ class QueryImpl {
|
|
|
5974
6198
|
* Initializes a Query with a path and optional additional query constraints.
|
|
5975
6199
|
* Path must currently be empty if this is a collection group query.
|
|
5976
6200
|
*/
|
|
5977
|
-
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
|
|
6201
|
+
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
|
|
5978
6202
|
this.path = path;
|
|
5979
6203
|
this.collectionGroup = collectionGroup;
|
|
5980
6204
|
this.explicitOrderBy = explicitOrderBy;
|
|
@@ -6028,20 +6252,9 @@ function getFirstOrderByField(query) {
|
|
|
6028
6252
|
}
|
|
6029
6253
|
function getInequalityFilterField(query) {
|
|
6030
6254
|
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;
|
|
6255
|
+
const result = filter.getFirstInequalityField();
|
|
6256
|
+
if (result !== null) {
|
|
6257
|
+
return result;
|
|
6045
6258
|
}
|
|
6046
6259
|
}
|
|
6047
6260
|
return null;
|
|
@@ -6087,7 +6300,7 @@ function queryOrderBy(query) {
|
|
|
6087
6300
|
if (!inequalityField.isKeyField()) {
|
|
6088
6301
|
queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
|
|
6089
6302
|
}
|
|
6090
|
-
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
|
|
6303
|
+
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
|
|
6091
6304
|
}
|
|
6092
6305
|
else {
|
|
6093
6306
|
let foundKeyOrdering = false;
|
|
@@ -6103,7 +6316,7 @@ function queryOrderBy(query) {
|
|
|
6103
6316
|
const lastDirection = queryImpl.explicitOrderBy.length > 0
|
|
6104
6317
|
? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
|
|
6105
6318
|
.dir
|
|
6106
|
-
: "asc" /* ASCENDING */;
|
|
6319
|
+
: "asc" /* Direction.ASCENDING */;
|
|
6107
6320
|
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
|
|
6108
6321
|
}
|
|
6109
6322
|
}
|
|
@@ -6116,16 +6329,16 @@ function queryOrderBy(query) {
|
|
|
6116
6329
|
function queryToTarget(query) {
|
|
6117
6330
|
const queryImpl = debugCast(query);
|
|
6118
6331
|
if (!queryImpl.memoizedTarget) {
|
|
6119
|
-
if (queryImpl.limitType === "F" /* First */) {
|
|
6332
|
+
if (queryImpl.limitType === "F" /* LimitType.First */) {
|
|
6120
6333
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
|
|
6121
6334
|
}
|
|
6122
6335
|
else {
|
|
6123
6336
|
// Flip the orderBy directions since we want the last results
|
|
6124
6337
|
const orderBys = [];
|
|
6125
6338
|
for (const orderBy of queryOrderBy(queryImpl)) {
|
|
6126
|
-
const dir = orderBy.dir === "desc" /* DESCENDING */
|
|
6127
|
-
? "asc" /* ASCENDING */
|
|
6128
|
-
: "desc" /* DESCENDING */;
|
|
6339
|
+
const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
|
|
6340
|
+
? "asc" /* Direction.ASCENDING */
|
|
6341
|
+
: "desc" /* Direction.DESCENDING */;
|
|
6129
6342
|
orderBys.push(new OrderBy(orderBy.field, dir));
|
|
6130
6343
|
}
|
|
6131
6344
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
@@ -6142,6 +6355,8 @@ function queryToTarget(query) {
|
|
|
6142
6355
|
return queryImpl.memoizedTarget;
|
|
6143
6356
|
}
|
|
6144
6357
|
function queryWithAddedFilter(query, filter) {
|
|
6358
|
+
filter.getFirstInequalityField();
|
|
6359
|
+
getInequalityFilterField(query);
|
|
6145
6360
|
const newFilters = query.filters.concat([filter]);
|
|
6146
6361
|
return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
|
|
6147
6362
|
}
|
|
@@ -6202,7 +6417,13 @@ function queryMatchesPathAndCollectionGroup(query, doc) {
|
|
|
6202
6417
|
* in the results.
|
|
6203
6418
|
*/
|
|
6204
6419
|
function queryMatchesOrderBy(query, doc) {
|
|
6205
|
-
|
|
6420
|
+
// We must use `queryOrderBy()` to get the list of all orderBys (both implicit and explicit).
|
|
6421
|
+
// Note that for OR queries, orderBy applies to all disjunction terms and implicit orderBys must
|
|
6422
|
+
// be taken into account. For example, the query "a > 1 || b==1" has an implicit "orderBy a" due
|
|
6423
|
+
// to the inequality, and is evaluated as "a > 1 orderBy a || b==1 orderBy a".
|
|
6424
|
+
// A document with content of {b:1} matches the filters, but does not match the orderBy because
|
|
6425
|
+
// it's missing the field 'a'.
|
|
6426
|
+
for (const orderBy of queryOrderBy(query)) {
|
|
6206
6427
|
// order by key always matches
|
|
6207
6428
|
if (!orderBy.field.isKeyField() && doc.data.field(orderBy.field) === null) {
|
|
6208
6429
|
return false;
|
|
@@ -6264,9 +6485,9 @@ function compareDocs(orderBy, d1, d2) {
|
|
|
6264
6485
|
? DocumentKey.comparator(d1.key, d2.key)
|
|
6265
6486
|
: compareDocumentsByField(orderBy.field, d1, d2);
|
|
6266
6487
|
switch (orderBy.dir) {
|
|
6267
|
-
case "asc" /* ASCENDING */:
|
|
6488
|
+
case "asc" /* Direction.ASCENDING */:
|
|
6268
6489
|
return comparison;
|
|
6269
|
-
case "desc" /* DESCENDING */:
|
|
6490
|
+
case "desc" /* Direction.DESCENDING */:
|
|
6270
6491
|
return -1 * comparison;
|
|
6271
6492
|
default:
|
|
6272
6493
|
return fail();
|
|
@@ -6952,10 +7173,10 @@ function mutationEquals(left, right) {
|
|
|
6952
7173
|
if (!fieldTransformsAreEqual(left.fieldTransforms, right.fieldTransforms)) {
|
|
6953
7174
|
return false;
|
|
6954
7175
|
}
|
|
6955
|
-
if (left.type === 0 /* Set */) {
|
|
7176
|
+
if (left.type === 0 /* MutationType.Set */) {
|
|
6956
7177
|
return left.value.isEqual(right.value);
|
|
6957
7178
|
}
|
|
6958
|
-
if (left.type === 1 /* Patch */) {
|
|
7179
|
+
if (left.type === 1 /* MutationType.Patch */) {
|
|
6959
7180
|
return (left.data.isEqual(right.data) &&
|
|
6960
7181
|
left.fieldMask.isEqual(right.fieldMask));
|
|
6961
7182
|
}
|
|
@@ -6972,7 +7193,7 @@ class SetMutation extends Mutation {
|
|
|
6972
7193
|
this.value = value;
|
|
6973
7194
|
this.precondition = precondition;
|
|
6974
7195
|
this.fieldTransforms = fieldTransforms;
|
|
6975
|
-
this.type = 0 /* Set */;
|
|
7196
|
+
this.type = 0 /* MutationType.Set */;
|
|
6976
7197
|
}
|
|
6977
7198
|
getFieldMask() {
|
|
6978
7199
|
return null;
|
|
@@ -7024,7 +7245,7 @@ class PatchMutation extends Mutation {
|
|
|
7024
7245
|
this.fieldMask = fieldMask;
|
|
7025
7246
|
this.precondition = precondition;
|
|
7026
7247
|
this.fieldTransforms = fieldTransforms;
|
|
7027
|
-
this.type = 1 /* Patch */;
|
|
7248
|
+
this.type = 1 /* MutationType.Patch */;
|
|
7028
7249
|
}
|
|
7029
7250
|
getFieldMask() {
|
|
7030
7251
|
return this.fieldMask;
|
|
@@ -7126,7 +7347,7 @@ class DeleteMutation extends Mutation {
|
|
|
7126
7347
|
super();
|
|
7127
7348
|
this.key = key;
|
|
7128
7349
|
this.precondition = precondition;
|
|
7129
|
-
this.type = 2 /* Delete */;
|
|
7350
|
+
this.type = 2 /* MutationType.Delete */;
|
|
7130
7351
|
this.fieldTransforms = [];
|
|
7131
7352
|
}
|
|
7132
7353
|
getFieldMask() {
|
|
@@ -7160,7 +7381,7 @@ class VerifyMutation extends Mutation {
|
|
|
7160
7381
|
super();
|
|
7161
7382
|
this.key = key;
|
|
7162
7383
|
this.precondition = precondition;
|
|
7163
|
-
this.type = 3 /* Verify */;
|
|
7384
|
+
this.type = 3 /* MutationType.Verify */;
|
|
7164
7385
|
this.fieldTransforms = [];
|
|
7165
7386
|
}
|
|
7166
7387
|
getFieldMask() {
|
|
@@ -7789,13 +8010,13 @@ class TargetState {
|
|
|
7789
8010
|
let removedDocuments = documentKeySet();
|
|
7790
8011
|
this.documentChanges.forEach((key, changeType) => {
|
|
7791
8012
|
switch (changeType) {
|
|
7792
|
-
case 0 /* Added */:
|
|
8013
|
+
case 0 /* ChangeType.Added */:
|
|
7793
8014
|
addedDocuments = addedDocuments.add(key);
|
|
7794
8015
|
break;
|
|
7795
|
-
case 2 /* Modified */:
|
|
8016
|
+
case 2 /* ChangeType.Modified */:
|
|
7796
8017
|
modifiedDocuments = modifiedDocuments.add(key);
|
|
7797
8018
|
break;
|
|
7798
|
-
case 1 /* Removed */:
|
|
8019
|
+
case 1 /* ChangeType.Removed */:
|
|
7799
8020
|
removedDocuments = removedDocuments.add(key);
|
|
7800
8021
|
break;
|
|
7801
8022
|
default:
|
|
@@ -7871,12 +8092,12 @@ class WatchChangeAggregator {
|
|
|
7871
8092
|
this.forEachTarget(targetChange, targetId => {
|
|
7872
8093
|
const targetState = this.ensureTargetState(targetId);
|
|
7873
8094
|
switch (targetChange.state) {
|
|
7874
|
-
case 0 /* NoChange */:
|
|
8095
|
+
case 0 /* WatchTargetChangeState.NoChange */:
|
|
7875
8096
|
if (this.isActiveTarget(targetId)) {
|
|
7876
8097
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7877
8098
|
}
|
|
7878
8099
|
break;
|
|
7879
|
-
case 1 /* Added */:
|
|
8100
|
+
case 1 /* WatchTargetChangeState.Added */:
|
|
7880
8101
|
// We need to decrement the number of pending acks needed from watch
|
|
7881
8102
|
// for this targetId.
|
|
7882
8103
|
targetState.recordTargetResponse();
|
|
@@ -7888,7 +8109,7 @@ class WatchChangeAggregator {
|
|
|
7888
8109
|
}
|
|
7889
8110
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7890
8111
|
break;
|
|
7891
|
-
case 2 /* Removed */:
|
|
8112
|
+
case 2 /* WatchTargetChangeState.Removed */:
|
|
7892
8113
|
// We need to keep track of removed targets to we can post-filter and
|
|
7893
8114
|
// remove any target changes.
|
|
7894
8115
|
// We need to decrement the number of pending acks needed from watch
|
|
@@ -7898,13 +8119,13 @@ class WatchChangeAggregator {
|
|
|
7898
8119
|
this.removeTarget(targetId);
|
|
7899
8120
|
}
|
|
7900
8121
|
break;
|
|
7901
|
-
case 3 /* Current */:
|
|
8122
|
+
case 3 /* WatchTargetChangeState.Current */:
|
|
7902
8123
|
if (this.isActiveTarget(targetId)) {
|
|
7903
8124
|
targetState.markCurrent();
|
|
7904
8125
|
targetState.updateResumeToken(targetChange.resumeToken);
|
|
7905
8126
|
}
|
|
7906
8127
|
break;
|
|
7907
|
-
case 4 /* Reset */:
|
|
8128
|
+
case 4 /* WatchTargetChangeState.Reset */:
|
|
7908
8129
|
if (this.isActiveTarget(targetId)) {
|
|
7909
8130
|
// Reset the target and synthesizes removes for all existing
|
|
7910
8131
|
// documents. The backend will re-add any documents that still
|
|
@@ -8014,7 +8235,7 @@ class WatchChangeAggregator {
|
|
|
8014
8235
|
targets.forEachWhile(targetId => {
|
|
8015
8236
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
8016
8237
|
if (targetData &&
|
|
8017
|
-
targetData.purpose !== 2 /* LimboResolution */) {
|
|
8238
|
+
targetData.purpose !== 2 /* TargetPurpose.LimboResolution */) {
|
|
8018
8239
|
isOnlyLimboTarget = false;
|
|
8019
8240
|
return false;
|
|
8020
8241
|
}
|
|
@@ -8041,8 +8262,8 @@ class WatchChangeAggregator {
|
|
|
8041
8262
|
return;
|
|
8042
8263
|
}
|
|
8043
8264
|
const changeType = this.targetContainsDocument(targetId, document.key)
|
|
8044
|
-
? 2 /* Modified */
|
|
8045
|
-
: 0 /* Added */;
|
|
8265
|
+
? 2 /* ChangeType.Modified */
|
|
8266
|
+
: 0 /* ChangeType.Added */;
|
|
8046
8267
|
const targetState = this.ensureTargetState(targetId);
|
|
8047
8268
|
targetState.addDocumentChange(document.key, changeType);
|
|
8048
8269
|
this.pendingDocumentUpdates = this.pendingDocumentUpdates.insert(document.key, document);
|
|
@@ -8063,7 +8284,7 @@ class WatchChangeAggregator {
|
|
|
8063
8284
|
}
|
|
8064
8285
|
const targetState = this.ensureTargetState(targetId);
|
|
8065
8286
|
if (this.targetContainsDocument(targetId, key)) {
|
|
8066
|
-
targetState.addDocumentChange(key, 1 /* Removed */);
|
|
8287
|
+
targetState.addDocumentChange(key, 1 /* ChangeType.Removed */);
|
|
8067
8288
|
}
|
|
8068
8289
|
else {
|
|
8069
8290
|
// The document may have entered and left the target before we raised a
|
|
@@ -8188,22 +8409,28 @@ function snapshotChangesMap() {
|
|
|
8188
8409
|
*/
|
|
8189
8410
|
const DIRECTIONS = (() => {
|
|
8190
8411
|
const dirs = {};
|
|
8191
|
-
dirs["asc" /* ASCENDING */] = 'ASCENDING';
|
|
8192
|
-
dirs["desc" /* DESCENDING */] = 'DESCENDING';
|
|
8412
|
+
dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
|
|
8413
|
+
dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
|
|
8193
8414
|
return dirs;
|
|
8194
8415
|
})();
|
|
8195
8416
|
const OPERATORS = (() => {
|
|
8196
8417
|
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';
|
|
8418
|
+
ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
|
|
8419
|
+
ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
8420
|
+
ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
|
|
8421
|
+
ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
8422
|
+
ops["==" /* Operator.EQUAL */] = 'EQUAL';
|
|
8423
|
+
ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
|
|
8424
|
+
ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
8425
|
+
ops["in" /* Operator.IN */] = 'IN';
|
|
8426
|
+
ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
|
|
8427
|
+
ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
8428
|
+
return ops;
|
|
8429
|
+
})();
|
|
8430
|
+
const COMPOSITE_OPERATORS = (() => {
|
|
8431
|
+
const ops = {};
|
|
8432
|
+
ops["and" /* CompositeOperator.AND */] = 'AND';
|
|
8433
|
+
ops["or" /* CompositeOperator.OR */] = 'OR';
|
|
8207
8434
|
return ops;
|
|
8208
8435
|
})();
|
|
8209
8436
|
function assertPresent(value, description) {
|
|
@@ -8397,14 +8624,21 @@ function toDocument(serializer, document) {
|
|
|
8397
8624
|
return {
|
|
8398
8625
|
name: toName(serializer, document.key),
|
|
8399
8626
|
fields: document.data.value.mapValue.fields,
|
|
8400
|
-
updateTime: toTimestamp(serializer, document.version.toTimestamp())
|
|
8627
|
+
updateTime: toTimestamp(serializer, document.version.toTimestamp()),
|
|
8628
|
+
createTime: toTimestamp(serializer, document.createTime.toTimestamp())
|
|
8401
8629
|
};
|
|
8402
8630
|
}
|
|
8403
8631
|
function fromDocument(serializer, document, hasCommittedMutations) {
|
|
8404
8632
|
const key = fromName(serializer, document.name);
|
|
8405
8633
|
const version = fromVersion(document.updateTime);
|
|
8634
|
+
// If we read a document from persistence that is missing createTime, it's due
|
|
8635
|
+
// to older SDK versions not storing this information. In such cases, we'll
|
|
8636
|
+
// set the createTime to zero. This can be removed in the long term.
|
|
8637
|
+
const createTime = document.createTime
|
|
8638
|
+
? fromVersion(document.createTime)
|
|
8639
|
+
: SnapshotVersion.min();
|
|
8406
8640
|
const data = new ObjectValue({ mapValue: { fields: document.fields } });
|
|
8407
|
-
const result = MutableDocument.newFoundDocument(key, version, data);
|
|
8641
|
+
const result = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8408
8642
|
if (hasCommittedMutations) {
|
|
8409
8643
|
result.setHasCommittedMutations();
|
|
8410
8644
|
}
|
|
@@ -8416,8 +8650,11 @@ function fromFound(serializer, doc) {
|
|
|
8416
8650
|
assertPresent(doc.found.updateTime);
|
|
8417
8651
|
const key = fromName(serializer, doc.found.name);
|
|
8418
8652
|
const version = fromVersion(doc.found.updateTime);
|
|
8653
|
+
const createTime = doc.found.createTime
|
|
8654
|
+
? fromVersion(doc.found.createTime)
|
|
8655
|
+
: SnapshotVersion.min();
|
|
8419
8656
|
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
|
|
8420
|
-
return MutableDocument.newFoundDocument(key, version, data);
|
|
8657
|
+
return MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8421
8658
|
}
|
|
8422
8659
|
function fromMissing(serializer, result) {
|
|
8423
8660
|
hardAssert(!!result.missing);
|
|
@@ -8456,10 +8693,13 @@ function fromWatchChange(serializer, change) {
|
|
|
8456
8693
|
assertPresent(entityChange.document.updateTime);
|
|
8457
8694
|
const key = fromName(serializer, entityChange.document.name);
|
|
8458
8695
|
const version = fromVersion(entityChange.document.updateTime);
|
|
8696
|
+
const createTime = entityChange.document.createTime
|
|
8697
|
+
? fromVersion(entityChange.document.createTime)
|
|
8698
|
+
: SnapshotVersion.min();
|
|
8459
8699
|
const data = new ObjectValue({
|
|
8460
8700
|
mapValue: { fields: entityChange.document.fields }
|
|
8461
8701
|
});
|
|
8462
|
-
const doc = MutableDocument.newFoundDocument(key, version, data);
|
|
8702
|
+
const doc = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
8463
8703
|
const updatedTargetIds = entityChange.targetIds || [];
|
|
8464
8704
|
const removedTargetIds = entityChange.removedTargetIds || [];
|
|
8465
8705
|
watchChange = new DocumentWatchChange(updatedTargetIds, removedTargetIds, doc.key, doc);
|
|
@@ -8501,19 +8741,19 @@ function fromWatchChange(serializer, change) {
|
|
|
8501
8741
|
}
|
|
8502
8742
|
function fromWatchTargetChangeState(state) {
|
|
8503
8743
|
if (state === 'NO_CHANGE') {
|
|
8504
|
-
return 0 /* NoChange */;
|
|
8744
|
+
return 0 /* WatchTargetChangeState.NoChange */;
|
|
8505
8745
|
}
|
|
8506
8746
|
else if (state === 'ADD') {
|
|
8507
|
-
return 1 /* Added */;
|
|
8747
|
+
return 1 /* WatchTargetChangeState.Added */;
|
|
8508
8748
|
}
|
|
8509
8749
|
else if (state === 'REMOVE') {
|
|
8510
|
-
return 2 /* Removed */;
|
|
8750
|
+
return 2 /* WatchTargetChangeState.Removed */;
|
|
8511
8751
|
}
|
|
8512
8752
|
else if (state === 'CURRENT') {
|
|
8513
|
-
return 3 /* Current */;
|
|
8753
|
+
return 3 /* WatchTargetChangeState.Current */;
|
|
8514
8754
|
}
|
|
8515
8755
|
else if (state === 'RESET') {
|
|
8516
|
-
return 4 /* Reset */;
|
|
8756
|
+
return 4 /* WatchTargetChangeState.Reset */;
|
|
8517
8757
|
}
|
|
8518
8758
|
else {
|
|
8519
8759
|
return fail();
|
|
@@ -8731,7 +8971,7 @@ function toQueryTarget(serializer, target) {
|
|
|
8731
8971
|
result.parent = toQueryPath(serializer, path.popLast());
|
|
8732
8972
|
result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
|
|
8733
8973
|
}
|
|
8734
|
-
const where =
|
|
8974
|
+
const where = toFilters(target.filters);
|
|
8735
8975
|
if (where) {
|
|
8736
8976
|
result.structuredQuery.where = where;
|
|
8737
8977
|
}
|
|
@@ -8783,7 +9023,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8783
9023
|
}
|
|
8784
9024
|
let filterBy = [];
|
|
8785
9025
|
if (query.where) {
|
|
8786
|
-
filterBy =
|
|
9026
|
+
filterBy = fromFilters(query.where);
|
|
8787
9027
|
}
|
|
8788
9028
|
let orderBy = [];
|
|
8789
9029
|
if (query.orderBy) {
|
|
@@ -8801,7 +9041,7 @@ function convertQueryTargetToQuery(target) {
|
|
|
8801
9041
|
if (query.endAt) {
|
|
8802
9042
|
endAt = fromEndAtCursor(query.endAt);
|
|
8803
9043
|
}
|
|
8804
|
-
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
9044
|
+
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* LimitType.First */, startAt, endAt);
|
|
8805
9045
|
}
|
|
8806
9046
|
function fromQueryTarget(target) {
|
|
8807
9047
|
return queryToTarget(convertQueryTargetToQuery(target));
|
|
@@ -8819,11 +9059,11 @@ function toListenRequestLabels(serializer, targetData) {
|
|
|
8819
9059
|
}
|
|
8820
9060
|
function toLabel(serializer, purpose) {
|
|
8821
9061
|
switch (purpose) {
|
|
8822
|
-
case 0 /* Listen */:
|
|
9062
|
+
case 0 /* TargetPurpose.Listen */:
|
|
8823
9063
|
return null;
|
|
8824
|
-
case 1 /* ExistenceFilterMismatch */:
|
|
9064
|
+
case 1 /* TargetPurpose.ExistenceFilterMismatch */:
|
|
8825
9065
|
return 'existence-filter-mismatch';
|
|
8826
|
-
case 2 /* LimboResolution */:
|
|
9066
|
+
case 2 /* TargetPurpose.LimboResolution */:
|
|
8827
9067
|
return 'limbo-document';
|
|
8828
9068
|
default:
|
|
8829
9069
|
return fail();
|
|
@@ -8850,32 +9090,29 @@ function toTarget(serializer, targetData) {
|
|
|
8850
9090
|
}
|
|
8851
9091
|
return result;
|
|
8852
9092
|
}
|
|
8853
|
-
function
|
|
9093
|
+
function toFilters(filters) {
|
|
8854
9094
|
if (filters.length === 0) {
|
|
8855
9095
|
return;
|
|
8856
9096
|
}
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
9097
|
+
return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
|
|
9098
|
+
}
|
|
9099
|
+
function fromFilters(filter) {
|
|
9100
|
+
const result = fromFilter(filter);
|
|
9101
|
+
if (result instanceof CompositeFilter &&
|
|
9102
|
+
compositeFilterIsFlatConjunction(result)) {
|
|
9103
|
+
return result.getFilters();
|
|
8862
9104
|
}
|
|
8863
|
-
return
|
|
9105
|
+
return [result];
|
|
8864
9106
|
}
|
|
8865
9107
|
function fromFilter(filter) {
|
|
8866
|
-
if (
|
|
8867
|
-
return
|
|
8868
|
-
}
|
|
8869
|
-
else if (filter.unaryFilter !== undefined) {
|
|
8870
|
-
return [fromUnaryFilter(filter)];
|
|
9108
|
+
if (filter.unaryFilter !== undefined) {
|
|
9109
|
+
return fromUnaryFilter(filter);
|
|
8871
9110
|
}
|
|
8872
9111
|
else if (filter.fieldFilter !== undefined) {
|
|
8873
|
-
return
|
|
9112
|
+
return fromFieldFilter(filter);
|
|
8874
9113
|
}
|
|
8875
9114
|
else if (filter.compositeFilter !== undefined) {
|
|
8876
|
-
return filter
|
|
8877
|
-
.filters.map(f => fromFilter(f))
|
|
8878
|
-
.reduce((accum, current) => accum.concat(current));
|
|
9115
|
+
return fromCompositeFilter(filter);
|
|
8879
9116
|
}
|
|
8880
9117
|
else {
|
|
8881
9118
|
return fail();
|
|
@@ -8920,9 +9157,9 @@ function toDirection(dir) {
|
|
|
8920
9157
|
function fromDirection(dir) {
|
|
8921
9158
|
switch (dir) {
|
|
8922
9159
|
case 'ASCENDING':
|
|
8923
|
-
return "asc" /* ASCENDING */;
|
|
9160
|
+
return "asc" /* Direction.ASCENDING */;
|
|
8924
9161
|
case 'DESCENDING':
|
|
8925
|
-
return "desc" /* DESCENDING */;
|
|
9162
|
+
return "desc" /* Direction.DESCENDING */;
|
|
8926
9163
|
default:
|
|
8927
9164
|
return undefined;
|
|
8928
9165
|
}
|
|
@@ -8931,34 +9168,47 @@ function fromDirection(dir) {
|
|
|
8931
9168
|
function toOperatorName(op) {
|
|
8932
9169
|
return OPERATORS[op];
|
|
8933
9170
|
}
|
|
9171
|
+
function toCompositeOperatorName(op) {
|
|
9172
|
+
return COMPOSITE_OPERATORS[op];
|
|
9173
|
+
}
|
|
8934
9174
|
function fromOperatorName(op) {
|
|
8935
9175
|
switch (op) {
|
|
8936
9176
|
case 'EQUAL':
|
|
8937
|
-
return "==" /* EQUAL */;
|
|
9177
|
+
return "==" /* Operator.EQUAL */;
|
|
8938
9178
|
case 'NOT_EQUAL':
|
|
8939
|
-
return "!=" /* NOT_EQUAL */;
|
|
9179
|
+
return "!=" /* Operator.NOT_EQUAL */;
|
|
8940
9180
|
case 'GREATER_THAN':
|
|
8941
|
-
return ">" /* GREATER_THAN */;
|
|
9181
|
+
return ">" /* Operator.GREATER_THAN */;
|
|
8942
9182
|
case 'GREATER_THAN_OR_EQUAL':
|
|
8943
|
-
return ">=" /* GREATER_THAN_OR_EQUAL */;
|
|
9183
|
+
return ">=" /* Operator.GREATER_THAN_OR_EQUAL */;
|
|
8944
9184
|
case 'LESS_THAN':
|
|
8945
|
-
return "<" /* LESS_THAN */;
|
|
9185
|
+
return "<" /* Operator.LESS_THAN */;
|
|
8946
9186
|
case 'LESS_THAN_OR_EQUAL':
|
|
8947
|
-
return "<=" /* LESS_THAN_OR_EQUAL */;
|
|
9187
|
+
return "<=" /* Operator.LESS_THAN_OR_EQUAL */;
|
|
8948
9188
|
case 'ARRAY_CONTAINS':
|
|
8949
|
-
return "array-contains" /* ARRAY_CONTAINS */;
|
|
9189
|
+
return "array-contains" /* Operator.ARRAY_CONTAINS */;
|
|
8950
9190
|
case 'IN':
|
|
8951
|
-
return "in" /* IN */;
|
|
9191
|
+
return "in" /* Operator.IN */;
|
|
8952
9192
|
case 'NOT_IN':
|
|
8953
|
-
return "not-in" /* NOT_IN */;
|
|
9193
|
+
return "not-in" /* Operator.NOT_IN */;
|
|
8954
9194
|
case 'ARRAY_CONTAINS_ANY':
|
|
8955
|
-
return "array-contains-any" /* ARRAY_CONTAINS_ANY */;
|
|
9195
|
+
return "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
8956
9196
|
case 'OPERATOR_UNSPECIFIED':
|
|
8957
9197
|
return fail();
|
|
8958
9198
|
default:
|
|
8959
9199
|
return fail();
|
|
8960
9200
|
}
|
|
8961
9201
|
}
|
|
9202
|
+
function fromCompositeOperatorName(op) {
|
|
9203
|
+
switch (op) {
|
|
9204
|
+
case 'AND':
|
|
9205
|
+
return "and" /* CompositeOperator.AND */;
|
|
9206
|
+
case 'OR':
|
|
9207
|
+
return "or" /* CompositeOperator.OR */;
|
|
9208
|
+
default:
|
|
9209
|
+
return fail();
|
|
9210
|
+
}
|
|
9211
|
+
}
|
|
8962
9212
|
function toFieldPathReference(path) {
|
|
8963
9213
|
return { fieldPath: path.canonicalString() };
|
|
8964
9214
|
}
|
|
@@ -8975,12 +9225,32 @@ function toPropertyOrder(orderBy) {
|
|
|
8975
9225
|
function fromPropertyOrder(orderBy) {
|
|
8976
9226
|
return new OrderBy(fromFieldPathReference(orderBy.field), fromDirection(orderBy.direction));
|
|
8977
9227
|
}
|
|
8978
|
-
function fromFieldFilter(filter) {
|
|
8979
|
-
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
8980
|
-
}
|
|
8981
9228
|
// visible for testing
|
|
9229
|
+
function toFilter(filter) {
|
|
9230
|
+
if (filter instanceof FieldFilter) {
|
|
9231
|
+
return toUnaryOrFieldFilter(filter);
|
|
9232
|
+
}
|
|
9233
|
+
else if (filter instanceof CompositeFilter) {
|
|
9234
|
+
return toCompositeFilter(filter);
|
|
9235
|
+
}
|
|
9236
|
+
else {
|
|
9237
|
+
return fail();
|
|
9238
|
+
}
|
|
9239
|
+
}
|
|
9240
|
+
function toCompositeFilter(filter) {
|
|
9241
|
+
const protos = filter.getFilters().map(filter => toFilter(filter));
|
|
9242
|
+
if (protos.length === 1) {
|
|
9243
|
+
return protos[0];
|
|
9244
|
+
}
|
|
9245
|
+
return {
|
|
9246
|
+
compositeFilter: {
|
|
9247
|
+
op: toCompositeOperatorName(filter.op),
|
|
9248
|
+
filters: protos
|
|
9249
|
+
}
|
|
9250
|
+
};
|
|
9251
|
+
}
|
|
8982
9252
|
function toUnaryOrFieldFilter(filter) {
|
|
8983
|
-
if (filter.op === "==" /* EQUAL */) {
|
|
9253
|
+
if (filter.op === "==" /* Operator.EQUAL */) {
|
|
8984
9254
|
if (isNanValue(filter.value)) {
|
|
8985
9255
|
return {
|
|
8986
9256
|
unaryFilter: {
|
|
@@ -8998,7 +9268,7 @@ function toUnaryOrFieldFilter(filter) {
|
|
|
8998
9268
|
};
|
|
8999
9269
|
}
|
|
9000
9270
|
}
|
|
9001
|
-
else if (filter.op === "!=" /* NOT_EQUAL */) {
|
|
9271
|
+
else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
9002
9272
|
if (isNanValue(filter.value)) {
|
|
9003
9273
|
return {
|
|
9004
9274
|
unaryFilter: {
|
|
@@ -9028,22 +9298,22 @@ function fromUnaryFilter(filter) {
|
|
|
9028
9298
|
switch (filter.unaryFilter.op) {
|
|
9029
9299
|
case 'IS_NAN':
|
|
9030
9300
|
const nanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9031
|
-
return FieldFilter.create(nanField, "==" /* EQUAL */, {
|
|
9301
|
+
return FieldFilter.create(nanField, "==" /* Operator.EQUAL */, {
|
|
9032
9302
|
doubleValue: NaN
|
|
9033
9303
|
});
|
|
9034
9304
|
case 'IS_NULL':
|
|
9035
9305
|
const nullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9036
|
-
return FieldFilter.create(nullField, "==" /* EQUAL */, {
|
|
9306
|
+
return FieldFilter.create(nullField, "==" /* Operator.EQUAL */, {
|
|
9037
9307
|
nullValue: 'NULL_VALUE'
|
|
9038
9308
|
});
|
|
9039
9309
|
case 'IS_NOT_NAN':
|
|
9040
9310
|
const notNanField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9041
|
-
return FieldFilter.create(notNanField, "!=" /* NOT_EQUAL */, {
|
|
9311
|
+
return FieldFilter.create(notNanField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9042
9312
|
doubleValue: NaN
|
|
9043
9313
|
});
|
|
9044
9314
|
case 'IS_NOT_NULL':
|
|
9045
9315
|
const notNullField = fromFieldPathReference(filter.unaryFilter.field);
|
|
9046
|
-
return FieldFilter.create(notNullField, "!=" /* NOT_EQUAL */, {
|
|
9316
|
+
return FieldFilter.create(notNullField, "!=" /* Operator.NOT_EQUAL */, {
|
|
9047
9317
|
nullValue: 'NULL_VALUE'
|
|
9048
9318
|
});
|
|
9049
9319
|
case 'OPERATOR_UNSPECIFIED':
|
|
@@ -9052,6 +9322,12 @@ function fromUnaryFilter(filter) {
|
|
|
9052
9322
|
return fail();
|
|
9053
9323
|
}
|
|
9054
9324
|
}
|
|
9325
|
+
function fromFieldFilter(filter) {
|
|
9326
|
+
return FieldFilter.create(fromFieldPathReference(filter.fieldFilter.field), fromOperatorName(filter.fieldFilter.op), filter.fieldFilter.value);
|
|
9327
|
+
}
|
|
9328
|
+
function fromCompositeFilter(filter) {
|
|
9329
|
+
return CompositeFilter.create(filter.compositeFilter.filters.map(filter => fromFilter(filter)), fromCompositeOperatorName(filter.compositeFilter.op));
|
|
9330
|
+
}
|
|
9055
9331
|
function toDocumentMask(fieldMask) {
|
|
9056
9332
|
const canonicalFields = [];
|
|
9057
9333
|
fieldMask.fields.forEach(field => canonicalFields.push(field.canonicalString()));
|
|
@@ -9291,7 +9567,7 @@ function fromDbTarget(dbTarget) {
|
|
|
9291
9567
|
else {
|
|
9292
9568
|
target = fromQueryTarget(dbTarget.query);
|
|
9293
9569
|
}
|
|
9294
|
-
return new TargetData(target, dbTarget.targetId, 0 /* Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9570
|
+
return new TargetData(target, dbTarget.targetId, 0 /* TargetPurpose.Listen */, dbTarget.lastListenSequenceNumber, version, lastLimboFreeSnapshotVersion, ByteString.fromBase64String(dbTarget.resumeToken));
|
|
9295
9571
|
}
|
|
9296
9572
|
/** Encodes TargetData into a DbTarget for storage locally. */
|
|
9297
9573
|
function toDbTarget(localSerializer, targetData) {
|
|
@@ -9368,7 +9644,7 @@ function fromBundledQuery(bundledQuery) {
|
|
|
9368
9644
|
structuredQuery: bundledQuery.structuredQuery
|
|
9369
9645
|
});
|
|
9370
9646
|
if (bundledQuery.limitType === 'LAST') {
|
|
9371
|
-
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
9647
|
+
return queryWithLimit(query, query.limit, "L" /* LimitType.Last */);
|
|
9372
9648
|
}
|
|
9373
9649
|
return query;
|
|
9374
9650
|
}
|
|
@@ -10143,7 +10419,7 @@ class IndexByteEncoder {
|
|
|
10143
10419
|
this.orderedCode.seed(encodedBytes);
|
|
10144
10420
|
}
|
|
10145
10421
|
forKind(kind) {
|
|
10146
|
-
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
10422
|
+
return kind === 0 /* IndexKind.ASCENDING */ ? this.ascending : this.descending;
|
|
10147
10423
|
}
|
|
10148
10424
|
encodedBytes() {
|
|
10149
10425
|
return this.orderedCode.encodedBytes();
|
|
@@ -10298,6 +10574,7 @@ class TargetIndexMatcher {
|
|
|
10298
10574
|
* omitted.
|
|
10299
10575
|
*/
|
|
10300
10576
|
servedByIndex(index) {
|
|
10577
|
+
hardAssert(index.collectionGroup === this.collectionId);
|
|
10301
10578
|
// If there is an array element, find a matching filter.
|
|
10302
10579
|
const arraySegment = fieldIndexGetArraySegment(index);
|
|
10303
10580
|
if (arraySegment !== undefined &&
|
|
@@ -10361,19 +10638,268 @@ class TargetIndexMatcher {
|
|
|
10361
10638
|
if (filter === undefined || !filter.field.isEqual(segment.fieldPath)) {
|
|
10362
10639
|
return false;
|
|
10363
10640
|
}
|
|
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;
|
|
10641
|
+
const isArrayOperator = filter.op === "array-contains" /* Operator.ARRAY_CONTAINS */ ||
|
|
10642
|
+
filter.op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */;
|
|
10643
|
+
return (segment.kind === 2 /* IndexKind.CONTAINS */) === isArrayOperator;
|
|
10367
10644
|
}
|
|
10368
10645
|
matchesOrderBy(orderBy, segment) {
|
|
10369
10646
|
if (!orderBy.field.isEqual(segment.fieldPath)) {
|
|
10370
10647
|
return false;
|
|
10371
10648
|
}
|
|
10372
|
-
return ((segment.kind === 0 /* ASCENDING */ &&
|
|
10373
|
-
orderBy.dir === "asc" /* ASCENDING */) ||
|
|
10374
|
-
(segment.kind === 1 /* DESCENDING */ &&
|
|
10375
|
-
orderBy.dir === "desc" /* DESCENDING */));
|
|
10649
|
+
return ((segment.kind === 0 /* IndexKind.ASCENDING */ &&
|
|
10650
|
+
orderBy.dir === "asc" /* Direction.ASCENDING */) ||
|
|
10651
|
+
(segment.kind === 1 /* IndexKind.DESCENDING */ &&
|
|
10652
|
+
orderBy.dir === "desc" /* Direction.DESCENDING */));
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
|
|
10656
|
+
/**
|
|
10657
|
+
* @license
|
|
10658
|
+
* Copyright 2022 Google LLC
|
|
10659
|
+
*
|
|
10660
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10661
|
+
* you may not use this file except in compliance with the License.
|
|
10662
|
+
* You may obtain a copy of the License at
|
|
10663
|
+
*
|
|
10664
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10665
|
+
*
|
|
10666
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
10667
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
10668
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
10669
|
+
* See the License for the specific language governing permissions and
|
|
10670
|
+
* limitations under the License.
|
|
10671
|
+
*/
|
|
10672
|
+
/**
|
|
10673
|
+
* Provides utility functions that help with boolean logic transformations needed for handling
|
|
10674
|
+
* complex filters used in queries.
|
|
10675
|
+
*/
|
|
10676
|
+
/**
|
|
10677
|
+
* The `in` filter is only a syntactic sugar over a disjunction of equalities. For instance: `a in
|
|
10678
|
+
* [1,2,3]` is in fact `a==1 || a==2 || a==3`. This method expands any `in` filter in the given
|
|
10679
|
+
* input into a disjunction of equality filters and returns the expanded filter.
|
|
10680
|
+
*/
|
|
10681
|
+
function computeInExpansion(filter) {
|
|
10682
|
+
var _a, _b;
|
|
10683
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10684
|
+
if (filter instanceof FieldFilter) {
|
|
10685
|
+
if (filter instanceof InFilter) {
|
|
10686
|
+
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))) || [];
|
|
10687
|
+
return CompositeFilter.create(expandedFilters, "or" /* CompositeOperator.OR */);
|
|
10688
|
+
}
|
|
10689
|
+
else {
|
|
10690
|
+
// We have reached other kinds of field filters.
|
|
10691
|
+
return filter;
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
// We have a composite filter.
|
|
10695
|
+
const expandedFilters = filter.filters.map(subfilter => computeInExpansion(subfilter));
|
|
10696
|
+
return CompositeFilter.create(expandedFilters, filter.op);
|
|
10697
|
+
}
|
|
10698
|
+
/**
|
|
10699
|
+
* Given a composite filter, returns the list of terms in its disjunctive normal form.
|
|
10700
|
+
*
|
|
10701
|
+
* <p>Each element in the return value is one term of the resulting DNF. For instance: For the
|
|
10702
|
+
* input: (A || B) && C, the DNF form is: (A && C) || (B && C), and the return value is a list
|
|
10703
|
+
* with two elements: a composite filter that performs (A && C), and a composite filter that
|
|
10704
|
+
* performs (B && C).
|
|
10705
|
+
*
|
|
10706
|
+
* @param filter the composite filter to calculate DNF transform for.
|
|
10707
|
+
* @return the terms in the DNF transform.
|
|
10708
|
+
*/
|
|
10709
|
+
function getDnfTerms(filter) {
|
|
10710
|
+
if (filter.getFilters().length === 0) {
|
|
10711
|
+
return [];
|
|
10712
|
+
}
|
|
10713
|
+
const result = computeDistributedNormalForm(computeInExpansion(filter));
|
|
10714
|
+
hardAssert(isDisjunctiveNormalForm(result));
|
|
10715
|
+
if (isSingleFieldFilter(result) || isFlatConjunction(result)) {
|
|
10716
|
+
return [result];
|
|
10717
|
+
}
|
|
10718
|
+
return result.getFilters();
|
|
10719
|
+
}
|
|
10720
|
+
/** Returns true if the given filter is a single field filter. e.g. (a == 10). */
|
|
10721
|
+
function isSingleFieldFilter(filter) {
|
|
10722
|
+
return filter instanceof FieldFilter;
|
|
10723
|
+
}
|
|
10724
|
+
/**
|
|
10725
|
+
* Returns true if the given filter is the conjunction of one or more field filters. e.g. (a == 10
|
|
10726
|
+
* && b == 20)
|
|
10727
|
+
*/
|
|
10728
|
+
function isFlatConjunction(filter) {
|
|
10729
|
+
return (filter instanceof CompositeFilter &&
|
|
10730
|
+
compositeFilterIsFlatConjunction(filter));
|
|
10731
|
+
}
|
|
10732
|
+
/**
|
|
10733
|
+
* Returns whether or not the given filter is in disjunctive normal form (DNF).
|
|
10734
|
+
*
|
|
10735
|
+
* <p>In boolean logic, a disjunctive normal form (DNF) is a canonical normal form of a logical
|
|
10736
|
+
* formula consisting of a disjunction of conjunctions; it can also be described as an OR of ANDs.
|
|
10737
|
+
*
|
|
10738
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Disjunctive_normal_form
|
|
10739
|
+
*/
|
|
10740
|
+
function isDisjunctiveNormalForm(filter) {
|
|
10741
|
+
return (isSingleFieldFilter(filter) ||
|
|
10742
|
+
isFlatConjunction(filter) ||
|
|
10743
|
+
isDisjunctionOfFieldFiltersAndFlatConjunctions(filter));
|
|
10744
|
+
}
|
|
10745
|
+
/**
|
|
10746
|
+
* Returns true if the given filter is the disjunction of one or more "flat conjunctions" and
|
|
10747
|
+
* field filters. e.g. (a == 10) || (b==20 && c==30)
|
|
10748
|
+
*/
|
|
10749
|
+
function isDisjunctionOfFieldFiltersAndFlatConjunctions(filter) {
|
|
10750
|
+
if (filter instanceof CompositeFilter) {
|
|
10751
|
+
if (compositeFilterIsDisjunction(filter)) {
|
|
10752
|
+
for (const subFilter of filter.getFilters()) {
|
|
10753
|
+
if (!isSingleFieldFilter(subFilter) && !isFlatConjunction(subFilter)) {
|
|
10754
|
+
return false;
|
|
10755
|
+
}
|
|
10756
|
+
}
|
|
10757
|
+
return true;
|
|
10758
|
+
}
|
|
10759
|
+
}
|
|
10760
|
+
return false;
|
|
10761
|
+
}
|
|
10762
|
+
function computeDistributedNormalForm(filter) {
|
|
10763
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10764
|
+
if (filter instanceof FieldFilter) {
|
|
10765
|
+
return filter;
|
|
10766
|
+
}
|
|
10767
|
+
if (filter.filters.length === 1) {
|
|
10768
|
+
return computeDistributedNormalForm(filter.filters[0]);
|
|
10769
|
+
}
|
|
10770
|
+
// Compute DNF for each of the subfilters first
|
|
10771
|
+
const result = filter.filters.map(subfilter => computeDistributedNormalForm(subfilter));
|
|
10772
|
+
let newFilter = CompositeFilter.create(result, filter.op);
|
|
10773
|
+
newFilter = applyAssociation(newFilter);
|
|
10774
|
+
if (isDisjunctiveNormalForm(newFilter)) {
|
|
10775
|
+
return newFilter;
|
|
10776
|
+
}
|
|
10777
|
+
hardAssert(newFilter instanceof CompositeFilter);
|
|
10778
|
+
hardAssert(compositeFilterIsConjunction(newFilter));
|
|
10779
|
+
hardAssert(newFilter.filters.length > 1);
|
|
10780
|
+
return newFilter.filters.reduce((runningResult, filter) => applyDistribution(runningResult, filter));
|
|
10781
|
+
}
|
|
10782
|
+
function applyDistribution(lhs, rhs) {
|
|
10783
|
+
hardAssert(lhs instanceof FieldFilter || lhs instanceof CompositeFilter);
|
|
10784
|
+
hardAssert(rhs instanceof FieldFilter || rhs instanceof CompositeFilter);
|
|
10785
|
+
let result;
|
|
10786
|
+
if (lhs instanceof FieldFilter) {
|
|
10787
|
+
if (rhs instanceof FieldFilter) {
|
|
10788
|
+
// FieldFilter FieldFilter
|
|
10789
|
+
result = applyDistributionFieldFilters(lhs, rhs);
|
|
10790
|
+
}
|
|
10791
|
+
else {
|
|
10792
|
+
// FieldFilter CompositeFilter
|
|
10793
|
+
result = applyDistributionFieldAndCompositeFilters(lhs, rhs);
|
|
10794
|
+
}
|
|
10795
|
+
}
|
|
10796
|
+
else {
|
|
10797
|
+
if (rhs instanceof FieldFilter) {
|
|
10798
|
+
// CompositeFilter FieldFilter
|
|
10799
|
+
result = applyDistributionFieldAndCompositeFilters(rhs, lhs);
|
|
10800
|
+
}
|
|
10801
|
+
else {
|
|
10802
|
+
// CompositeFilter CompositeFilter
|
|
10803
|
+
result = applyDistributionCompositeFilters(lhs, rhs);
|
|
10804
|
+
}
|
|
10805
|
+
}
|
|
10806
|
+
return applyAssociation(result);
|
|
10807
|
+
}
|
|
10808
|
+
function applyDistributionFieldFilters(lhs, rhs) {
|
|
10809
|
+
// Conjunction distribution for two field filters is the conjunction of them.
|
|
10810
|
+
return CompositeFilter.create([lhs, rhs], "and" /* CompositeOperator.AND */);
|
|
10811
|
+
}
|
|
10812
|
+
function applyDistributionCompositeFilters(lhs, rhs) {
|
|
10813
|
+
hardAssert(lhs.filters.length > 0 && rhs.filters.length > 0);
|
|
10814
|
+
// There are four cases:
|
|
10815
|
+
// (A & B) & (C & D) --> (A & B & C & D)
|
|
10816
|
+
// (A & B) & (C | D) --> (A & B & C) | (A & B & D)
|
|
10817
|
+
// (A | B) & (C & D) --> (C & D & A) | (C & D & B)
|
|
10818
|
+
// (A | B) & (C | D) --> (A & C) | (A & D) | (B & C) | (B & D)
|
|
10819
|
+
// Case 1 is a merge.
|
|
10820
|
+
if (compositeFilterIsConjunction(lhs) && compositeFilterIsConjunction(rhs)) {
|
|
10821
|
+
return compositeFilterWithAddedFilters(lhs, rhs.getFilters());
|
|
10822
|
+
}
|
|
10823
|
+
// Case 2,3,4 all have at least one side (lhs or rhs) that is a disjunction. In all three cases
|
|
10824
|
+
// we should take each element of the disjunction and distribute it over the other side, and
|
|
10825
|
+
// return the disjunction of the distribution results.
|
|
10826
|
+
const disjunctionSide = compositeFilterIsDisjunction(lhs) ? lhs : rhs;
|
|
10827
|
+
const otherSide = compositeFilterIsDisjunction(lhs) ? rhs : lhs;
|
|
10828
|
+
const results = disjunctionSide.filters.map(subfilter => applyDistribution(subfilter, otherSide));
|
|
10829
|
+
return CompositeFilter.create(results, "or" /* CompositeOperator.OR */);
|
|
10830
|
+
}
|
|
10831
|
+
function applyDistributionFieldAndCompositeFilters(fieldFilter, compositeFilter) {
|
|
10832
|
+
// There are two cases:
|
|
10833
|
+
// A & (B & C) --> (A & B & C)
|
|
10834
|
+
// A & (B | C) --> (A & B) | (A & C)
|
|
10835
|
+
if (compositeFilterIsConjunction(compositeFilter)) {
|
|
10836
|
+
// Case 1
|
|
10837
|
+
return compositeFilterWithAddedFilters(compositeFilter, fieldFilter.getFilters());
|
|
10838
|
+
}
|
|
10839
|
+
else {
|
|
10840
|
+
// Case 2
|
|
10841
|
+
const newFilters = compositeFilter.filters.map(subfilter => applyDistribution(fieldFilter, subfilter));
|
|
10842
|
+
return CompositeFilter.create(newFilters, "or" /* CompositeOperator.OR */);
|
|
10843
|
+
}
|
|
10844
|
+
}
|
|
10845
|
+
/**
|
|
10846
|
+
* Applies the associativity property to the given filter and returns the resulting filter.
|
|
10847
|
+
*
|
|
10848
|
+
* <ul>
|
|
10849
|
+
* <li>A | (B | C) == (A | B) | C == (A | B | C)
|
|
10850
|
+
* <li>A & (B & C) == (A & B) & C == (A & B & C)
|
|
10851
|
+
* </ul>
|
|
10852
|
+
*
|
|
10853
|
+
* <p>For more info, visit: https://en.wikipedia.org/wiki/Associative_property#Propositional_logic
|
|
10854
|
+
*/
|
|
10855
|
+
function applyAssociation(filter) {
|
|
10856
|
+
hardAssert(filter instanceof FieldFilter || filter instanceof CompositeFilter);
|
|
10857
|
+
if (filter instanceof FieldFilter) {
|
|
10858
|
+
return filter;
|
|
10859
|
+
}
|
|
10860
|
+
const filters = filter.getFilters();
|
|
10861
|
+
// If the composite filter only contains 1 filter, apply associativity to it.
|
|
10862
|
+
if (filters.length === 1) {
|
|
10863
|
+
return applyAssociation(filters[0]);
|
|
10864
|
+
}
|
|
10865
|
+
// Associativity applied to a flat composite filter results is itself.
|
|
10866
|
+
if (compositeFilterIsFlat(filter)) {
|
|
10867
|
+
return filter;
|
|
10868
|
+
}
|
|
10869
|
+
// First apply associativity to all subfilters. This will in turn recursively apply
|
|
10870
|
+
// associativity to all nested composite filters and field filters.
|
|
10871
|
+
const updatedFilters = filters.map(subfilter => applyAssociation(subfilter));
|
|
10872
|
+
// For composite subfilters that perform the same kind of logical operation as `compositeFilter`
|
|
10873
|
+
// take out their filters and add them to `compositeFilter`. For example:
|
|
10874
|
+
// compositeFilter = (A | (B | C | D))
|
|
10875
|
+
// compositeSubfilter = (B | C | D)
|
|
10876
|
+
// Result: (A | B | C | D)
|
|
10877
|
+
// Note that the `compositeSubfilter` has been eliminated, and its filters (B, C, D) have been
|
|
10878
|
+
// added to the top-level "compositeFilter".
|
|
10879
|
+
const newSubfilters = [];
|
|
10880
|
+
updatedFilters.forEach(subfilter => {
|
|
10881
|
+
if (subfilter instanceof FieldFilter) {
|
|
10882
|
+
newSubfilters.push(subfilter);
|
|
10883
|
+
}
|
|
10884
|
+
else if (subfilter instanceof CompositeFilter) {
|
|
10885
|
+
if (subfilter.op === filter.op) {
|
|
10886
|
+
// compositeFilter: (A | (B | C))
|
|
10887
|
+
// compositeSubfilter: (B | C)
|
|
10888
|
+
// Result: (A | B | C)
|
|
10889
|
+
newSubfilters.push(...subfilter.filters);
|
|
10890
|
+
}
|
|
10891
|
+
else {
|
|
10892
|
+
// compositeFilter: (A | (B & C))
|
|
10893
|
+
// compositeSubfilter: (B & C)
|
|
10894
|
+
// Result: (A | (B & C))
|
|
10895
|
+
newSubfilters.push(subfilter);
|
|
10896
|
+
}
|
|
10897
|
+
}
|
|
10898
|
+
});
|
|
10899
|
+
if (newSubfilters.length === 1) {
|
|
10900
|
+
return newSubfilters[0];
|
|
10376
10901
|
}
|
|
10902
|
+
return CompositeFilter.create(newSubfilters, filter.op);
|
|
10377
10903
|
}
|
|
10378
10904
|
|
|
10379
10905
|
/**
|
|
@@ -10420,7 +10946,7 @@ class MemoryIndexManager {
|
|
|
10420
10946
|
}
|
|
10421
10947
|
getIndexType(transaction, target) {
|
|
10422
10948
|
// Field indices are not supported with memory persistence.
|
|
10423
|
-
return PersistencePromise.resolve(0 /* NONE */);
|
|
10949
|
+
return PersistencePromise.resolve(0 /* IndexType.NONE */);
|
|
10424
10950
|
}
|
|
10425
10951
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10426
10952
|
// Field indices are not supported with memory persistence.
|
|
@@ -10643,8 +11169,14 @@ class IndexedDbIndexManager {
|
|
|
10643
11169
|
if (subTargets) {
|
|
10644
11170
|
return subTargets;
|
|
10645
11171
|
}
|
|
10646
|
-
|
|
10647
|
-
|
|
11172
|
+
if (target.filters.length === 0) {
|
|
11173
|
+
subTargets = [target];
|
|
11174
|
+
}
|
|
11175
|
+
else {
|
|
11176
|
+
// There is an implicit AND operation between all the filters stored in the target
|
|
11177
|
+
const dnf = getDnfTerms(CompositeFilter.create(target.filters, "and" /* CompositeOperator.AND */));
|
|
11178
|
+
subTargets = dnf.map(term => newTarget(target.path, target.collectionGroup, target.orderBy, term.getFilters(), target.limit, target.startAt, target.endAt));
|
|
11179
|
+
}
|
|
10648
11180
|
this.targetToDnfSubTargets.set(target, subTargets);
|
|
10649
11181
|
return subTargets;
|
|
10650
11182
|
}
|
|
@@ -10702,18 +11234,29 @@ class IndexedDbIndexManager {
|
|
|
10702
11234
|
});
|
|
10703
11235
|
}
|
|
10704
11236
|
getIndexType(transaction, target) {
|
|
10705
|
-
let indexType = 2 /* FULL */;
|
|
10706
|
-
|
|
11237
|
+
let indexType = 2 /* IndexType.FULL */;
|
|
11238
|
+
const subTargets = this.getSubTargets(target);
|
|
11239
|
+
return PersistencePromise.forEach(subTargets, (target) => {
|
|
10707
11240
|
return this.getFieldIndex(transaction, target).next(index => {
|
|
10708
11241
|
if (!index) {
|
|
10709
|
-
indexType = 0 /* NONE */;
|
|
11242
|
+
indexType = 0 /* IndexType.NONE */;
|
|
10710
11243
|
}
|
|
10711
|
-
else if (indexType !== 0 /* NONE */ &&
|
|
11244
|
+
else if (indexType !== 0 /* IndexType.NONE */ &&
|
|
10712
11245
|
index.fields.length < targetGetSegmentCount(target)) {
|
|
10713
|
-
indexType = 1 /* PARTIAL */;
|
|
11246
|
+
indexType = 1 /* IndexType.PARTIAL */;
|
|
10714
11247
|
}
|
|
10715
11248
|
});
|
|
10716
|
-
}).next(() =>
|
|
11249
|
+
}).next(() => {
|
|
11250
|
+
// OR queries have more than one sub-target (one sub-target per DNF term). We currently consider
|
|
11251
|
+
// OR queries that have a `limit` to have a partial index. For such queries we perform sorting
|
|
11252
|
+
// and apply the limit in memory as a post-processing step.
|
|
11253
|
+
if (targetHasLimit(target) &&
|
|
11254
|
+
subTargets.length > 1 &&
|
|
11255
|
+
indexType === 2 /* IndexType.FULL */) {
|
|
11256
|
+
return 1 /* IndexType.PARTIAL */;
|
|
11257
|
+
}
|
|
11258
|
+
return indexType;
|
|
11259
|
+
});
|
|
10717
11260
|
}
|
|
10718
11261
|
/**
|
|
10719
11262
|
* Returns the byte encoded form of the directional values in the field index.
|
|
@@ -10735,7 +11278,7 @@ class IndexedDbIndexManager {
|
|
|
10735
11278
|
/** Encodes a single value to the ascending index format. */
|
|
10736
11279
|
encodeSingleElement(value) {
|
|
10737
11280
|
const encoder = new IndexByteEncoder();
|
|
10738
|
-
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
11281
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* IndexKind.ASCENDING */));
|
|
10739
11282
|
return encoder.encodedBytes();
|
|
10740
11283
|
}
|
|
10741
11284
|
/**
|
|
@@ -10751,15 +11294,15 @@ class IndexedDbIndexManager {
|
|
|
10751
11294
|
* Encodes the given field values according to the specification in `target`.
|
|
10752
11295
|
* For IN queries, a list of possible values is returned.
|
|
10753
11296
|
*/
|
|
10754
|
-
encodeValues(fieldIndex, target,
|
|
10755
|
-
if (
|
|
11297
|
+
encodeValues(fieldIndex, target, values) {
|
|
11298
|
+
if (values === null) {
|
|
10756
11299
|
return [];
|
|
10757
11300
|
}
|
|
10758
11301
|
let encoders = [];
|
|
10759
11302
|
encoders.push(new IndexByteEncoder());
|
|
10760
|
-
let
|
|
11303
|
+
let valueIdx = 0;
|
|
10761
11304
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
10762
|
-
const value =
|
|
11305
|
+
const value = values[valueIdx++];
|
|
10763
11306
|
for (const encoder of encoders) {
|
|
10764
11307
|
if (this.isInFilter(target, segment.fieldPath) && isArray(value)) {
|
|
10765
11308
|
encoders = this.expandIndexValues(encoders, segment, value);
|
|
@@ -10810,7 +11353,7 @@ class IndexedDbIndexManager {
|
|
|
10810
11353
|
isInFilter(target, fieldPath) {
|
|
10811
11354
|
return !!target.filters.find(f => f instanceof FieldFilter &&
|
|
10812
11355
|
f.field.isEqual(fieldPath) &&
|
|
10813
|
-
(f.op === "in" /* IN */ || f.op === "not-in" /* NOT_IN */));
|
|
11356
|
+
(f.op === "in" /* Operator.IN */ || f.op === "not-in" /* Operator.NOT_IN */));
|
|
10814
11357
|
}
|
|
10815
11358
|
getFieldIndexes(transaction, collectionGroup) {
|
|
10816
11359
|
const indexes = indexConfigurationStore(transaction);
|
|
@@ -10999,24 +11542,36 @@ class IndexedDbIndexManager {
|
|
|
10999
11542
|
bounds.push(upper);
|
|
11000
11543
|
const ranges = [];
|
|
11001
11544
|
for (let i = 0; i < bounds.length; i += 2) {
|
|
11002
|
-
|
|
11545
|
+
// If we encounter two bounds that will create an unmatchable key range,
|
|
11546
|
+
// then we return an empty set of key ranges.
|
|
11547
|
+
if (this.isRangeMatchable(bounds[i], bounds[i + 1])) {
|
|
11548
|
+
return [];
|
|
11549
|
+
}
|
|
11550
|
+
const lowerBound = [
|
|
11003
11551
|
bounds[i].indexId,
|
|
11004
11552
|
this.uid,
|
|
11005
11553
|
bounds[i].arrayValue,
|
|
11006
11554
|
bounds[i].directionalValue,
|
|
11007
11555
|
EMPTY_VALUE,
|
|
11008
11556
|
[]
|
|
11009
|
-
]
|
|
11557
|
+
];
|
|
11558
|
+
const upperBound = [
|
|
11010
11559
|
bounds[i + 1].indexId,
|
|
11011
11560
|
this.uid,
|
|
11012
11561
|
bounds[i + 1].arrayValue,
|
|
11013
11562
|
bounds[i + 1].directionalValue,
|
|
11014
11563
|
EMPTY_VALUE,
|
|
11015
11564
|
[]
|
|
11016
|
-
]
|
|
11565
|
+
];
|
|
11566
|
+
ranges.push(IDBKeyRange.bound(lowerBound, upperBound));
|
|
11017
11567
|
}
|
|
11018
11568
|
return ranges;
|
|
11019
11569
|
}
|
|
11570
|
+
isRangeMatchable(lowerBound, upperBound) {
|
|
11571
|
+
// If lower bound is greater than the upper bound, then the key
|
|
11572
|
+
// range can never be matched.
|
|
11573
|
+
return indexEntryComparator(lowerBound, upperBound) > 0;
|
|
11574
|
+
}
|
|
11020
11575
|
getMinOffsetFromCollectionGroup(transaction, collectionGroup) {
|
|
11021
11576
|
return this.getFieldIndexes(transaction, collectionGroup).next(getMinOffsetFromFieldIndexes);
|
|
11022
11577
|
}
|
|
@@ -11993,7 +12548,7 @@ class LruScheduler {
|
|
|
11993
12548
|
}
|
|
11994
12549
|
scheduleGC(delay) {
|
|
11995
12550
|
logDebug(LOG_TAG$e, `Garbage collection scheduled in ${delay}ms`);
|
|
11996
|
-
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* LruGarbageCollection */, delay, async () => {
|
|
12551
|
+
this.gcTask = this.asyncQueue.enqueueAfterDelay("lru_garbage_collection" /* TimerId.LruGarbageCollection */, delay, async () => {
|
|
11997
12552
|
this.gcTask = null;
|
|
11998
12553
|
try {
|
|
11999
12554
|
await this.localStore.collectGarbage(this.garbageCollector);
|
|
@@ -12891,7 +13446,7 @@ class LocalDocumentsView {
|
|
|
12891
13446
|
.getOverlay(transaction, key)
|
|
12892
13447
|
.next(value => {
|
|
12893
13448
|
overlay = value;
|
|
12894
|
-
return this.
|
|
13449
|
+
return this.remoteDocumentCache.getEntry(transaction, key);
|
|
12895
13450
|
})
|
|
12896
13451
|
.next(document => {
|
|
12897
13452
|
if (overlay !== null) {
|
|
@@ -13114,7 +13669,9 @@ class LocalDocumentsView {
|
|
|
13114
13669
|
if (originalDocs.get(key)) {
|
|
13115
13670
|
return PersistencePromise.resolve();
|
|
13116
13671
|
}
|
|
13117
|
-
return this.
|
|
13672
|
+
return this.remoteDocumentCache
|
|
13673
|
+
.getEntry(transaction, key)
|
|
13674
|
+
.next(doc => {
|
|
13118
13675
|
modifiedDocs = modifiedDocs.insert(key, doc);
|
|
13119
13676
|
});
|
|
13120
13677
|
})
|
|
@@ -13188,12 +13745,6 @@ class LocalDocumentsView {
|
|
|
13188
13745
|
return results;
|
|
13189
13746
|
});
|
|
13190
13747
|
}
|
|
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
13748
|
}
|
|
13198
13749
|
|
|
13199
13750
|
/**
|
|
@@ -14943,7 +15494,7 @@ class IndexedDbPersistence {
|
|
|
14943
15494
|
* extend or acquire the primary lease if the client is eligible.
|
|
14944
15495
|
*/
|
|
14945
15496
|
scheduleClientMetadataAndPrimaryLeaseRefreshes() {
|
|
14946
|
-
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
15497
|
+
this.clientMetadataRefresher = this.queue.enqueueAfterDelay("client_metadata_refresh" /* TimerId.ClientMetadataRefresh */, CLIENT_METADATA_REFRESH_INTERVAL_MS, () => {
|
|
14947
15498
|
return this.updateClientMetadataAndTryBecomePrimary()
|
|
14948
15499
|
.then(() => this.maybeGarbageCollectMultiClientState())
|
|
14949
15500
|
.then(() => this.scheduleClientMetadataAndPrimaryLeaseRefreshes());
|
|
@@ -15953,7 +16504,7 @@ function localStoreAllocateTarget(localStore, target) {
|
|
|
15953
16504
|
return localStoreImpl.targetCache
|
|
15954
16505
|
.allocateTargetId(txn)
|
|
15955
16506
|
.next(targetId => {
|
|
15956
|
-
targetData = new TargetData(target, targetId, 0 /* Listen */, txn.currentSequenceNumber);
|
|
16507
|
+
targetData = new TargetData(target, targetId, 0 /* TargetPurpose.Listen */, txn.currentSequenceNumber);
|
|
15957
16508
|
return localStoreImpl.targetCache
|
|
15958
16509
|
.addTargetData(txn, targetData)
|
|
15959
16510
|
.next(() => targetData);
|
|
@@ -16375,11 +16926,11 @@ class QueryEngine {
|
|
|
16375
16926
|
return this.indexManager
|
|
16376
16927
|
.getIndexType(transaction, target)
|
|
16377
16928
|
.next(indexType => {
|
|
16378
|
-
if (indexType === 0 /* NONE */) {
|
|
16929
|
+
if (indexType === 0 /* IndexType.NONE */) {
|
|
16379
16930
|
// The target cannot be served from any index.
|
|
16380
16931
|
return null;
|
|
16381
16932
|
}
|
|
16382
|
-
if (query.limit !== null && indexType === 1 /* PARTIAL */) {
|
|
16933
|
+
if (query.limit !== null && indexType === 1 /* IndexType.PARTIAL */) {
|
|
16383
16934
|
// We cannot apply a limit for targets that are served using a partial
|
|
16384
16935
|
// index. If a partial index will be used to serve the target, the
|
|
16385
16936
|
// query may return a superset of documents that match the target
|
|
@@ -16387,7 +16938,7 @@ class QueryEngine {
|
|
|
16387
16938
|
// may return the correct set of documents in the wrong order (e.g. if
|
|
16388
16939
|
// the index doesn't include a segment for one of the orderBys).
|
|
16389
16940
|
// Therefore, a limit should not be applied in such cases.
|
|
16390
|
-
query = queryWithLimit(query, null, "F" /* First */);
|
|
16941
|
+
query = queryWithLimit(query, null, "F" /* LimitType.First */);
|
|
16391
16942
|
target = queryToTarget(query);
|
|
16392
16943
|
}
|
|
16393
16944
|
return this.indexManager
|
|
@@ -16408,7 +16959,7 @@ class QueryEngine {
|
|
|
16408
16959
|
// query's filters are included in the result set. The SDK
|
|
16409
16960
|
// can then apply the limit once all local edits are
|
|
16410
16961
|
// incorporated.
|
|
16411
|
-
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* First */));
|
|
16962
|
+
return this.performQueryUsingIndex(transaction, queryWithLimit(query, null, "F" /* LimitType.First */));
|
|
16412
16963
|
}
|
|
16413
16964
|
return this.appendRemainingResults(transaction, previousResults, query, offset);
|
|
16414
16965
|
});
|
|
@@ -16487,7 +17038,7 @@ class QueryEngine {
|
|
|
16487
17038
|
// the boundary of the limit itself did not change and documents from cache
|
|
16488
17039
|
// will continue to be "rejected" by this boundary. Therefore, we can ignore
|
|
16489
17040
|
// any modifications that don't affect the last document.
|
|
16490
|
-
const docAtLimitEdge = query.limitType === "F" /* First */
|
|
17041
|
+
const docAtLimitEdge = query.limitType === "F" /* LimitType.First */
|
|
16491
17042
|
? sortedPreviousResults.last()
|
|
16492
17043
|
: sortedPreviousResults.first();
|
|
16493
17044
|
if (!docAtLimitEdge) {
|
|
@@ -17408,7 +17959,7 @@ function nodePromise(action) {
|
|
|
17408
17959
|
*/
|
|
17409
17960
|
// TODO: Fetch runtime version from grpc-js/package.json instead
|
|
17410
17961
|
// when there's a cleaner way to dynamic require JSON in both Node ESM and CJS
|
|
17411
|
-
const grpcVersion = '1.3
|
|
17962
|
+
const grpcVersion = '1.7.3';
|
|
17412
17963
|
const LOG_TAG$9 = 'Connection';
|
|
17413
17964
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
17414
17965
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
@@ -19856,7 +20407,8 @@ const nested = {
|
|
|
19856
20407
|
Operator: {
|
|
19857
20408
|
values: {
|
|
19858
20409
|
OPERATOR_UNSPECIFIED: 0,
|
|
19859
|
-
AND: 1
|
|
20410
|
+
AND: 1,
|
|
20411
|
+
OR: 2
|
|
19860
20412
|
}
|
|
19861
20413
|
}
|
|
19862
20414
|
}
|
|
@@ -20732,7 +21284,7 @@ class PersistentStream {
|
|
|
20732
21284
|
this.authCredentialsProvider = authCredentialsProvider;
|
|
20733
21285
|
this.appCheckCredentialsProvider = appCheckCredentialsProvider;
|
|
20734
21286
|
this.listener = listener;
|
|
20735
|
-
this.state = 0 /* Initial */;
|
|
21287
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20736
21288
|
/**
|
|
20737
21289
|
* A close count that's incremented every time the stream is closed; used by
|
|
20738
21290
|
* getCloseGuardedDispatcher() to invalidate callbacks that happen after
|
|
@@ -20752,8 +21304,8 @@ class PersistentStream {
|
|
|
20752
21304
|
* outbound requests.
|
|
20753
21305
|
*/
|
|
20754
21306
|
isStarted() {
|
|
20755
|
-
return (this.state === 1 /* Starting */ ||
|
|
20756
|
-
this.state === 5 /* Backoff */ ||
|
|
21307
|
+
return (this.state === 1 /* PersistentStreamState.Starting */ ||
|
|
21308
|
+
this.state === 5 /* PersistentStreamState.Backoff */ ||
|
|
20757
21309
|
this.isOpen());
|
|
20758
21310
|
}
|
|
20759
21311
|
/**
|
|
@@ -20761,8 +21313,8 @@ class PersistentStream {
|
|
|
20761
21313
|
* called) and the stream is ready for outbound requests.
|
|
20762
21314
|
*/
|
|
20763
21315
|
isOpen() {
|
|
20764
|
-
return (this.state === 2 /* Open */ ||
|
|
20765
|
-
this.state === 3 /* Healthy */);
|
|
21316
|
+
return (this.state === 2 /* PersistentStreamState.Open */ ||
|
|
21317
|
+
this.state === 3 /* PersistentStreamState.Healthy */);
|
|
20766
21318
|
}
|
|
20767
21319
|
/**
|
|
20768
21320
|
* Starts the RPC. Only allowed if isStarted() returns false. The stream is
|
|
@@ -20772,7 +21324,7 @@ class PersistentStream {
|
|
|
20772
21324
|
* When start returns, isStarted() will return true.
|
|
20773
21325
|
*/
|
|
20774
21326
|
start() {
|
|
20775
|
-
if (this.state === 4 /* Error */) {
|
|
21327
|
+
if (this.state === 4 /* PersistentStreamState.Error */) {
|
|
20776
21328
|
this.performBackoff();
|
|
20777
21329
|
return;
|
|
20778
21330
|
}
|
|
@@ -20786,7 +21338,7 @@ class PersistentStream {
|
|
|
20786
21338
|
*/
|
|
20787
21339
|
async stop() {
|
|
20788
21340
|
if (this.isStarted()) {
|
|
20789
|
-
await this.close(0 /* Initial */);
|
|
21341
|
+
await this.close(0 /* PersistentStreamState.Initial */);
|
|
20790
21342
|
}
|
|
20791
21343
|
}
|
|
20792
21344
|
/**
|
|
@@ -20798,7 +21350,7 @@ class PersistentStream {
|
|
|
20798
21350
|
* inhibit backoff if required.
|
|
20799
21351
|
*/
|
|
20800
21352
|
inhibitBackoff() {
|
|
20801
|
-
this.state = 0 /* Initial */;
|
|
21353
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20802
21354
|
this.backoff.reset();
|
|
20803
21355
|
}
|
|
20804
21356
|
/**
|
|
@@ -20828,7 +21380,7 @@ class PersistentStream {
|
|
|
20828
21380
|
if (this.isOpen()) {
|
|
20829
21381
|
// When timing out an idle stream there's no reason to force the stream into backoff when
|
|
20830
21382
|
// it restarts so set the stream state to Initial instead of Error.
|
|
20831
|
-
return this.close(0 /* Initial */);
|
|
21383
|
+
return this.close(0 /* PersistentStreamState.Initial */);
|
|
20832
21384
|
}
|
|
20833
21385
|
}
|
|
20834
21386
|
/** Marks the stream as active again. */
|
|
@@ -20866,7 +21418,7 @@ class PersistentStream {
|
|
|
20866
21418
|
// Invalidates any stream-related callbacks (e.g. from auth or the
|
|
20867
21419
|
// underlying stream), guaranteeing they won't execute.
|
|
20868
21420
|
this.closeCount++;
|
|
20869
|
-
if (finalState !== 4 /* Error */) {
|
|
21421
|
+
if (finalState !== 4 /* PersistentStreamState.Error */) {
|
|
20870
21422
|
// If this is an intentional close ensure we don't delay our next connection attempt.
|
|
20871
21423
|
this.backoff.reset();
|
|
20872
21424
|
}
|
|
@@ -20878,7 +21430,7 @@ class PersistentStream {
|
|
|
20878
21430
|
}
|
|
20879
21431
|
else if (error &&
|
|
20880
21432
|
error.code === Code.UNAUTHENTICATED &&
|
|
20881
|
-
this.state !== 3 /* Healthy */) {
|
|
21433
|
+
this.state !== 3 /* PersistentStreamState.Healthy */) {
|
|
20882
21434
|
// "unauthenticated" error means the token was rejected. This should rarely
|
|
20883
21435
|
// happen since both Auth and AppCheck ensure a sufficient TTL when we
|
|
20884
21436
|
// request a token. If a user manually resets their system clock this can
|
|
@@ -20906,7 +21458,7 @@ class PersistentStream {
|
|
|
20906
21458
|
*/
|
|
20907
21459
|
tearDown() { }
|
|
20908
21460
|
auth() {
|
|
20909
|
-
this.state = 1 /* Starting */;
|
|
21461
|
+
this.state = 1 /* PersistentStreamState.Starting */;
|
|
20910
21462
|
const dispatchIfNotClosed = this.getCloseGuardedDispatcher(this.closeCount);
|
|
20911
21463
|
// TODO(mikelehen): Just use dispatchIfNotClosed, but see TODO below.
|
|
20912
21464
|
const closeCount = this.closeCount;
|
|
@@ -20936,10 +21488,10 @@ class PersistentStream {
|
|
|
20936
21488
|
this.stream = this.startRpc(authToken, appCheckToken);
|
|
20937
21489
|
this.stream.onOpen(() => {
|
|
20938
21490
|
dispatchIfNotClosed(() => {
|
|
20939
|
-
this.state = 2 /* Open */;
|
|
21491
|
+
this.state = 2 /* PersistentStreamState.Open */;
|
|
20940
21492
|
this.healthCheck = this.queue.enqueueAfterDelay(this.healthTimerId, HEALTHY_TIMEOUT_MS, () => {
|
|
20941
21493
|
if (this.isOpen()) {
|
|
20942
|
-
this.state = 3 /* Healthy */;
|
|
21494
|
+
this.state = 3 /* PersistentStreamState.Healthy */;
|
|
20943
21495
|
}
|
|
20944
21496
|
return Promise.resolve();
|
|
20945
21497
|
});
|
|
@@ -20958,9 +21510,9 @@ class PersistentStream {
|
|
|
20958
21510
|
});
|
|
20959
21511
|
}
|
|
20960
21512
|
performBackoff() {
|
|
20961
|
-
this.state = 5 /* Backoff */;
|
|
21513
|
+
this.state = 5 /* PersistentStreamState.Backoff */;
|
|
20962
21514
|
this.backoff.backoffAndRun(async () => {
|
|
20963
|
-
this.state = 0 /* Initial */;
|
|
21515
|
+
this.state = 0 /* PersistentStreamState.Initial */;
|
|
20964
21516
|
this.start();
|
|
20965
21517
|
});
|
|
20966
21518
|
}
|
|
@@ -20972,7 +21524,7 @@ class PersistentStream {
|
|
|
20972
21524
|
// we never expect this to happen because if we stop a stream ourselves,
|
|
20973
21525
|
// this callback will never be called. To prevent cases where we retry
|
|
20974
21526
|
// without a backoff accidentally, we set the stream to error in all cases.
|
|
20975
|
-
return this.close(4 /* Error */, error);
|
|
21527
|
+
return this.close(4 /* PersistentStreamState.Error */, error);
|
|
20976
21528
|
}
|
|
20977
21529
|
/**
|
|
20978
21530
|
* Returns a "dispatcher" function that dispatches operations onto the
|
|
@@ -21003,7 +21555,7 @@ class PersistentStream {
|
|
|
21003
21555
|
*/
|
|
21004
21556
|
class PersistentListenStream extends PersistentStream {
|
|
21005
21557
|
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);
|
|
21558
|
+
super(queue, "listen_stream_connection_backoff" /* TimerId.ListenStreamConnectionBackoff */, "listen_stream_idle" /* TimerId.ListenStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21007
21559
|
this.serializer = serializer;
|
|
21008
21560
|
}
|
|
21009
21561
|
startRpc(authToken, appCheckToken) {
|
|
@@ -21062,7 +21614,7 @@ class PersistentListenStream extends PersistentStream {
|
|
|
21062
21614
|
*/
|
|
21063
21615
|
class PersistentWriteStream extends PersistentStream {
|
|
21064
21616
|
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);
|
|
21617
|
+
super(queue, "write_stream_connection_backoff" /* TimerId.WriteStreamConnectionBackoff */, "write_stream_idle" /* TimerId.WriteStreamIdle */, "health_check_timeout" /* TimerId.HealthCheckTimeout */, connection, authCredentials, appCheckCredentials, listener);
|
|
21066
21618
|
this.serializer = serializer;
|
|
21067
21619
|
this.handshakeComplete_ = false;
|
|
21068
21620
|
}
|
|
@@ -21321,7 +21873,7 @@ class OnlineStateTracker {
|
|
|
21321
21873
|
this.asyncQueue = asyncQueue;
|
|
21322
21874
|
this.onlineStateHandler = onlineStateHandler;
|
|
21323
21875
|
/** The current OnlineState. */
|
|
21324
|
-
this.state = "Unknown" /* Unknown */;
|
|
21876
|
+
this.state = "Unknown" /* OnlineState.Unknown */;
|
|
21325
21877
|
/**
|
|
21326
21878
|
* A count of consecutive failures to open the stream. If it reaches the
|
|
21327
21879
|
* maximum defined by MAX_WATCH_STREAM_FAILURES, we'll set the OnlineState to
|
|
@@ -21350,12 +21902,12 @@ class OnlineStateTracker {
|
|
|
21350
21902
|
*/
|
|
21351
21903
|
handleWatchStreamStart() {
|
|
21352
21904
|
if (this.watchStreamFailures === 0) {
|
|
21353
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21354
|
-
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21905
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21906
|
+
this.onlineStateTimer = this.asyncQueue.enqueueAfterDelay("online_state_timeout" /* TimerId.OnlineStateTimeout */, ONLINE_STATE_TIMEOUT_MS, () => {
|
|
21355
21907
|
this.onlineStateTimer = null;
|
|
21356
21908
|
this.logClientOfflineWarningIfNecessary(`Backend didn't respond within ${ONLINE_STATE_TIMEOUT_MS / 1000} ` +
|
|
21357
21909
|
`seconds.`);
|
|
21358
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21910
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21359
21911
|
// NOTE: handleWatchStreamFailure() will continue to increment
|
|
21360
21912
|
// watchStreamFailures even though we are already marked Offline,
|
|
21361
21913
|
// but this is non-harmful.
|
|
@@ -21370,8 +21922,8 @@ class OnlineStateTracker {
|
|
|
21370
21922
|
* actually transition to the 'Offline' state.
|
|
21371
21923
|
*/
|
|
21372
21924
|
handleWatchStreamFailure(error) {
|
|
21373
|
-
if (this.state === "Online" /* Online */) {
|
|
21374
|
-
this.setAndBroadcast("Unknown" /* Unknown */);
|
|
21925
|
+
if (this.state === "Online" /* OnlineState.Online */) {
|
|
21926
|
+
this.setAndBroadcast("Unknown" /* OnlineState.Unknown */);
|
|
21375
21927
|
}
|
|
21376
21928
|
else {
|
|
21377
21929
|
this.watchStreamFailures++;
|
|
@@ -21379,7 +21931,7 @@ class OnlineStateTracker {
|
|
|
21379
21931
|
this.clearOnlineStateTimer();
|
|
21380
21932
|
this.logClientOfflineWarningIfNecessary(`Connection failed ${MAX_WATCH_STREAM_FAILURES} ` +
|
|
21381
21933
|
`times. Most recent error: ${error.toString()}`);
|
|
21382
|
-
this.setAndBroadcast("Offline" /* Offline */);
|
|
21934
|
+
this.setAndBroadcast("Offline" /* OnlineState.Offline */);
|
|
21383
21935
|
}
|
|
21384
21936
|
}
|
|
21385
21937
|
}
|
|
@@ -21393,7 +21945,7 @@ class OnlineStateTracker {
|
|
|
21393
21945
|
set(newState) {
|
|
21394
21946
|
this.clearOnlineStateTimer();
|
|
21395
21947
|
this.watchStreamFailures = 0;
|
|
21396
|
-
if (newState === "Online" /* Online */) {
|
|
21948
|
+
if (newState === "Online" /* OnlineState.Online */) {
|
|
21397
21949
|
// We've connected to watch at least once. Don't warn the developer
|
|
21398
21950
|
// about being offline going forward.
|
|
21399
21951
|
this.shouldWarnClientIsOffline = false;
|
|
@@ -21520,7 +22072,7 @@ function newRemoteStore(localStore, datastore, asyncQueue, onlineStateHandler, c
|
|
|
21520
22072
|
/** Re-enables the network. Idempotent. */
|
|
21521
22073
|
function remoteStoreEnableNetwork(remoteStore) {
|
|
21522
22074
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21523
|
-
remoteStoreImpl.offlineCauses.delete(0 /* UserDisabled */);
|
|
22075
|
+
remoteStoreImpl.offlineCauses.delete(0 /* OfflineCause.UserDisabled */);
|
|
21524
22076
|
return enableNetworkInternal(remoteStoreImpl);
|
|
21525
22077
|
}
|
|
21526
22078
|
async function enableNetworkInternal(remoteStoreImpl) {
|
|
@@ -21536,10 +22088,10 @@ async function enableNetworkInternal(remoteStoreImpl) {
|
|
|
21536
22088
|
*/
|
|
21537
22089
|
async function remoteStoreDisableNetwork(remoteStore) {
|
|
21538
22090
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21539
|
-
remoteStoreImpl.offlineCauses.add(0 /* UserDisabled */);
|
|
22091
|
+
remoteStoreImpl.offlineCauses.add(0 /* OfflineCause.UserDisabled */);
|
|
21540
22092
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21541
22093
|
// Set the OnlineState to Offline so get()s return from cache, etc.
|
|
21542
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22094
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21543
22095
|
}
|
|
21544
22096
|
async function disableNetworkInternal(remoteStoreImpl) {
|
|
21545
22097
|
for (const networkStatusHandler of remoteStoreImpl.onNetworkStatusChange) {
|
|
@@ -21549,12 +22101,12 @@ async function disableNetworkInternal(remoteStoreImpl) {
|
|
|
21549
22101
|
async function remoteStoreShutdown(remoteStore) {
|
|
21550
22102
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21551
22103
|
logDebug(LOG_TAG$5, 'RemoteStore shutting down.');
|
|
21552
|
-
remoteStoreImpl.offlineCauses.add(5 /* Shutdown */);
|
|
22104
|
+
remoteStoreImpl.offlineCauses.add(5 /* OfflineCause.Shutdown */);
|
|
21553
22105
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21554
22106
|
remoteStoreImpl.connectivityMonitor.shutdown();
|
|
21555
22107
|
// Set the OnlineState to Unknown (rather than Offline) to avoid potentially
|
|
21556
22108
|
// triggering spurious listener events with cached data, etc.
|
|
21557
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22109
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21558
22110
|
}
|
|
21559
22111
|
/**
|
|
21560
22112
|
* Starts new listen for the given target. Uses resume token if provided. It
|
|
@@ -21594,7 +22146,7 @@ function remoteStoreUnlisten(remoteStore, targetId) {
|
|
|
21594
22146
|
// Revert to OnlineState.Unknown if the watch stream is not open and we
|
|
21595
22147
|
// have no listeners, since without any listens to send we cannot
|
|
21596
22148
|
// confirm if the stream is healthy and upgrade to OnlineState.Online.
|
|
21597
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22149
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21598
22150
|
}
|
|
21599
22151
|
}
|
|
21600
22152
|
}
|
|
@@ -21655,14 +22207,14 @@ async function onWatchStreamClose(remoteStoreImpl, error) {
|
|
|
21655
22207
|
// No need to restart watch stream because there are no active targets.
|
|
21656
22208
|
// The online state is set to unknown because there is no active attempt
|
|
21657
22209
|
// at establishing a connection
|
|
21658
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22210
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21659
22211
|
}
|
|
21660
22212
|
}
|
|
21661
22213
|
async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion) {
|
|
21662
22214
|
// Mark the client as online since we got a message from the server
|
|
21663
|
-
remoteStoreImpl.onlineStateTracker.set("Online" /* Online */);
|
|
22215
|
+
remoteStoreImpl.onlineStateTracker.set("Online" /* OnlineState.Online */);
|
|
21664
22216
|
if (watchChange instanceof WatchTargetChange &&
|
|
21665
|
-
watchChange.state === 2 /* Removed */ &&
|
|
22217
|
+
watchChange.state === 2 /* WatchTargetChangeState.Removed */ &&
|
|
21666
22218
|
watchChange.cause) {
|
|
21667
22219
|
// There was an error on a target, don't wait for a consistent snapshot
|
|
21668
22220
|
// to raise events
|
|
@@ -21710,10 +22262,10 @@ async function onWatchStreamChange(remoteStoreImpl, watchChange, snapshotVersion
|
|
|
21710
22262
|
*/
|
|
21711
22263
|
async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
21712
22264
|
if (isIndexedDbTransactionError(e)) {
|
|
21713
|
-
remoteStoreImpl.offlineCauses.add(1 /* IndexedDbFailed */);
|
|
22265
|
+
remoteStoreImpl.offlineCauses.add(1 /* OfflineCause.IndexedDbFailed */);
|
|
21714
22266
|
// Disable network and raise offline snapshots
|
|
21715
22267
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21716
|
-
remoteStoreImpl.onlineStateTracker.set("Offline" /* Offline */);
|
|
22268
|
+
remoteStoreImpl.onlineStateTracker.set("Offline" /* OnlineState.Offline */);
|
|
21717
22269
|
if (!op) {
|
|
21718
22270
|
// Use a simple read operation to determine if IndexedDB recovered.
|
|
21719
22271
|
// Ideally, we would expose a health check directly on SimpleDb, but
|
|
@@ -21724,7 +22276,7 @@ async function disableNetworkUntilRecovery(remoteStoreImpl, e, op) {
|
|
|
21724
22276
|
remoteStoreImpl.asyncQueue.enqueueRetryable(async () => {
|
|
21725
22277
|
logDebug(LOG_TAG$5, 'Retrying IndexedDB access');
|
|
21726
22278
|
await op();
|
|
21727
|
-
remoteStoreImpl.offlineCauses.delete(1 /* IndexedDbFailed */);
|
|
22279
|
+
remoteStoreImpl.offlineCauses.delete(1 /* OfflineCause.IndexedDbFailed */);
|
|
21728
22280
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21729
22281
|
});
|
|
21730
22282
|
}
|
|
@@ -21775,7 +22327,7 @@ function raiseWatchSnapshot(remoteStoreImpl, snapshotVersion) {
|
|
|
21775
22327
|
// mismatch, but don't actually retain that in listenTargets. This ensures
|
|
21776
22328
|
// that we flag the first re-listen this way without impacting future
|
|
21777
22329
|
// listens of this target (that might happen e.g. on reconnect).
|
|
21778
|
-
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
22330
|
+
const requestTargetData = new TargetData(targetData.target, targetId, 1 /* TargetPurpose.ExistenceFilterMismatch */, targetData.sequenceNumber);
|
|
21779
22331
|
sendWatchRequest(remoteStoreImpl, requestTargetData);
|
|
21780
22332
|
});
|
|
21781
22333
|
return remoteStoreImpl.remoteSyncer.applyRemoteEvent(remoteEvent);
|
|
@@ -21906,10 +22458,10 @@ async function handleWriteError(remoteStoreImpl, error) {
|
|
|
21906
22458
|
}
|
|
21907
22459
|
async function restartNetwork(remoteStore) {
|
|
21908
22460
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21909
|
-
remoteStoreImpl.offlineCauses.add(4 /* ConnectivityChange */);
|
|
22461
|
+
remoteStoreImpl.offlineCauses.add(4 /* OfflineCause.ConnectivityChange */);
|
|
21910
22462
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21911
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
21912
|
-
remoteStoreImpl.offlineCauses.delete(4 /* ConnectivityChange */);
|
|
22463
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
22464
|
+
remoteStoreImpl.offlineCauses.delete(4 /* OfflineCause.ConnectivityChange */);
|
|
21913
22465
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21914
22466
|
}
|
|
21915
22467
|
async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
@@ -21920,14 +22472,14 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21920
22472
|
// Tear down and re-create our network streams. This will ensure we get a
|
|
21921
22473
|
// fresh auth token for the new user and re-fill the write pipeline with
|
|
21922
22474
|
// new mutations from the LocalStore (since mutations are per-user).
|
|
21923
|
-
remoteStoreImpl.offlineCauses.add(3 /* CredentialChange */);
|
|
22475
|
+
remoteStoreImpl.offlineCauses.add(3 /* OfflineCause.CredentialChange */);
|
|
21924
22476
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21925
22477
|
if (usesNetwork) {
|
|
21926
22478
|
// Don't set the network status to Unknown if we are offline.
|
|
21927
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22479
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21928
22480
|
}
|
|
21929
22481
|
await remoteStoreImpl.remoteSyncer.handleCredentialChange(user);
|
|
21930
|
-
remoteStoreImpl.offlineCauses.delete(3 /* CredentialChange */);
|
|
22482
|
+
remoteStoreImpl.offlineCauses.delete(3 /* OfflineCause.CredentialChange */);
|
|
21931
22483
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21932
22484
|
}
|
|
21933
22485
|
/**
|
|
@@ -21936,13 +22488,13 @@ async function remoteStoreHandleCredentialChange(remoteStore, user) {
|
|
|
21936
22488
|
async function remoteStoreApplyPrimaryState(remoteStore, isPrimary) {
|
|
21937
22489
|
const remoteStoreImpl = debugCast(remoteStore);
|
|
21938
22490
|
if (isPrimary) {
|
|
21939
|
-
remoteStoreImpl.offlineCauses.delete(2 /* IsSecondary */);
|
|
22491
|
+
remoteStoreImpl.offlineCauses.delete(2 /* OfflineCause.IsSecondary */);
|
|
21940
22492
|
await enableNetworkInternal(remoteStoreImpl);
|
|
21941
22493
|
}
|
|
21942
22494
|
else if (!isPrimary) {
|
|
21943
|
-
remoteStoreImpl.offlineCauses.add(2 /* IsSecondary */);
|
|
22495
|
+
remoteStoreImpl.offlineCauses.add(2 /* OfflineCause.IsSecondary */);
|
|
21944
22496
|
await disableNetworkInternal(remoteStoreImpl);
|
|
21945
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22497
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21946
22498
|
}
|
|
21947
22499
|
}
|
|
21948
22500
|
/**
|
|
@@ -21968,7 +22520,7 @@ function ensureWatchStream(remoteStoreImpl) {
|
|
|
21968
22520
|
startWatchStream(remoteStoreImpl);
|
|
21969
22521
|
}
|
|
21970
22522
|
else {
|
|
21971
|
-
remoteStoreImpl.onlineStateTracker.set("Unknown" /* Unknown */);
|
|
22523
|
+
remoteStoreImpl.onlineStateTracker.set("Unknown" /* OnlineState.Unknown */);
|
|
21972
22524
|
}
|
|
21973
22525
|
}
|
|
21974
22526
|
else {
|
|
@@ -22299,46 +22851,46 @@ class DocumentChangeSet {
|
|
|
22299
22851
|
return;
|
|
22300
22852
|
}
|
|
22301
22853
|
// Merge the new change with the existing change.
|
|
22302
|
-
if (change.type !== 0 /* Added */ &&
|
|
22303
|
-
oldChange.type === 3 /* Metadata */) {
|
|
22854
|
+
if (change.type !== 0 /* ChangeType.Added */ &&
|
|
22855
|
+
oldChange.type === 3 /* ChangeType.Metadata */) {
|
|
22304
22856
|
this.changeMap = this.changeMap.insert(key, change);
|
|
22305
22857
|
}
|
|
22306
|
-
else if (change.type === 3 /* Metadata */ &&
|
|
22307
|
-
oldChange.type !== 1 /* Removed */) {
|
|
22858
|
+
else if (change.type === 3 /* ChangeType.Metadata */ &&
|
|
22859
|
+
oldChange.type !== 1 /* ChangeType.Removed */) {
|
|
22308
22860
|
this.changeMap = this.changeMap.insert(key, {
|
|
22309
22861
|
type: oldChange.type,
|
|
22310
22862
|
doc: change.doc
|
|
22311
22863
|
});
|
|
22312
22864
|
}
|
|
22313
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22314
|
-
oldChange.type === 2 /* Modified */) {
|
|
22865
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22866
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
22315
22867
|
this.changeMap = this.changeMap.insert(key, {
|
|
22316
|
-
type: 2 /* Modified */,
|
|
22868
|
+
type: 2 /* ChangeType.Modified */,
|
|
22317
22869
|
doc: change.doc
|
|
22318
22870
|
});
|
|
22319
22871
|
}
|
|
22320
|
-
else if (change.type === 2 /* Modified */ &&
|
|
22321
|
-
oldChange.type === 0 /* Added */) {
|
|
22872
|
+
else if (change.type === 2 /* ChangeType.Modified */ &&
|
|
22873
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22322
22874
|
this.changeMap = this.changeMap.insert(key, {
|
|
22323
|
-
type: 0 /* Added */,
|
|
22875
|
+
type: 0 /* ChangeType.Added */,
|
|
22324
22876
|
doc: change.doc
|
|
22325
22877
|
});
|
|
22326
22878
|
}
|
|
22327
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22328
|
-
oldChange.type === 0 /* Added */) {
|
|
22879
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22880
|
+
oldChange.type === 0 /* ChangeType.Added */) {
|
|
22329
22881
|
this.changeMap = this.changeMap.remove(key);
|
|
22330
22882
|
}
|
|
22331
|
-
else if (change.type === 1 /* Removed */ &&
|
|
22332
|
-
oldChange.type === 2 /* Modified */) {
|
|
22883
|
+
else if (change.type === 1 /* ChangeType.Removed */ &&
|
|
22884
|
+
oldChange.type === 2 /* ChangeType.Modified */) {
|
|
22333
22885
|
this.changeMap = this.changeMap.insert(key, {
|
|
22334
|
-
type: 1 /* Removed */,
|
|
22886
|
+
type: 1 /* ChangeType.Removed */,
|
|
22335
22887
|
doc: oldChange.doc
|
|
22336
22888
|
});
|
|
22337
22889
|
}
|
|
22338
|
-
else if (change.type === 0 /* Added */ &&
|
|
22339
|
-
oldChange.type === 1 /* Removed */) {
|
|
22890
|
+
else if (change.type === 0 /* ChangeType.Added */ &&
|
|
22891
|
+
oldChange.type === 1 /* ChangeType.Removed */) {
|
|
22340
22892
|
this.changeMap = this.changeMap.insert(key, {
|
|
22341
|
-
type: 2 /* Modified */,
|
|
22893
|
+
type: 2 /* ChangeType.Modified */,
|
|
22342
22894
|
doc: change.doc
|
|
22343
22895
|
});
|
|
22344
22896
|
}
|
|
@@ -22377,7 +22929,7 @@ class ViewSnapshot {
|
|
|
22377
22929
|
static fromInitialDocuments(query, documents, mutatedKeys, fromCache, hasCachedResults) {
|
|
22378
22930
|
const changes = [];
|
|
22379
22931
|
documents.forEach(doc => {
|
|
22380
|
-
changes.push({ type: 0 /* Added */, doc });
|
|
22932
|
+
changes.push({ type: 0 /* ChangeType.Added */, doc });
|
|
22381
22933
|
});
|
|
22382
22934
|
return new ViewSnapshot(query, documents, DocumentSet.emptySet(documents), changes, mutatedKeys, fromCache,
|
|
22383
22935
|
/* syncStateChanged= */ true,
|
|
@@ -22443,7 +22995,7 @@ function newEventManager() {
|
|
|
22443
22995
|
class EventManagerImpl {
|
|
22444
22996
|
constructor() {
|
|
22445
22997
|
this.queries = new ObjectMap(q => canonifyQuery(q), queryEquals);
|
|
22446
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
22998
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22447
22999
|
this.snapshotsInSyncListeners = new Set();
|
|
22448
23000
|
}
|
|
22449
23001
|
}
|
|
@@ -22574,7 +23126,7 @@ class QueryListener {
|
|
|
22574
23126
|
*/
|
|
22575
23127
|
this.raisedInitialEvent = false;
|
|
22576
23128
|
this.snap = null;
|
|
22577
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23129
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
22578
23130
|
this.options = options || {};
|
|
22579
23131
|
}
|
|
22580
23132
|
/**
|
|
@@ -22588,7 +23140,7 @@ class QueryListener {
|
|
|
22588
23140
|
// Remove the metadata only changes.
|
|
22589
23141
|
const docChanges = [];
|
|
22590
23142
|
for (const docChange of snap.docChanges) {
|
|
22591
|
-
if (docChange.type !== 3 /* Metadata */) {
|
|
23143
|
+
if (docChange.type !== 3 /* ChangeType.Metadata */) {
|
|
22592
23144
|
docChanges.push(docChange);
|
|
22593
23145
|
}
|
|
22594
23146
|
}
|
|
@@ -22631,7 +23183,7 @@ class QueryListener {
|
|
|
22631
23183
|
}
|
|
22632
23184
|
// NOTE: We consider OnlineState.Unknown as online (it should become Offline
|
|
22633
23185
|
// or Online if we wait long enough).
|
|
22634
|
-
const maybeOnline = onlineState !== "Offline" /* Offline */;
|
|
23186
|
+
const maybeOnline = onlineState !== "Offline" /* OnlineState.Offline */;
|
|
22635
23187
|
// Don't raise the event if we're online, aren't synced yet (checked
|
|
22636
23188
|
// above) and are waiting for a sync.
|
|
22637
23189
|
if (this.options.waitForSyncWhenOnline && maybeOnline) {
|
|
@@ -22641,7 +23193,7 @@ class QueryListener {
|
|
|
22641
23193
|
// or we are offline.
|
|
22642
23194
|
return (!snap.docs.isEmpty() ||
|
|
22643
23195
|
snap.hasCachedResults ||
|
|
22644
|
-
onlineState === "Offline" /* Offline */);
|
|
23196
|
+
onlineState === "Offline" /* OnlineState.Offline */);
|
|
22645
23197
|
}
|
|
22646
23198
|
shouldRaiseEvent(snap) {
|
|
22647
23199
|
// We don't need to handle includeDocumentMetadataChanges here because
|
|
@@ -22700,10 +23252,10 @@ class LocalViewChanges {
|
|
|
22700
23252
|
let removedKeys = documentKeySet();
|
|
22701
23253
|
for (const docChange of viewSnapshot.docChanges) {
|
|
22702
23254
|
switch (docChange.type) {
|
|
22703
|
-
case 0 /* Added */:
|
|
23255
|
+
case 0 /* ChangeType.Added */:
|
|
22704
23256
|
addedKeys = addedKeys.add(docChange.doc.key);
|
|
22705
23257
|
break;
|
|
22706
|
-
case 1 /* Removed */:
|
|
23258
|
+
case 1 /* ChangeType.Removed */:
|
|
22707
23259
|
removedKeys = removedKeys.add(docChange.doc.key);
|
|
22708
23260
|
break;
|
|
22709
23261
|
// do nothing
|
|
@@ -22951,11 +23503,11 @@ class View {
|
|
|
22951
23503
|
//
|
|
22952
23504
|
// Note that this should never get used in a refill (when previousChanges is
|
|
22953
23505
|
// set), because there will only be adds -- no deletes or updates.
|
|
22954
|
-
const lastDocInLimit = this.query.limitType === "F" /* First */ &&
|
|
23506
|
+
const lastDocInLimit = this.query.limitType === "F" /* LimitType.First */ &&
|
|
22955
23507
|
oldDocumentSet.size === this.query.limit
|
|
22956
23508
|
? oldDocumentSet.last()
|
|
22957
23509
|
: null;
|
|
22958
|
-
const firstDocInLimit = this.query.limitType === "L" /* Last */ &&
|
|
23510
|
+
const firstDocInLimit = this.query.limitType === "L" /* LimitType.Last */ &&
|
|
22959
23511
|
oldDocumentSet.size === this.query.limit
|
|
22960
23512
|
? oldDocumentSet.first()
|
|
22961
23513
|
: null;
|
|
@@ -22978,7 +23530,7 @@ class View {
|
|
|
22978
23530
|
if (!docsEqual) {
|
|
22979
23531
|
if (!this.shouldWaitForSyncedDocument(oldDoc, newDoc)) {
|
|
22980
23532
|
changeSet.track({
|
|
22981
|
-
type: 2 /* Modified */,
|
|
23533
|
+
type: 2 /* ChangeType.Modified */,
|
|
22982
23534
|
doc: newDoc
|
|
22983
23535
|
});
|
|
22984
23536
|
changeApplied = true;
|
|
@@ -22994,16 +23546,16 @@ class View {
|
|
|
22994
23546
|
}
|
|
22995
23547
|
}
|
|
22996
23548
|
else if (oldDocHadPendingMutations !== newDocHasPendingMutations) {
|
|
22997
|
-
changeSet.track({ type: 3 /* Metadata */, doc: newDoc });
|
|
23549
|
+
changeSet.track({ type: 3 /* ChangeType.Metadata */, doc: newDoc });
|
|
22998
23550
|
changeApplied = true;
|
|
22999
23551
|
}
|
|
23000
23552
|
}
|
|
23001
23553
|
else if (!oldDoc && newDoc) {
|
|
23002
|
-
changeSet.track({ type: 0 /* Added */, doc: newDoc });
|
|
23554
|
+
changeSet.track({ type: 0 /* ChangeType.Added */, doc: newDoc });
|
|
23003
23555
|
changeApplied = true;
|
|
23004
23556
|
}
|
|
23005
23557
|
else if (oldDoc && !newDoc) {
|
|
23006
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23558
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
23007
23559
|
changeApplied = true;
|
|
23008
23560
|
if (lastDocInLimit || firstDocInLimit) {
|
|
23009
23561
|
// A doc was removed from a full limit query. We'll need to
|
|
@@ -23031,12 +23583,12 @@ class View {
|
|
|
23031
23583
|
// Drop documents out to meet limit/limitToLast requirement.
|
|
23032
23584
|
if (this.query.limit !== null) {
|
|
23033
23585
|
while (newDocumentSet.size > this.query.limit) {
|
|
23034
|
-
const oldDoc = this.query.limitType === "F" /* First */
|
|
23586
|
+
const oldDoc = this.query.limitType === "F" /* LimitType.First */
|
|
23035
23587
|
? newDocumentSet.last()
|
|
23036
23588
|
: newDocumentSet.first();
|
|
23037
23589
|
newDocumentSet = newDocumentSet.delete(oldDoc.key);
|
|
23038
23590
|
newMutatedKeys = newMutatedKeys.delete(oldDoc.key);
|
|
23039
|
-
changeSet.track({ type: 1 /* Removed */, doc: oldDoc });
|
|
23591
|
+
changeSet.track({ type: 1 /* ChangeType.Removed */, doc: oldDoc });
|
|
23040
23592
|
}
|
|
23041
23593
|
}
|
|
23042
23594
|
return {
|
|
@@ -23084,7 +23636,7 @@ class View {
|
|
|
23084
23636
|
? this.updateLimboDocuments()
|
|
23085
23637
|
: [];
|
|
23086
23638
|
const synced = this.limboDocuments.size === 0 && this.current;
|
|
23087
|
-
const newSyncState = synced ? 1 /* Synced */ : 0 /* Local */;
|
|
23639
|
+
const newSyncState = synced ? 1 /* SyncState.Synced */ : 0 /* SyncState.Local */;
|
|
23088
23640
|
const syncStateChanged = newSyncState !== this.syncState;
|
|
23089
23641
|
this.syncState = newSyncState;
|
|
23090
23642
|
if (changes.length === 0 && !syncStateChanged) {
|
|
@@ -23092,7 +23644,7 @@ class View {
|
|
|
23092
23644
|
return { limboChanges };
|
|
23093
23645
|
}
|
|
23094
23646
|
else {
|
|
23095
|
-
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* Local */, syncStateChanged,
|
|
23647
|
+
const snap = new ViewSnapshot(this.query, docChanges.documentSet, oldDocs, changes, docChanges.mutatedKeys, newSyncState === 0 /* SyncState.Local */, syncStateChanged,
|
|
23096
23648
|
/* excludesMetadataChanges= */ false, targetChange
|
|
23097
23649
|
? targetChange.resumeToken.approximateByteSize() > 0
|
|
23098
23650
|
: false);
|
|
@@ -23107,7 +23659,7 @@ class View {
|
|
|
23107
23659
|
* ViewChange if the view's syncState changes as a result.
|
|
23108
23660
|
*/
|
|
23109
23661
|
applyOnlineStateChange(onlineState) {
|
|
23110
|
-
if (this.current && onlineState === "Offline" /* Offline */) {
|
|
23662
|
+
if (this.current && onlineState === "Offline" /* OnlineState.Offline */) {
|
|
23111
23663
|
// If we're offline, set `current` to false and then call applyChanges()
|
|
23112
23664
|
// to refresh our syncState and generate a ViewChange as appropriate. We
|
|
23113
23665
|
// are guaranteed to get a new TargetChange that sets `current` back to
|
|
@@ -23222,22 +23774,22 @@ class View {
|
|
|
23222
23774
|
*/
|
|
23223
23775
|
// PORTING NOTE: Multi-tab only.
|
|
23224
23776
|
computeInitialSnapshot() {
|
|
23225
|
-
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* Local */, this.hasCachedResults);
|
|
23777
|
+
return ViewSnapshot.fromInitialDocuments(this.query, this.documentSet, this.mutatedKeys, this.syncState === 0 /* SyncState.Local */, this.hasCachedResults);
|
|
23226
23778
|
}
|
|
23227
23779
|
}
|
|
23228
23780
|
function compareChangeType(c1, c2) {
|
|
23229
23781
|
const order = (change) => {
|
|
23230
23782
|
switch (change) {
|
|
23231
|
-
case 0 /* Added */:
|
|
23783
|
+
case 0 /* ChangeType.Added */:
|
|
23232
23784
|
return 1;
|
|
23233
|
-
case 2 /* Modified */:
|
|
23785
|
+
case 2 /* ChangeType.Modified */:
|
|
23234
23786
|
return 2;
|
|
23235
|
-
case 3 /* Metadata */:
|
|
23787
|
+
case 3 /* ChangeType.Metadata */:
|
|
23236
23788
|
// A metadata change is converted to a modified change at the public
|
|
23237
23789
|
// api layer. Since we sort by document key and then change type,
|
|
23238
23790
|
// metadata and modified changes must be sorted equivalently.
|
|
23239
23791
|
return 2;
|
|
23240
|
-
case 1 /* Removed */:
|
|
23792
|
+
case 1 /* ChangeType.Removed */:
|
|
23241
23793
|
return 0;
|
|
23242
23794
|
default:
|
|
23243
23795
|
return fail();
|
|
@@ -23355,7 +23907,7 @@ class SyncEngineImpl {
|
|
|
23355
23907
|
/** Stores user callbacks waiting for all pending writes to be acknowledged. */
|
|
23356
23908
|
this.pendingWritesCallbacks = new Map();
|
|
23357
23909
|
this.limboTargetIdGenerator = TargetIdGenerator.forSyncEngine();
|
|
23358
|
-
this.onlineState = "Unknown" /* Unknown */;
|
|
23910
|
+
this.onlineState = "Unknown" /* OnlineState.Unknown */;
|
|
23359
23911
|
// The primary state is set to `true` or `false` immediately after Firestore
|
|
23360
23912
|
// startup. In the interim, a client should only be considered primary if
|
|
23361
23913
|
// `isPrimary` is true.
|
|
@@ -23419,7 +23971,7 @@ async function initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId,
|
|
|
23419
23971
|
/* usePreviousResults= */ true);
|
|
23420
23972
|
const view = new View(query, queryResult.remoteKeys);
|
|
23421
23973
|
const viewDocChanges = view.computeDocChanges(queryResult.documents);
|
|
23422
|
-
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* Offline */, resumeToken);
|
|
23974
|
+
const synthesizedTargetChange = TargetChange.createSynthesizedTargetChangeForCurrentChange(targetId, current && syncEngineImpl.onlineState !== "Offline" /* OnlineState.Offline */, resumeToken);
|
|
23423
23975
|
const viewChange = view.applyChanges(viewDocChanges,
|
|
23424
23976
|
/* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient, synthesizedTargetChange);
|
|
23425
23977
|
updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
|
|
@@ -23545,9 +24097,9 @@ function syncEngineApplyOnlineStateChange(syncEngine, onlineState, source) {
|
|
|
23545
24097
|
// tab remains online) and only apply the primary tab's online state from
|
|
23546
24098
|
// SharedClientState.
|
|
23547
24099
|
if ((syncEngineImpl.isPrimaryClient &&
|
|
23548
|
-
source === 0 /* RemoteStore */) ||
|
|
24100
|
+
source === 0 /* OnlineStateSource.RemoteStore */) ||
|
|
23549
24101
|
(!syncEngineImpl.isPrimaryClient &&
|
|
23550
|
-
source === 1 /* SharedClientState */)) {
|
|
24102
|
+
source === 1 /* OnlineStateSource.SharedClientState */)) {
|
|
23551
24103
|
const newViewSnapshots = [];
|
|
23552
24104
|
syncEngineImpl.queryViewsByQuery.forEach((query, queryView) => {
|
|
23553
24105
|
const viewChange = queryView.view.applyOnlineStateChange(onlineState);
|
|
@@ -23815,7 +24367,7 @@ function pumpEnqueuedLimboResolutions(syncEngineImpl) {
|
|
|
23815
24367
|
syncEngineImpl.activeLimboResolutionsByTarget.set(limboTargetId, new LimboResolution(key));
|
|
23816
24368
|
syncEngineImpl.activeLimboTargetsByKey =
|
|
23817
24369
|
syncEngineImpl.activeLimboTargetsByKey.insert(key, limboTargetId);
|
|
23818
|
-
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* LimboResolution */, ListenSequence.INVALID));
|
|
24370
|
+
remoteStoreListen(syncEngineImpl.remoteStore, new TargetData(queryToTarget(newQueryForPath(key.path)), limboTargetId, 2 /* TargetPurpose.LimboResolution */, ListenSequence.INVALID));
|
|
23819
24371
|
}
|
|
23820
24372
|
}
|
|
23821
24373
|
async function syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, changes, remoteEvent) {
|
|
@@ -24068,7 +24620,7 @@ async function synchronizeQueryViewsAndRaiseSnapshots(syncEngine, targets, trans
|
|
|
24068
24620
|
*/
|
|
24069
24621
|
// PORTING NOTE: Multi-Tab only.
|
|
24070
24622
|
function synthesizeTargetToQuery(target) {
|
|
24071
|
-
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* First */, target.startAt, target.endAt);
|
|
24623
|
+
return newQuery(target.path, target.collectionGroup, target.orderBy, target.filters, target.limit, "F" /* LimitType.First */, target.startAt, target.endAt);
|
|
24072
24624
|
}
|
|
24073
24625
|
/** Returns the IDs of the clients that are currently active. */
|
|
24074
24626
|
// PORTING NOTE: Multi-Tab only.
|
|
@@ -24399,7 +24951,7 @@ class OnlineComponentProvider {
|
|
|
24399
24951
|
this.eventManager = this.createEventManager(cfg);
|
|
24400
24952
|
this.syncEngine = this.createSyncEngine(cfg,
|
|
24401
24953
|
/* startAsPrimary=*/ !offlineComponentProvider.synchronizeTabs);
|
|
24402
|
-
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* SharedClientState */);
|
|
24954
|
+
this.sharedClientState.onlineStateHandler = onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 1 /* OnlineStateSource.SharedClientState */);
|
|
24403
24955
|
this.remoteStore.remoteSyncer.handleCredentialChange =
|
|
24404
24956
|
syncEngineHandleCredentialChange.bind(null, this.syncEngine);
|
|
24405
24957
|
await remoteStoreApplyPrimaryState(this.remoteStore, this.syncEngine.isPrimaryClient);
|
|
@@ -24413,7 +24965,7 @@ class OnlineComponentProvider {
|
|
|
24413
24965
|
return newDatastore(cfg.authCredentials, cfg.appCheckCredentials, connection, serializer);
|
|
24414
24966
|
}
|
|
24415
24967
|
createRemoteStore(cfg) {
|
|
24416
|
-
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* RemoteStore */), newConnectivityMonitor());
|
|
24968
|
+
return newRemoteStore(this.localStore, this.datastore, cfg.asyncQueue, onlineState => syncEngineApplyOnlineStateChange(this.syncEngine, onlineState, 0 /* OnlineStateSource.RemoteStore */), newConnectivityMonitor());
|
|
24417
24969
|
}
|
|
24418
24970
|
createSyncEngine(cfg, startAsPrimary) {
|
|
24419
24971
|
return newSyncEngine(this.localStore, this.remoteStore, this.eventManager, this.sharedClientState, cfg.initialUser, cfg.maxConcurrentLimboResolutions, startAsPrimary);
|
|
@@ -25170,7 +25722,7 @@ class TransactionRunner {
|
|
|
25170
25722
|
this.updateFunction = updateFunction;
|
|
25171
25723
|
this.deferred = deferred;
|
|
25172
25724
|
this.attemptsRemaining = options.maxAttempts;
|
|
25173
|
-
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
|
|
25725
|
+
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
|
|
25174
25726
|
}
|
|
25175
25727
|
/** Runs the transaction and sets the result on deferred. */
|
|
25176
25728
|
run() {
|
|
@@ -26203,7 +26755,7 @@ class AsyncQueueImpl {
|
|
|
26203
26755
|
// List of TimerIds to fast-forward delays for.
|
|
26204
26756
|
this.timerIdsToSkip = [];
|
|
26205
26757
|
// Backoff timer used to schedule retries for retryable operations
|
|
26206
|
-
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
|
|
26758
|
+
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
|
|
26207
26759
|
// Visibility handler that triggers an immediate retry of all retryable
|
|
26208
26760
|
// operations. Meant to speed up recovery when we regain file system access
|
|
26209
26761
|
// after page comes into foreground.
|
|
@@ -26373,7 +26925,7 @@ class AsyncQueueImpl {
|
|
|
26373
26925
|
this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
|
|
26374
26926
|
for (const op of this.delayedOperations) {
|
|
26375
26927
|
op.skipDelay();
|
|
26376
|
-
if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
|
|
26928
|
+
if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
|
|
26377
26929
|
break;
|
|
26378
26930
|
}
|
|
26379
26931
|
}
|
|
@@ -27286,12 +27838,12 @@ class ParsedUpdateData {
|
|
|
27286
27838
|
}
|
|
27287
27839
|
function isWrite(dataSource) {
|
|
27288
27840
|
switch (dataSource) {
|
|
27289
|
-
case 0 /* Set */: // fall through
|
|
27290
|
-
case 2 /* MergeSet */: // fall through
|
|
27291
|
-
case 1 /* Update */:
|
|
27841
|
+
case 0 /* UserDataSource.Set */: // fall through
|
|
27842
|
+
case 2 /* UserDataSource.MergeSet */: // fall through
|
|
27843
|
+
case 1 /* UserDataSource.Update */:
|
|
27292
27844
|
return true;
|
|
27293
|
-
case 3 /* Argument */:
|
|
27294
|
-
case 4 /* ArrayArgument */:
|
|
27845
|
+
case 3 /* UserDataSource.Argument */:
|
|
27846
|
+
case 4 /* UserDataSource.ArrayArgument */:
|
|
27295
27847
|
return false;
|
|
27296
27848
|
default:
|
|
27297
27849
|
throw fail();
|
|
@@ -27416,8 +27968,8 @@ function newUserDataReader(firestore) {
|
|
|
27416
27968
|
/** Parse document data from a set() call. */
|
|
27417
27969
|
function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
|
|
27418
27970
|
const context = userDataReader.createContext(options.merge || options.mergeFields
|
|
27419
|
-
? 2 /* MergeSet */
|
|
27420
|
-
: 0 /* Set */, methodName, targetDoc, hasConverter);
|
|
27971
|
+
? 2 /* UserDataSource.MergeSet */
|
|
27972
|
+
: 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
|
|
27421
27973
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27422
27974
|
const updateData = parseObject(input, context);
|
|
27423
27975
|
let fieldMask;
|
|
@@ -27448,12 +28000,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
|
|
|
27448
28000
|
}
|
|
27449
28001
|
class DeleteFieldValueImpl extends FieldValue {
|
|
27450
28002
|
_toFieldTransform(context) {
|
|
27451
|
-
if (context.dataSource === 2 /* MergeSet */) {
|
|
28003
|
+
if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
|
|
27452
28004
|
// No transform to add for a delete, but we need to add it to our
|
|
27453
28005
|
// fieldMask so it gets deleted.
|
|
27454
28006
|
context.fieldMask.push(context.path);
|
|
27455
28007
|
}
|
|
27456
|
-
else if (context.dataSource === 1 /* Update */) {
|
|
28008
|
+
else if (context.dataSource === 1 /* UserDataSource.Update */) {
|
|
27457
28009
|
throw context.createError(`${this._methodName}() can only appear at the top level ` +
|
|
27458
28010
|
'of your update data');
|
|
27459
28011
|
}
|
|
@@ -27486,7 +28038,7 @@ class DeleteFieldValueImpl extends FieldValue {
|
|
|
27486
28038
|
*/
|
|
27487
28039
|
function createSentinelChildContext(fieldValue, context, arrayElement) {
|
|
27488
28040
|
return new ParseContextImpl({
|
|
27489
|
-
dataSource: 3 /* Argument */,
|
|
28041
|
+
dataSource: 3 /* UserDataSource.Argument */,
|
|
27490
28042
|
targetDoc: context.settings.targetDoc,
|
|
27491
28043
|
methodName: fieldValue._methodName,
|
|
27492
28044
|
arrayElement
|
|
@@ -27550,7 +28102,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
|
|
|
27550
28102
|
}
|
|
27551
28103
|
/** Parse update data from an update() call. */
|
|
27552
28104
|
function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
27553
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28105
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27554
28106
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
27555
28107
|
const fieldMaskPaths = [];
|
|
27556
28108
|
const updateData = ObjectValue.empty();
|
|
@@ -27577,7 +28129,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
|
27577
28129
|
}
|
|
27578
28130
|
/** Parse update data from a list of field/value arguments. */
|
|
27579
28131
|
function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
|
|
27580
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
28132
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
27581
28133
|
const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
|
|
27582
28134
|
const values = [value];
|
|
27583
28135
|
if (moreFieldsAndValues.length % 2 !== 0) {
|
|
@@ -27624,7 +28176,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
|
|
|
27624
28176
|
* contain additional arrays (e.g. the operand of an `in` query).
|
|
27625
28177
|
*/
|
|
27626
28178
|
function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
|
|
27627
|
-
const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
|
|
28179
|
+
const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
|
|
27628
28180
|
const parsed = parseData(input, context);
|
|
27629
28181
|
return parsed;
|
|
27630
28182
|
}
|
|
@@ -27674,7 +28226,7 @@ function parseData(input, context) {
|
|
|
27674
28226
|
// contain additional arrays (each representing an individual field
|
|
27675
28227
|
// value), so we disable this validation.
|
|
27676
28228
|
if (context.settings.arrayElement &&
|
|
27677
|
-
context.dataSource !== 4 /* ArrayArgument */) {
|
|
28229
|
+
context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
|
|
27678
28230
|
throw context.createError('Nested arrays are not supported');
|
|
27679
28231
|
}
|
|
27680
28232
|
return parseArray(input, context);
|
|
@@ -28056,164 +28608,334 @@ function fieldPathFromArgument(methodName, arg) {
|
|
|
28056
28608
|
* limitations under the License.
|
|
28057
28609
|
*/
|
|
28058
28610
|
function validateHasExplicitOrderByForLimitToLast(query) {
|
|
28059
|
-
if (query.limitType === "L" /* Last */ &&
|
|
28611
|
+
if (query.limitType === "L" /* LimitType.Last */ &&
|
|
28060
28612
|
query.explicitOrderBy.length === 0) {
|
|
28061
28613
|
throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
|
|
28062
28614
|
}
|
|
28063
28615
|
}
|
|
28616
|
+
/**
|
|
28617
|
+
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
|
|
28618
|
+
* to a Firestore query.
|
|
28619
|
+
*/
|
|
28620
|
+
class AppliableConstraint {
|
|
28621
|
+
}
|
|
28064
28622
|
/**
|
|
28065
28623
|
* A `QueryConstraint` is used to narrow the set of documents returned by a
|
|
28066
28624
|
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
|
|
28067
|
-
* {@link orderBy}, {@link
|
|
28068
|
-
* endBefore
|
|
28625
|
+
* {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
|
|
28626
|
+
* endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
|
|
28069
28627
|
* can then be passed to {@link query} to create a new query instance that
|
|
28070
28628
|
* also contains this `QueryConstraint`.
|
|
28071
28629
|
*/
|
|
28072
|
-
class QueryConstraint {
|
|
28630
|
+
class QueryConstraint extends AppliableConstraint {
|
|
28073
28631
|
}
|
|
28074
|
-
|
|
28075
|
-
|
|
28076
|
-
|
|
28077
|
-
|
|
28078
|
-
|
|
28079
|
-
|
|
28080
|
-
|
|
28081
|
-
* existing or new constraints.
|
|
28082
|
-
*/
|
|
28083
|
-
function query(query, ...queryConstraints) {
|
|
28632
|
+
function query(query, queryConstraint, ...additionalQueryConstraints) {
|
|
28633
|
+
let queryConstraints = [];
|
|
28634
|
+
if (queryConstraint instanceof AppliableConstraint) {
|
|
28635
|
+
queryConstraints.push(queryConstraint);
|
|
28636
|
+
}
|
|
28637
|
+
queryConstraints = queryConstraints.concat(additionalQueryConstraints);
|
|
28638
|
+
validateQueryConstraintArray(queryConstraints);
|
|
28084
28639
|
for (const constraint of queryConstraints) {
|
|
28085
28640
|
query = constraint._apply(query);
|
|
28086
28641
|
}
|
|
28087
28642
|
return query;
|
|
28088
28643
|
}
|
|
28089
|
-
|
|
28644
|
+
/**
|
|
28645
|
+
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
|
|
28646
|
+
* a Firestore query by filtering on one or more document fields.
|
|
28647
|
+
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
|
|
28648
|
+
* be passed to {@link query} to create a new query instance that also contains
|
|
28649
|
+
* this `QueryFieldFilterConstraint`.
|
|
28650
|
+
*/
|
|
28651
|
+
class QueryFieldFilterConstraint extends QueryConstraint {
|
|
28652
|
+
/**
|
|
28653
|
+
* @internal
|
|
28654
|
+
*/
|
|
28090
28655
|
constructor(_field, _op, _value) {
|
|
28091
28656
|
super();
|
|
28092
28657
|
this._field = _field;
|
|
28093
28658
|
this._op = _op;
|
|
28094
28659
|
this._value = _value;
|
|
28660
|
+
/** The type of this query constraint */
|
|
28095
28661
|
this.type = 'where';
|
|
28096
28662
|
}
|
|
28663
|
+
static _create(_field, _op, _value) {
|
|
28664
|
+
return new QueryFieldFilterConstraint(_field, _op, _value);
|
|
28665
|
+
}
|
|
28097
28666
|
_apply(query) {
|
|
28667
|
+
const filter = this._parse(query);
|
|
28668
|
+
validateNewFieldFilter(query._query, filter);
|
|
28669
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
|
|
28670
|
+
}
|
|
28671
|
+
_parse(query) {
|
|
28098
28672
|
const reader = newUserDataReader(query.firestore);
|
|
28099
28673
|
const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
|
|
28100
|
-
return
|
|
28674
|
+
return filter;
|
|
28101
28675
|
}
|
|
28102
28676
|
}
|
|
28103
28677
|
/**
|
|
28104
|
-
* Creates a {@link
|
|
28105
|
-
* specified field and that the value should satisfy the
|
|
28106
|
-
* provided.
|
|
28678
|
+
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents
|
|
28679
|
+
* must contain the specified field and that the value should satisfy the
|
|
28680
|
+
* relation constraint provided.
|
|
28107
28681
|
*
|
|
28108
28682
|
* @param fieldPath - The path to compare
|
|
28109
28683
|
* @param opStr - The operation string (e.g "<", "<=", "==", "<",
|
|
28110
28684
|
* "<=", "!=").
|
|
28111
28685
|
* @param value - The value for comparison
|
|
28112
|
-
* @returns The created {@link
|
|
28686
|
+
* @returns The created {@link QueryFieldFilterConstraint}.
|
|
28113
28687
|
*/
|
|
28114
28688
|
function where(fieldPath, opStr, value) {
|
|
28115
28689
|
const op = opStr;
|
|
28116
28690
|
const field = fieldPathFromArgument('where', fieldPath);
|
|
28117
|
-
return
|
|
28691
|
+
return QueryFieldFilterConstraint._create(field, op, value);
|
|
28692
|
+
}
|
|
28693
|
+
/**
|
|
28694
|
+
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
|
|
28695
|
+
* returned by a Firestore query by performing the logical OR or AND of multiple
|
|
28696
|
+
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
|
|
28697
|
+
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
|
|
28698
|
+
* {@link and} and can then be passed to {@link query} to create a new query
|
|
28699
|
+
* instance that also contains the `QueryCompositeFilterConstraint`.
|
|
28700
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28701
|
+
*/
|
|
28702
|
+
class QueryCompositeFilterConstraint extends AppliableConstraint {
|
|
28703
|
+
/**
|
|
28704
|
+
* @internal
|
|
28705
|
+
*/
|
|
28706
|
+
constructor(
|
|
28707
|
+
/** The type of this query constraint */
|
|
28708
|
+
type, _queryConstraints) {
|
|
28709
|
+
super();
|
|
28710
|
+
this.type = type;
|
|
28711
|
+
this._queryConstraints = _queryConstraints;
|
|
28712
|
+
}
|
|
28713
|
+
static _create(type, _queryConstraints) {
|
|
28714
|
+
return new QueryCompositeFilterConstraint(type, _queryConstraints);
|
|
28715
|
+
}
|
|
28716
|
+
_parse(query) {
|
|
28717
|
+
const parsedFilters = this._queryConstraints
|
|
28718
|
+
.map(queryConstraint => {
|
|
28719
|
+
return queryConstraint._parse(query);
|
|
28720
|
+
})
|
|
28721
|
+
.filter(parsedFilter => parsedFilter.getFilters().length > 0);
|
|
28722
|
+
if (parsedFilters.length === 1) {
|
|
28723
|
+
return parsedFilters[0];
|
|
28724
|
+
}
|
|
28725
|
+
return CompositeFilter.create(parsedFilters, this._getOperator());
|
|
28726
|
+
}
|
|
28727
|
+
_apply(query) {
|
|
28728
|
+
const parsedFilter = this._parse(query);
|
|
28729
|
+
if (parsedFilter.getFilters().length === 0) {
|
|
28730
|
+
// Return the existing query if not adding any more filters (e.g. an empty
|
|
28731
|
+
// composite filter).
|
|
28732
|
+
return query;
|
|
28733
|
+
}
|
|
28734
|
+
validateNewFilter(query._query, parsedFilter);
|
|
28735
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
|
|
28736
|
+
}
|
|
28737
|
+
_getQueryConstraints() {
|
|
28738
|
+
return this._queryConstraints;
|
|
28739
|
+
}
|
|
28740
|
+
_getOperator() {
|
|
28741
|
+
return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
|
|
28742
|
+
}
|
|
28743
|
+
}
|
|
28744
|
+
/**
|
|
28745
|
+
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical OR
|
|
28746
|
+
* of all the provided {@link QueryFilterConstraint}s.
|
|
28747
|
+
*
|
|
28748
|
+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
|
|
28749
|
+
* for OR operation. These must be created with calls to {@link where},
|
|
28750
|
+
* {@link or}, or {@link and}.
|
|
28751
|
+
* @returns The created {@link QueryCompositeFilterConstraint}.
|
|
28752
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28753
|
+
*/
|
|
28754
|
+
function or(...queryConstraints) {
|
|
28755
|
+
// Only support QueryFilterConstraints
|
|
28756
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
|
|
28757
|
+
return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
|
|
28758
|
+
}
|
|
28759
|
+
/**
|
|
28760
|
+
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical AND
|
|
28761
|
+
* of all the provided {@link QueryFilterConstraint}s.
|
|
28762
|
+
*
|
|
28763
|
+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
|
|
28764
|
+
* for AND operation. These must be created with calls to {@link where},
|
|
28765
|
+
* {@link or}, or {@link and}.
|
|
28766
|
+
* @returns The created {@link QueryCompositeFilterConstraint}.
|
|
28767
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
28768
|
+
*/
|
|
28769
|
+
function and(...queryConstraints) {
|
|
28770
|
+
// Only support QueryFilterConstraints
|
|
28771
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
|
|
28772
|
+
return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
|
|
28118
28773
|
}
|
|
28774
|
+
/**
|
|
28775
|
+
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a
|
|
28776
|
+
* Firestore query. `QueryOrderByConstraint`s are created by invoking
|
|
28777
|
+
* {@link orderBy} and can then be passed to {@link query} to create a new query
|
|
28778
|
+
* instance that also contains this `QueryOrderByConstraint`.
|
|
28779
|
+
*
|
|
28780
|
+
* Note: Documents that do not contain the orderBy field will not be present in
|
|
28781
|
+
* the query result.
|
|
28782
|
+
*/
|
|
28119
28783
|
class QueryOrderByConstraint extends QueryConstraint {
|
|
28784
|
+
/**
|
|
28785
|
+
* @internal
|
|
28786
|
+
*/
|
|
28120
28787
|
constructor(_field, _direction) {
|
|
28121
28788
|
super();
|
|
28122
28789
|
this._field = _field;
|
|
28123
28790
|
this._direction = _direction;
|
|
28791
|
+
/** The type of this query constraint */
|
|
28124
28792
|
this.type = 'orderBy';
|
|
28125
28793
|
}
|
|
28794
|
+
static _create(_field, _direction) {
|
|
28795
|
+
return new QueryOrderByConstraint(_field, _direction);
|
|
28796
|
+
}
|
|
28126
28797
|
_apply(query) {
|
|
28127
28798
|
const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
|
|
28128
28799
|
return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
|
|
28129
28800
|
}
|
|
28130
28801
|
}
|
|
28131
28802
|
/**
|
|
28132
|
-
* Creates a {@link
|
|
28803
|
+
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the
|
|
28133
28804
|
* specified field, optionally in descending order instead of ascending.
|
|
28134
28805
|
*
|
|
28806
|
+
* Note: Documents that do not contain the specified field will not be present
|
|
28807
|
+
* in the query result.
|
|
28808
|
+
*
|
|
28135
28809
|
* @param fieldPath - The field to sort by.
|
|
28136
28810
|
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
|
|
28137
28811
|
* not specified, order will be ascending.
|
|
28138
|
-
* @returns The created {@link
|
|
28812
|
+
* @returns The created {@link QueryOrderByConstraint}.
|
|
28139
28813
|
*/
|
|
28140
28814
|
function orderBy(fieldPath, directionStr = 'asc') {
|
|
28141
28815
|
const direction = directionStr;
|
|
28142
28816
|
const path = fieldPathFromArgument('orderBy', fieldPath);
|
|
28143
|
-
return
|
|
28817
|
+
return QueryOrderByConstraint._create(path, direction);
|
|
28144
28818
|
}
|
|
28819
|
+
/**
|
|
28820
|
+
* A `QueryLimitConstraint` is used to limit the number of documents returned by
|
|
28821
|
+
* a Firestore query.
|
|
28822
|
+
* `QueryLimitConstraint`s are created by invoking {@link limit} or
|
|
28823
|
+
* {@link limitToLast} and can then be passed to {@link query} to create a new
|
|
28824
|
+
* query instance that also contains this `QueryLimitConstraint`.
|
|
28825
|
+
*/
|
|
28145
28826
|
class QueryLimitConstraint extends QueryConstraint {
|
|
28146
|
-
|
|
28827
|
+
/**
|
|
28828
|
+
* @internal
|
|
28829
|
+
*/
|
|
28830
|
+
constructor(
|
|
28831
|
+
/** The type of this query constraint */
|
|
28832
|
+
type, _limit, _limitType) {
|
|
28147
28833
|
super();
|
|
28148
28834
|
this.type = type;
|
|
28149
28835
|
this._limit = _limit;
|
|
28150
28836
|
this._limitType = _limitType;
|
|
28151
28837
|
}
|
|
28838
|
+
static _create(type, _limit, _limitType) {
|
|
28839
|
+
return new QueryLimitConstraint(type, _limit, _limitType);
|
|
28840
|
+
}
|
|
28152
28841
|
_apply(query) {
|
|
28153
28842
|
return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
|
|
28154
28843
|
}
|
|
28155
28844
|
}
|
|
28156
28845
|
/**
|
|
28157
|
-
* Creates a {@link
|
|
28846
|
+
* Creates a {@link QueryLimitConstraint} that only returns the first matching
|
|
28847
|
+
* documents.
|
|
28158
28848
|
*
|
|
28159
28849
|
* @param limit - The maximum number of items to return.
|
|
28160
|
-
* @returns The created {@link
|
|
28850
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28161
28851
|
*/
|
|
28162
28852
|
function limit(limit) {
|
|
28163
28853
|
validatePositiveNumber('limit', limit);
|
|
28164
|
-
return
|
|
28854
|
+
return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
|
|
28165
28855
|
}
|
|
28166
28856
|
/**
|
|
28167
|
-
* Creates a {@link
|
|
28857
|
+
* Creates a {@link QueryLimitConstraint} that only returns the last matching
|
|
28858
|
+
* documents.
|
|
28168
28859
|
*
|
|
28169
28860
|
* You must specify at least one `orderBy` clause for `limitToLast` queries,
|
|
28170
28861
|
* otherwise an exception will be thrown during execution.
|
|
28171
28862
|
*
|
|
28172
28863
|
* @param limit - The maximum number of items to return.
|
|
28173
|
-
* @returns The created {@link
|
|
28864
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
28174
28865
|
*/
|
|
28175
28866
|
function limitToLast(limit) {
|
|
28176
28867
|
validatePositiveNumber('limitToLast', limit);
|
|
28177
|
-
return
|
|
28868
|
+
return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
|
|
28178
28869
|
}
|
|
28870
|
+
/**
|
|
28871
|
+
* A `QueryStartAtConstraint` is used to exclude documents from the start of a
|
|
28872
|
+
* result set returned by a Firestore query.
|
|
28873
|
+
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
|
|
28874
|
+
* {@link (startAfter:1)} and can then be passed to {@link query} to create a
|
|
28875
|
+
* new query instance that also contains this `QueryStartAtConstraint`.
|
|
28876
|
+
*/
|
|
28179
28877
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
28180
|
-
|
|
28878
|
+
/**
|
|
28879
|
+
* @internal
|
|
28880
|
+
*/
|
|
28881
|
+
constructor(
|
|
28882
|
+
/** The type of this query constraint */
|
|
28883
|
+
type, _docOrFields, _inclusive) {
|
|
28181
28884
|
super();
|
|
28182
28885
|
this.type = type;
|
|
28183
28886
|
this._docOrFields = _docOrFields;
|
|
28184
28887
|
this._inclusive = _inclusive;
|
|
28185
28888
|
}
|
|
28889
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28890
|
+
return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
|
|
28891
|
+
}
|
|
28186
28892
|
_apply(query) {
|
|
28187
28893
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28188
28894
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
28189
28895
|
}
|
|
28190
28896
|
}
|
|
28191
28897
|
function startAt(...docOrFields) {
|
|
28192
|
-
return
|
|
28898
|
+
return QueryStartAtConstraint._create('startAt', docOrFields,
|
|
28193
28899
|
/*inclusive=*/ true);
|
|
28194
28900
|
}
|
|
28195
28901
|
function startAfter(...docOrFields) {
|
|
28196
|
-
return
|
|
28902
|
+
return QueryStartAtConstraint._create('startAfter', docOrFields,
|
|
28197
28903
|
/*inclusive=*/ false);
|
|
28198
28904
|
}
|
|
28905
|
+
/**
|
|
28906
|
+
* A `QueryEndAtConstraint` is used to exclude documents from the end of a
|
|
28907
|
+
* result set returned by a Firestore query.
|
|
28908
|
+
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
|
|
28909
|
+
* {@link (endBefore:1)} and can then be passed to {@link query} to create a new
|
|
28910
|
+
* query instance that also contains this `QueryEndAtConstraint`.
|
|
28911
|
+
*/
|
|
28199
28912
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
28200
|
-
|
|
28913
|
+
/**
|
|
28914
|
+
* @internal
|
|
28915
|
+
*/
|
|
28916
|
+
constructor(
|
|
28917
|
+
/** The type of this query constraint */
|
|
28918
|
+
type, _docOrFields, _inclusive) {
|
|
28201
28919
|
super();
|
|
28202
28920
|
this.type = type;
|
|
28203
28921
|
this._docOrFields = _docOrFields;
|
|
28204
28922
|
this._inclusive = _inclusive;
|
|
28205
28923
|
}
|
|
28924
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
28925
|
+
return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
|
|
28926
|
+
}
|
|
28206
28927
|
_apply(query) {
|
|
28207
28928
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
28208
28929
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
28209
28930
|
}
|
|
28210
28931
|
}
|
|
28211
28932
|
function endBefore(...docOrFields) {
|
|
28212
|
-
return
|
|
28933
|
+
return QueryEndAtConstraint._create('endBefore', docOrFields,
|
|
28213
28934
|
/*inclusive=*/ false);
|
|
28214
28935
|
}
|
|
28215
28936
|
function endAt(...docOrFields) {
|
|
28216
|
-
return
|
|
28937
|
+
return QueryEndAtConstraint._create('endAt', docOrFields,
|
|
28938
|
+
/*inclusive=*/ true);
|
|
28217
28939
|
}
|
|
28218
28940
|
/** Helper function to create a bound from a document or fields */
|
|
28219
28941
|
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
@@ -28229,10 +28951,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
|
|
|
28229
28951
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
28230
28952
|
let fieldValue;
|
|
28231
28953
|
if (fieldPath.isKeyField()) {
|
|
28232
|
-
if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28954
|
+
if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28233
28955
|
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
|
|
28234
28956
|
}
|
|
28235
|
-
else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
28957
|
+
else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
28236
28958
|
validateDisjunctiveFilterElements(value, op);
|
|
28237
28959
|
const referenceList = [];
|
|
28238
28960
|
for (const arrayValue of value) {
|
|
@@ -28245,16 +28967,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
|
|
|
28245
28967
|
}
|
|
28246
28968
|
}
|
|
28247
28969
|
else {
|
|
28248
|
-
if (op === "in" /* IN */ ||
|
|
28249
|
-
op === "not-in" /* NOT_IN */ ||
|
|
28250
|
-
op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
28970
|
+
if (op === "in" /* Operator.IN */ ||
|
|
28971
|
+
op === "not-in" /* Operator.NOT_IN */ ||
|
|
28972
|
+
op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
28251
28973
|
validateDisjunctiveFilterElements(value, op);
|
|
28252
28974
|
}
|
|
28253
28975
|
fieldValue = parseQueryValue(dataReader, methodName, value,
|
|
28254
|
-
/* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
|
|
28976
|
+
/* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
|
|
28255
28977
|
}
|
|
28256
28978
|
const filter = FieldFilter.create(fieldPath, op, fieldValue);
|
|
28257
|
-
validateNewFilter(query, filter);
|
|
28258
28979
|
return filter;
|
|
28259
28980
|
}
|
|
28260
28981
|
function newQueryOrderBy(query, fieldPath, direction) {
|
|
@@ -28424,62 +29145,84 @@ function validateDisjunctiveFilterElements(value, operator) {
|
|
|
28424
29145
|
*/
|
|
28425
29146
|
function conflictingOps(op) {
|
|
28426
29147
|
switch (op) {
|
|
28427
|
-
case "!=" /* NOT_EQUAL */:
|
|
28428
|
-
return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
|
|
28429
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
29148
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
29149
|
+
return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
|
|
29150
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
28430
29151
|
return [
|
|
28431
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28432
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28433
|
-
"not-in" /* NOT_IN */
|
|
29152
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29153
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29154
|
+
"not-in" /* Operator.NOT_IN */
|
|
28434
29155
|
];
|
|
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 */:
|
|
29156
|
+
case "in" /* Operator.IN */:
|
|
29157
|
+
return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
|
|
29158
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
28438
29159
|
return [
|
|
28439
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28440
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28441
|
-
"in" /* IN */,
|
|
28442
|
-
"not-in" /* NOT_IN */
|
|
29160
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29161
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29162
|
+
"in" /* Operator.IN */,
|
|
29163
|
+
"not-in" /* Operator.NOT_IN */
|
|
28443
29164
|
];
|
|
28444
|
-
case "not-in" /* NOT_IN */:
|
|
29165
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
28445
29166
|
return [
|
|
28446
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
28447
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
28448
|
-
"in" /* IN */,
|
|
28449
|
-
"not-in" /* NOT_IN */,
|
|
28450
|
-
"!=" /* NOT_EQUAL */
|
|
29167
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
29168
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
29169
|
+
"in" /* Operator.IN */,
|
|
29170
|
+
"not-in" /* Operator.NOT_IN */,
|
|
29171
|
+
"!=" /* Operator.NOT_EQUAL */
|
|
28451
29172
|
];
|
|
28452
29173
|
default:
|
|
28453
29174
|
return [];
|
|
28454
29175
|
}
|
|
28455
29176
|
}
|
|
28456
|
-
function
|
|
28457
|
-
if (
|
|
28458
|
-
const
|
|
28459
|
-
|
|
29177
|
+
function validateNewFieldFilter(query, fieldFilter) {
|
|
29178
|
+
if (fieldFilter.isInequality()) {
|
|
29179
|
+
const existingInequality = getInequalityFilterField(query);
|
|
29180
|
+
const newInequality = fieldFilter.field;
|
|
29181
|
+
if (existingInequality !== null &&
|
|
29182
|
+
!existingInequality.isEqual(newInequality)) {
|
|
28460
29183
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
|
|
28461
29184
|
' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
|
|
28462
|
-
` inequality filters on '${
|
|
28463
|
-
` and '${
|
|
29185
|
+
` inequality filters on '${existingInequality.toString()}'` +
|
|
29186
|
+
` and '${newInequality.toString()}'`);
|
|
28464
29187
|
}
|
|
28465
29188
|
const firstOrderByField = getFirstOrderByField(query);
|
|
28466
29189
|
if (firstOrderByField !== null) {
|
|
28467
|
-
validateOrderByAndInequalityMatch(query,
|
|
29190
|
+
validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
|
|
28468
29191
|
}
|
|
28469
29192
|
}
|
|
28470
|
-
const conflictingOp =
|
|
29193
|
+
const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
|
|
28471
29194
|
if (conflictingOp !== null) {
|
|
28472
29195
|
// Special case when it's a duplicate op to give a slightly clearer error message.
|
|
28473
|
-
if (conflictingOp ===
|
|
29196
|
+
if (conflictingOp === fieldFilter.op) {
|
|
28474
29197
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
|
|
28475
|
-
`'${
|
|
29198
|
+
`'${fieldFilter.op.toString()}' filter.`);
|
|
28476
29199
|
}
|
|
28477
29200
|
else {
|
|
28478
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${
|
|
29201
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
|
|
28479
29202
|
`with '${conflictingOp.toString()}' filters.`);
|
|
28480
29203
|
}
|
|
28481
29204
|
}
|
|
28482
29205
|
}
|
|
29206
|
+
function validateNewFilter(query, filter) {
|
|
29207
|
+
let testQuery = query;
|
|
29208
|
+
const subFilters = filter.getFlattenedFilters();
|
|
29209
|
+
for (const subFilter of subFilters) {
|
|
29210
|
+
validateNewFieldFilter(testQuery, subFilter);
|
|
29211
|
+
testQuery = queryWithAddedFilter(testQuery, subFilter);
|
|
29212
|
+
}
|
|
29213
|
+
}
|
|
29214
|
+
// Checks if any of the provided filter operators are included in the given list of filters and
|
|
29215
|
+
// returns the first one that is, or null if none are.
|
|
29216
|
+
function findOpInsideFilters(filters, operators) {
|
|
29217
|
+
for (const filter of filters) {
|
|
29218
|
+
for (const fieldFilter of filter.getFlattenedFilters()) {
|
|
29219
|
+
if (operators.indexOf(fieldFilter.op) >= 0) {
|
|
29220
|
+
return fieldFilter.op;
|
|
29221
|
+
}
|
|
29222
|
+
}
|
|
29223
|
+
}
|
|
29224
|
+
return null;
|
|
29225
|
+
}
|
|
28483
29226
|
function validateNewOrderBy(query, orderBy) {
|
|
28484
29227
|
if (getFirstOrderByField(query) === null) {
|
|
28485
29228
|
// This is the first order by. It must match any inequality.
|
|
@@ -28497,6 +29240,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28497
29240
|
`as your first argument to orderBy(), but your first orderBy() ` +
|
|
28498
29241
|
`is on field '${orderBy.toString()}' instead.`);
|
|
28499
29242
|
}
|
|
29243
|
+
}
|
|
29244
|
+
function validateQueryFilterConstraint(functionName, queryConstraint) {
|
|
29245
|
+
if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
|
|
29246
|
+
!(queryConstraint instanceof QueryCompositeFilterConstraint)) {
|
|
29247
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
|
|
29248
|
+
}
|
|
29249
|
+
}
|
|
29250
|
+
function validateQueryConstraintArray(queryConstraint) {
|
|
29251
|
+
const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
|
|
29252
|
+
const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
|
|
29253
|
+
if (compositeFilterCount > 1 ||
|
|
29254
|
+
(compositeFilterCount > 0 && fieldFilterCount > 0)) {
|
|
29255
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
|
|
29256
|
+
'more than one filter at the top level. Consider nesting the multiple ' +
|
|
29257
|
+
'filters within an `and(...)` statement. For example: ' +
|
|
29258
|
+
'change `query(query, where(...), or(...))` to ' +
|
|
29259
|
+
'`query(query, and(where(...), or(...)))`.');
|
|
29260
|
+
}
|
|
28500
29261
|
}
|
|
28501
29262
|
|
|
28502
29263
|
/**
|
|
@@ -28524,27 +29285,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
28524
29285
|
class AbstractUserDataWriter {
|
|
28525
29286
|
convertValue(value, serverTimestampBehavior = 'none') {
|
|
28526
29287
|
switch (typeOrder(value)) {
|
|
28527
|
-
case 0 /* NullValue */:
|
|
29288
|
+
case 0 /* TypeOrder.NullValue */:
|
|
28528
29289
|
return null;
|
|
28529
|
-
case 1 /* BooleanValue */:
|
|
29290
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
28530
29291
|
return value.booleanValue;
|
|
28531
|
-
case 2 /* NumberValue */:
|
|
29292
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
28532
29293
|
return normalizeNumber(value.integerValue || value.doubleValue);
|
|
28533
|
-
case 3 /* TimestampValue */:
|
|
29294
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
28534
29295
|
return this.convertTimestamp(value.timestampValue);
|
|
28535
|
-
case 4 /* ServerTimestampValue */:
|
|
29296
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
28536
29297
|
return this.convertServerTimestamp(value, serverTimestampBehavior);
|
|
28537
|
-
case 5 /* StringValue */:
|
|
29298
|
+
case 5 /* TypeOrder.StringValue */:
|
|
28538
29299
|
return value.stringValue;
|
|
28539
|
-
case 6 /* BlobValue */:
|
|
29300
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
28540
29301
|
return this.convertBytes(normalizeByteString(value.bytesValue));
|
|
28541
|
-
case 7 /* RefValue */:
|
|
29302
|
+
case 7 /* TypeOrder.RefValue */:
|
|
28542
29303
|
return this.convertReference(value.referenceValue);
|
|
28543
|
-
case 8 /* GeoPointValue */:
|
|
29304
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
28544
29305
|
return this.convertGeoPoint(value.geoPointValue);
|
|
28545
|
-
case 9 /* ArrayValue */:
|
|
29306
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
28546
29307
|
return this.convertArray(value.arrayValue, serverTimestampBehavior);
|
|
28547
|
-
case 10 /* ObjectValue */:
|
|
29308
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
28548
29309
|
return this.convertObject(value.mapValue, serverTimestampBehavior);
|
|
28549
29310
|
default:
|
|
28550
29311
|
throw fail();
|
|
@@ -28886,16 +29647,16 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28886
29647
|
// to lookup the index of a document.
|
|
28887
29648
|
let indexTracker = querySnapshot._snapshot.oldDocs;
|
|
28888
29649
|
return querySnapshot._snapshot.docChanges
|
|
28889
|
-
.filter(change => includeMetadataChanges || change.type !== 3 /* Metadata */)
|
|
29650
|
+
.filter(change => includeMetadataChanges || change.type !== 3 /* ChangeType.Metadata */)
|
|
28890
29651
|
.map(change => {
|
|
28891
29652
|
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
29653
|
let oldIndex = -1;
|
|
28893
29654
|
let newIndex = -1;
|
|
28894
|
-
if (change.type !== 0 /* Added */) {
|
|
29655
|
+
if (change.type !== 0 /* ChangeType.Added */) {
|
|
28895
29656
|
oldIndex = indexTracker.indexOf(change.doc.key);
|
|
28896
29657
|
indexTracker = indexTracker.delete(change.doc.key);
|
|
28897
29658
|
}
|
|
28898
|
-
if (change.type !== 1 /* Removed */) {
|
|
29659
|
+
if (change.type !== 1 /* ChangeType.Removed */) {
|
|
28899
29660
|
indexTracker = indexTracker.add(change.doc);
|
|
28900
29661
|
newIndex = indexTracker.indexOf(change.doc.key);
|
|
28901
29662
|
}
|
|
@@ -28910,12 +29671,12 @@ function changesFromSnapshot(querySnapshot, includeMetadataChanges) {
|
|
|
28910
29671
|
}
|
|
28911
29672
|
function resultChangeType(type) {
|
|
28912
29673
|
switch (type) {
|
|
28913
|
-
case 0 /* Added */:
|
|
29674
|
+
case 0 /* ChangeType.Added */:
|
|
28914
29675
|
return 'added';
|
|
28915
|
-
case 2 /* Modified */:
|
|
28916
|
-
case 3 /* Metadata */:
|
|
29676
|
+
case 2 /* ChangeType.Modified */:
|
|
29677
|
+
case 3 /* ChangeType.Metadata */:
|
|
28917
29678
|
return 'modified';
|
|
28918
|
-
case 1 /* Removed */:
|
|
29679
|
+
case 1 /* ChangeType.Removed */:
|
|
28919
29680
|
return 'removed';
|
|
28920
29681
|
default:
|
|
28921
29682
|
return fail();
|
|
@@ -29749,13 +30510,13 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29749
30510
|
const fieldPathString = tryGetString(field, 'fieldPath');
|
|
29750
30511
|
const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
|
|
29751
30512
|
if (field.arrayConfig === 'CONTAINS') {
|
|
29752
|
-
segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
|
|
30513
|
+
segments.push(new IndexSegment(fieldPath, 2 /* IndexKind.CONTAINS */));
|
|
29753
30514
|
}
|
|
29754
30515
|
else if (field.order === 'ASCENDING') {
|
|
29755
|
-
segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
|
|
30516
|
+
segments.push(new IndexSegment(fieldPath, 0 /* IndexKind.ASCENDING */));
|
|
29756
30517
|
}
|
|
29757
30518
|
else if (field.order === 'DESCENDING') {
|
|
29758
|
-
segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
|
|
30519
|
+
segments.push(new IndexSegment(fieldPath, 1 /* IndexKind.DESCENDING */));
|
|
29759
30520
|
}
|
|
29760
30521
|
}
|
|
29761
30522
|
}
|
|
@@ -29765,12 +30526,11 @@ function parseIndexes(jsonOrConfiguration) {
|
|
|
29765
30526
|
return parsedIndexes;
|
|
29766
30527
|
}
|
|
29767
30528
|
function tryParseJson(json) {
|
|
29768
|
-
var _a;
|
|
29769
30529
|
try {
|
|
29770
30530
|
return JSON.parse(json);
|
|
29771
30531
|
}
|
|
29772
30532
|
catch (e) {
|
|
29773
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (
|
|
30533
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Failed to parse JSON: ' + (e === null || e === void 0 ? void 0 : e.message));
|
|
29774
30534
|
}
|
|
29775
30535
|
}
|
|
29776
30536
|
function tryGetString(data, property) {
|
|
@@ -29813,9 +30573,15 @@ exports.FirestoreError = FirestoreError;
|
|
|
29813
30573
|
exports.GeoPoint = GeoPoint;
|
|
29814
30574
|
exports.LoadBundleTask = LoadBundleTask;
|
|
29815
30575
|
exports.Query = Query;
|
|
30576
|
+
exports.QueryCompositeFilterConstraint = QueryCompositeFilterConstraint;
|
|
29816
30577
|
exports.QueryConstraint = QueryConstraint;
|
|
29817
30578
|
exports.QueryDocumentSnapshot = QueryDocumentSnapshot;
|
|
30579
|
+
exports.QueryEndAtConstraint = QueryEndAtConstraint;
|
|
30580
|
+
exports.QueryFieldFilterConstraint = QueryFieldFilterConstraint;
|
|
30581
|
+
exports.QueryLimitConstraint = QueryLimitConstraint;
|
|
30582
|
+
exports.QueryOrderByConstraint = QueryOrderByConstraint;
|
|
29818
30583
|
exports.QuerySnapshot = QuerySnapshot;
|
|
30584
|
+
exports.QueryStartAtConstraint = QueryStartAtConstraint;
|
|
29819
30585
|
exports.SnapshotMetadata = SnapshotMetadata;
|
|
29820
30586
|
exports.Timestamp = Timestamp;
|
|
29821
30587
|
exports.Transaction = Transaction;
|
|
@@ -29832,6 +30598,7 @@ exports._logWarn = logWarn;
|
|
|
29832
30598
|
exports._validateIsNotUsedTogether = validateIsNotUsedTogether;
|
|
29833
30599
|
exports.addDoc = addDoc;
|
|
29834
30600
|
exports.aggregateQuerySnapshotEqual = aggregateQuerySnapshotEqual;
|
|
30601
|
+
exports.and = and;
|
|
29835
30602
|
exports.arrayRemove = arrayRemove;
|
|
29836
30603
|
exports.arrayUnion = arrayUnion;
|
|
29837
30604
|
exports.clearIndexedDbPersistence = clearIndexedDbPersistence;
|
|
@@ -29866,6 +30633,7 @@ exports.loadBundle = loadBundle;
|
|
|
29866
30633
|
exports.namedQuery = namedQuery;
|
|
29867
30634
|
exports.onSnapshot = onSnapshot;
|
|
29868
30635
|
exports.onSnapshotsInSync = onSnapshotsInSync;
|
|
30636
|
+
exports.or = or;
|
|
29869
30637
|
exports.orderBy = orderBy;
|
|
29870
30638
|
exports.query = query;
|
|
29871
30639
|
exports.queryEqual = queryEqual;
|