@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/lite/index.node.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { FirebaseError, getDefaultEmulatorHostnameAndPort, createMockUserToken,
|
|
|
6
6
|
import nodeFetch from 'node-fetch';
|
|
7
7
|
import { randomBytes as randomBytes$1 } from 'crypto';
|
|
8
8
|
|
|
9
|
-
const version$1 = "3.
|
|
9
|
+
const version$1 = "3.8.0";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @license
|
|
@@ -59,7 +59,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
59
59
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
60
60
|
User.MOCK_USER = new User('mock-user');
|
|
61
61
|
|
|
62
|
-
const version = "9.
|
|
62
|
+
const version = "9.15.0";
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* @license
|
|
@@ -1470,7 +1470,8 @@ class FetchConnection extends RestConnection {
|
|
|
1470
1470
|
body: requestJson
|
|
1471
1471
|
});
|
|
1472
1472
|
}
|
|
1473
|
-
catch (
|
|
1473
|
+
catch (e) {
|
|
1474
|
+
const err = e;
|
|
1474
1475
|
throw new FirestoreError(mapCodeFromHttpStatus(err.status), 'Request failed with error: ' + err.statusText);
|
|
1475
1476
|
}
|
|
1476
1477
|
if (!response.ok) {
|
|
@@ -1604,159 +1605,29 @@ function arrayEquals(left, right, comparator) {
|
|
|
1604
1605
|
* See the License for the specific language governing permissions and
|
|
1605
1606
|
* limitations under the License.
|
|
1606
1607
|
*/
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
* A `Timestamp` represents a point in time independent of any time zone or
|
|
1613
|
-
* calendar, represented as seconds and fractions of seconds at nanosecond
|
|
1614
|
-
* resolution in UTC Epoch time.
|
|
1615
|
-
*
|
|
1616
|
-
* It is encoded using the Proleptic Gregorian Calendar which extends the
|
|
1617
|
-
* Gregorian calendar backwards to year one. It is encoded assuming all minutes
|
|
1618
|
-
* are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
|
|
1619
|
-
* table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
|
|
1620
|
-
* 9999-12-31T23:59:59.999999999Z.
|
|
1621
|
-
*
|
|
1622
|
-
* For examples and further specifications, refer to the
|
|
1623
|
-
* {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
|
|
1624
|
-
*/
|
|
1625
|
-
class Timestamp {
|
|
1626
|
-
/**
|
|
1627
|
-
* Creates a new timestamp.
|
|
1628
|
-
*
|
|
1629
|
-
* @param seconds - The number of seconds of UTC time since Unix epoch
|
|
1630
|
-
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
|
1631
|
-
* 9999-12-31T23:59:59Z inclusive.
|
|
1632
|
-
* @param nanoseconds - The non-negative fractions of a second at nanosecond
|
|
1633
|
-
* resolution. Negative second values with fractions must still have
|
|
1634
|
-
* non-negative nanoseconds values that count forward in time. Must be
|
|
1635
|
-
* from 0 to 999,999,999 inclusive.
|
|
1636
|
-
*/
|
|
1637
|
-
constructor(
|
|
1638
|
-
/**
|
|
1639
|
-
* The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
|
|
1640
|
-
*/
|
|
1641
|
-
seconds,
|
|
1642
|
-
/**
|
|
1643
|
-
* The fractions of a second at nanosecond resolution.*
|
|
1644
|
-
*/
|
|
1645
|
-
nanoseconds) {
|
|
1646
|
-
this.seconds = seconds;
|
|
1647
|
-
this.nanoseconds = nanoseconds;
|
|
1648
|
-
if (nanoseconds < 0) {
|
|
1649
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
|
|
1650
|
-
}
|
|
1651
|
-
if (nanoseconds >= 1e9) {
|
|
1652
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
|
|
1653
|
-
}
|
|
1654
|
-
if (seconds < MIN_SECONDS) {
|
|
1655
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
|
|
1656
|
-
}
|
|
1657
|
-
// This will break in the year 10,000.
|
|
1658
|
-
if (seconds >= 253402300800) {
|
|
1659
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
|
|
1608
|
+
function objectSize(obj) {
|
|
1609
|
+
let count = 0;
|
|
1610
|
+
for (const key in obj) {
|
|
1611
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1612
|
+
count++;
|
|
1660
1613
|
}
|
|
1661
1614
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
return Timestamp.fromMillis(Date.now());
|
|
1669
|
-
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Creates a new timestamp from the given date.
|
|
1672
|
-
*
|
|
1673
|
-
* @param date - The date to initialize the `Timestamp` from.
|
|
1674
|
-
* @returns A new `Timestamp` representing the same point in time as the given
|
|
1675
|
-
* date.
|
|
1676
|
-
*/
|
|
1677
|
-
static fromDate(date) {
|
|
1678
|
-
return Timestamp.fromMillis(date.getTime());
|
|
1679
|
-
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Creates a new timestamp from the given number of milliseconds.
|
|
1682
|
-
*
|
|
1683
|
-
* @param milliseconds - Number of milliseconds since Unix epoch
|
|
1684
|
-
* 1970-01-01T00:00:00Z.
|
|
1685
|
-
* @returns A new `Timestamp` representing the same point in time as the given
|
|
1686
|
-
* number of milliseconds.
|
|
1687
|
-
*/
|
|
1688
|
-
static fromMillis(milliseconds) {
|
|
1689
|
-
const seconds = Math.floor(milliseconds / 1000);
|
|
1690
|
-
const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
|
|
1691
|
-
return new Timestamp(seconds, nanos);
|
|
1692
|
-
}
|
|
1693
|
-
/**
|
|
1694
|
-
* Converts a `Timestamp` to a JavaScript `Date` object. This conversion
|
|
1695
|
-
* causes a loss of precision since `Date` objects only support millisecond
|
|
1696
|
-
* precision.
|
|
1697
|
-
*
|
|
1698
|
-
* @returns JavaScript `Date` object representing the same point in time as
|
|
1699
|
-
* this `Timestamp`, with millisecond precision.
|
|
1700
|
-
*/
|
|
1701
|
-
toDate() {
|
|
1702
|
-
return new Date(this.toMillis());
|
|
1703
|
-
}
|
|
1704
|
-
/**
|
|
1705
|
-
* Converts a `Timestamp` to a numeric timestamp (in milliseconds since
|
|
1706
|
-
* epoch). This operation causes a loss of precision.
|
|
1707
|
-
*
|
|
1708
|
-
* @returns The point in time corresponding to this timestamp, represented as
|
|
1709
|
-
* the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
|
|
1710
|
-
*/
|
|
1711
|
-
toMillis() {
|
|
1712
|
-
return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
|
|
1713
|
-
}
|
|
1714
|
-
_compareTo(other) {
|
|
1715
|
-
if (this.seconds === other.seconds) {
|
|
1716
|
-
return primitiveComparator(this.nanoseconds, other.nanoseconds);
|
|
1615
|
+
return count;
|
|
1616
|
+
}
|
|
1617
|
+
function forEach(obj, fn) {
|
|
1618
|
+
for (const key in obj) {
|
|
1619
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1620
|
+
fn(key, obj[key]);
|
|
1717
1621
|
}
|
|
1718
|
-
return primitiveComparator(this.seconds, other.seconds);
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* Returns true if this `Timestamp` is equal to the provided one.
|
|
1722
|
-
*
|
|
1723
|
-
* @param other - The `Timestamp` to compare against.
|
|
1724
|
-
* @returns true if this `Timestamp` is equal to the provided one.
|
|
1725
|
-
*/
|
|
1726
|
-
isEqual(other) {
|
|
1727
|
-
return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
|
|
1728
|
-
}
|
|
1729
|
-
/** Returns a textual representation of this `Timestamp`. */
|
|
1730
|
-
toString() {
|
|
1731
|
-
return ('Timestamp(seconds=' +
|
|
1732
|
-
this.seconds +
|
|
1733
|
-
', nanoseconds=' +
|
|
1734
|
-
this.nanoseconds +
|
|
1735
|
-
')');
|
|
1736
|
-
}
|
|
1737
|
-
/** Returns a JSON-serializable representation of this `Timestamp`. */
|
|
1738
|
-
toJSON() {
|
|
1739
|
-
return { seconds: this.seconds, nanoseconds: this.nanoseconds };
|
|
1740
1622
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
// <seconds> is translated to have a non-negative value and both <seconds>
|
|
1748
|
-
// and <nanoseconds> are left-padded with zeroes to be a consistent length.
|
|
1749
|
-
// Strings with this format then have a lexiographical ordering that matches
|
|
1750
|
-
// the expected ordering. The <seconds> translation is done to avoid having
|
|
1751
|
-
// a leading negative sign (i.e. a leading '-' character) in its string
|
|
1752
|
-
// representation, which would affect its lexiographical ordering.
|
|
1753
|
-
const adjustedSeconds = this.seconds - MIN_SECONDS;
|
|
1754
|
-
// Note: Up to 12 decimal digits are required to represent all valid
|
|
1755
|
-
// 'seconds' values.
|
|
1756
|
-
const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
|
|
1757
|
-
const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
|
|
1758
|
-
return formattedSeconds + '.' + formattedNanoseconds;
|
|
1623
|
+
}
|
|
1624
|
+
function isEmpty(obj) {
|
|
1625
|
+
for (const key in obj) {
|
|
1626
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1627
|
+
return false;
|
|
1628
|
+
}
|
|
1759
1629
|
}
|
|
1630
|
+
return true;
|
|
1760
1631
|
}
|
|
1761
1632
|
|
|
1762
1633
|
/**
|
|
@@ -1776,44 +1647,32 @@ class Timestamp {
|
|
|
1776
1647
|
* limitations under the License.
|
|
1777
1648
|
*/
|
|
1778
1649
|
/**
|
|
1779
|
-
*
|
|
1780
|
-
* timestamp, such as update_time or read_time.
|
|
1650
|
+
* Returns whether a variable is either undefined or null.
|
|
1781
1651
|
*/
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
/** Returns a number representation of the version for use in spec tests. */
|
|
1802
|
-
toMicroseconds() {
|
|
1803
|
-
// Convert to microseconds.
|
|
1804
|
-
return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
|
|
1805
|
-
}
|
|
1806
|
-
toString() {
|
|
1807
|
-
return 'SnapshotVersion(' + this.timestamp.toString() + ')';
|
|
1808
|
-
}
|
|
1809
|
-
toTimestamp() {
|
|
1810
|
-
return this.timestamp;
|
|
1811
|
-
}
|
|
1652
|
+
function isNullOrUndefined(value) {
|
|
1653
|
+
return value === null || value === undefined;
|
|
1654
|
+
}
|
|
1655
|
+
/** Returns whether the value represents -0. */
|
|
1656
|
+
function isNegativeZero(value) {
|
|
1657
|
+
// Detect if the value is -0.0. Based on polyfill from
|
|
1658
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
|
1659
|
+
return value === 0 && 1 / value === 1 / -0;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Returns whether a value is an integer and in the safe integer range
|
|
1663
|
+
* @param value - The value to test for being an integer and in the safe range
|
|
1664
|
+
*/
|
|
1665
|
+
function isSafeInteger(value) {
|
|
1666
|
+
return (typeof value === 'number' &&
|
|
1667
|
+
Number.isInteger(value) &&
|
|
1668
|
+
!isNegativeZero(value) &&
|
|
1669
|
+
value <= Number.MAX_SAFE_INTEGER &&
|
|
1670
|
+
value >= Number.MIN_SAFE_INTEGER);
|
|
1812
1671
|
}
|
|
1813
1672
|
|
|
1814
1673
|
/**
|
|
1815
1674
|
* @license
|
|
1816
|
-
* Copyright
|
|
1675
|
+
* Copyright 2020 Google LLC
|
|
1817
1676
|
*
|
|
1818
1677
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1819
1678
|
* you may not use this file except in compliance with the License.
|
|
@@ -1827,34 +1686,20 @@ class SnapshotVersion {
|
|
|
1827
1686
|
* See the License for the specific language governing permissions and
|
|
1828
1687
|
* limitations under the License.
|
|
1829
1688
|
*/
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
return count;
|
|
1838
|
-
}
|
|
1839
|
-
function forEach(obj, fn) {
|
|
1840
|
-
for (const key in obj) {
|
|
1841
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
1842
|
-
fn(key, obj[key]);
|
|
1843
|
-
}
|
|
1844
|
-
}
|
|
1689
|
+
/** Converts a Base64 encoded string to a binary string. */
|
|
1690
|
+
function decodeBase64(encoded) {
|
|
1691
|
+
// Note: We used to validate the base64 string here via a regular expression.
|
|
1692
|
+
// This was removed to improve the performance of indexing.
|
|
1693
|
+
return Buffer.from(encoded, 'base64').toString('binary');
|
|
1845
1694
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
return false;
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
return true;
|
|
1695
|
+
/** Converts a binary string to a Base64 encoded string. */
|
|
1696
|
+
function encodeBase64(raw) {
|
|
1697
|
+
return Buffer.from(raw, 'binary').toString('base64');
|
|
1853
1698
|
}
|
|
1854
1699
|
|
|
1855
1700
|
/**
|
|
1856
1701
|
* @license
|
|
1857
|
-
* Copyright
|
|
1702
|
+
* Copyright 2020 Google LLC
|
|
1858
1703
|
*
|
|
1859
1704
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1860
1705
|
* you may not use this file except in compliance with the License.
|
|
@@ -1868,445 +1713,1165 @@ function isEmpty(obj) {
|
|
|
1868
1713
|
* See the License for the specific language governing permissions and
|
|
1869
1714
|
* limitations under the License.
|
|
1870
1715
|
*/
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1716
|
+
/**
|
|
1717
|
+
* Immutable class that represents a "proto" byte string.
|
|
1718
|
+
*
|
|
1719
|
+
* Proto byte strings can either be Base64-encoded strings or Uint8Arrays when
|
|
1720
|
+
* sent on the wire. This class abstracts away this differentiation by holding
|
|
1721
|
+
* the proto byte string in a common class that must be converted into a string
|
|
1722
|
+
* before being sent as a proto.
|
|
1723
|
+
* @internal
|
|
1724
|
+
*/
|
|
1725
|
+
class ByteString {
|
|
1726
|
+
constructor(binaryString) {
|
|
1727
|
+
this.binaryString = binaryString;
|
|
1877
1728
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
return new
|
|
1881
|
-
.insert(key, value, this.comparator)
|
|
1882
|
-
.copy(null, null, LLRBNode.BLACK, null, null));
|
|
1729
|
+
static fromBase64String(base64) {
|
|
1730
|
+
const binaryString = decodeBase64(base64);
|
|
1731
|
+
return new ByteString(binaryString);
|
|
1883
1732
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1733
|
+
static fromUint8Array(array) {
|
|
1734
|
+
// TODO(indexing); Remove the copy of the byte string here as this method
|
|
1735
|
+
// is frequently called during indexing.
|
|
1736
|
+
const binaryString = binaryStringFromUint8Array(array);
|
|
1737
|
+
return new ByteString(binaryString);
|
|
1889
1738
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
}
|
|
1901
|
-
else if (cmp > 0) {
|
|
1902
|
-
node = node.right;
|
|
1739
|
+
[Symbol.iterator]() {
|
|
1740
|
+
let i = 0;
|
|
1741
|
+
return {
|
|
1742
|
+
next: () => {
|
|
1743
|
+
if (i < this.binaryString.length) {
|
|
1744
|
+
return { value: this.binaryString.charCodeAt(i++), done: false };
|
|
1745
|
+
}
|
|
1746
|
+
else {
|
|
1747
|
+
return { value: undefined, done: true };
|
|
1748
|
+
}
|
|
1903
1749
|
}
|
|
1904
|
-
}
|
|
1905
|
-
return null;
|
|
1750
|
+
};
|
|
1906
1751
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
indexOf(key) {
|
|
1910
|
-
// Number of nodes that were pruned when descending right
|
|
1911
|
-
let prunedNodes = 0;
|
|
1912
|
-
let node = this.root;
|
|
1913
|
-
while (!node.isEmpty()) {
|
|
1914
|
-
const cmp = this.comparator(key, node.key);
|
|
1915
|
-
if (cmp === 0) {
|
|
1916
|
-
return prunedNodes + node.left.size;
|
|
1917
|
-
}
|
|
1918
|
-
else if (cmp < 0) {
|
|
1919
|
-
node = node.left;
|
|
1920
|
-
}
|
|
1921
|
-
else {
|
|
1922
|
-
// Count all nodes left of the node plus the node itself
|
|
1923
|
-
prunedNodes += node.left.size + 1;
|
|
1924
|
-
node = node.right;
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
// Node not found
|
|
1928
|
-
return -1;
|
|
1752
|
+
toBase64() {
|
|
1753
|
+
return encodeBase64(this.binaryString);
|
|
1929
1754
|
}
|
|
1930
|
-
|
|
1931
|
-
return this.
|
|
1755
|
+
toUint8Array() {
|
|
1756
|
+
return uint8ArrayFromBinaryString(this.binaryString);
|
|
1932
1757
|
}
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
return this.root.size;
|
|
1758
|
+
approximateByteSize() {
|
|
1759
|
+
return this.binaryString.length * 2;
|
|
1936
1760
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
return this.root.minKey();
|
|
1761
|
+
compareTo(other) {
|
|
1762
|
+
return primitiveComparator(this.binaryString, other.binaryString);
|
|
1940
1763
|
}
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
return this.root.maxKey();
|
|
1764
|
+
isEqual(other) {
|
|
1765
|
+
return this.binaryString === other.binaryString;
|
|
1944
1766
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1767
|
+
}
|
|
1768
|
+
ByteString.EMPTY_BYTE_STRING = new ByteString('');
|
|
1769
|
+
/**
|
|
1770
|
+
* Helper function to convert an Uint8array to a binary string.
|
|
1771
|
+
*/
|
|
1772
|
+
function binaryStringFromUint8Array(array) {
|
|
1773
|
+
let binaryString = '';
|
|
1774
|
+
for (let i = 0; i < array.length; ++i) {
|
|
1775
|
+
binaryString += String.fromCharCode(array[i]);
|
|
1951
1776
|
}
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1777
|
+
return binaryString;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* Helper function to convert a binary string to an Uint8Array.
|
|
1781
|
+
*/
|
|
1782
|
+
function uint8ArrayFromBinaryString(binaryString) {
|
|
1783
|
+
const buffer = new Uint8Array(binaryString.length);
|
|
1784
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
1785
|
+
buffer[i] = binaryString.charCodeAt(i);
|
|
1957
1786
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1787
|
+
return buffer;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* @license
|
|
1792
|
+
* Copyright 2020 Google LLC
|
|
1793
|
+
*
|
|
1794
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1795
|
+
* you may not use this file except in compliance with the License.
|
|
1796
|
+
* You may obtain a copy of the License at
|
|
1797
|
+
*
|
|
1798
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1799
|
+
*
|
|
1800
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1801
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1802
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1803
|
+
* See the License for the specific language governing permissions and
|
|
1804
|
+
* limitations under the License.
|
|
1805
|
+
*/
|
|
1806
|
+
// A RegExp matching ISO 8601 UTC timestamps with optional fraction.
|
|
1807
|
+
const ISO_TIMESTAMP_REG_EXP = new RegExp(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/);
|
|
1808
|
+
/**
|
|
1809
|
+
* Converts the possible Proto values for a timestamp value into a "seconds and
|
|
1810
|
+
* nanos" representation.
|
|
1811
|
+
*/
|
|
1812
|
+
function normalizeTimestamp(date) {
|
|
1813
|
+
hardAssert(!!date);
|
|
1814
|
+
// The json interface (for the browser) will return an iso timestamp string,
|
|
1815
|
+
// while the proto js library (for node) will return a
|
|
1816
|
+
// google.protobuf.Timestamp instance.
|
|
1817
|
+
if (typeof date === 'string') {
|
|
1818
|
+
// The date string can have higher precision (nanos) than the Date class
|
|
1819
|
+
// (millis), so we do some custom parsing here.
|
|
1820
|
+
// Parse the nanos right out of the string.
|
|
1821
|
+
let nanos = 0;
|
|
1822
|
+
const fraction = ISO_TIMESTAMP_REG_EXP.exec(date);
|
|
1823
|
+
hardAssert(!!fraction);
|
|
1824
|
+
if (fraction[1]) {
|
|
1825
|
+
// Pad the fraction out to 9 digits (nanos).
|
|
1826
|
+
let nanoStr = fraction[1];
|
|
1827
|
+
nanoStr = (nanoStr + '000000000').substr(0, 9);
|
|
1828
|
+
nanos = Number(nanoStr);
|
|
1829
|
+
}
|
|
1830
|
+
// Parse the date to get the seconds.
|
|
1831
|
+
const parsedDate = new Date(date);
|
|
1832
|
+
const seconds = Math.floor(parsedDate.getTime() / 1000);
|
|
1833
|
+
return { seconds, nanos };
|
|
1965
1834
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
return
|
|
1835
|
+
else {
|
|
1836
|
+
// TODO(b/37282237): Use strings for Proto3 timestamps
|
|
1837
|
+
// assert(!this.options.useProto3Json,
|
|
1838
|
+
// 'The timestamp instance format requires Proto JS.');
|
|
1839
|
+
const seconds = normalizeNumber(date.seconds);
|
|
1840
|
+
const nanos = normalizeNumber(date.nanos);
|
|
1841
|
+
return { seconds, nanos };
|
|
1973
1842
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Converts the possible Proto types for numbers into a JavaScript number.
|
|
1846
|
+
* Returns 0 if the value is not numeric.
|
|
1847
|
+
*/
|
|
1848
|
+
function normalizeNumber(value) {
|
|
1849
|
+
// TODO(bjornick): Handle int64 greater than 53 bits.
|
|
1850
|
+
if (typeof value === 'number') {
|
|
1851
|
+
return value;
|
|
1977
1852
|
}
|
|
1978
|
-
|
|
1979
|
-
return
|
|
1853
|
+
else if (typeof value === 'string') {
|
|
1854
|
+
return Number(value);
|
|
1980
1855
|
}
|
|
1981
|
-
|
|
1982
|
-
return
|
|
1856
|
+
else {
|
|
1857
|
+
return 0;
|
|
1983
1858
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1859
|
+
}
|
|
1860
|
+
/** Converts the possible Proto types for Blobs into a ByteString. */
|
|
1861
|
+
function normalizeByteString(blob) {
|
|
1862
|
+
if (typeof blob === 'string') {
|
|
1863
|
+
return ByteString.fromBase64String(blob);
|
|
1986
1864
|
}
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
1865
|
+
else {
|
|
1866
|
+
return ByteString.fromUint8Array(blob);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* @license
|
|
1872
|
+
* Copyright 2017 Google LLC
|
|
1873
|
+
*
|
|
1874
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1875
|
+
* you may not use this file except in compliance with the License.
|
|
1876
|
+
* You may obtain a copy of the License at
|
|
1877
|
+
*
|
|
1878
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1879
|
+
*
|
|
1880
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1881
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1882
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1883
|
+
* See the License for the specific language governing permissions and
|
|
1884
|
+
* limitations under the License.
|
|
1885
|
+
*/
|
|
1886
|
+
// The earliest date supported by Firestore timestamps (0001-01-01T00:00:00Z).
|
|
1887
|
+
const MIN_SECONDS = -62135596800;
|
|
1888
|
+
// Number of nanoseconds in a millisecond.
|
|
1889
|
+
const MS_TO_NANOS = 1e6;
|
|
1890
|
+
/**
|
|
1891
|
+
* A `Timestamp` represents a point in time independent of any time zone or
|
|
1892
|
+
* calendar, represented as seconds and fractions of seconds at nanosecond
|
|
1893
|
+
* resolution in UTC Epoch time.
|
|
1894
|
+
*
|
|
1895
|
+
* It is encoded using the Proleptic Gregorian Calendar which extends the
|
|
1896
|
+
* Gregorian calendar backwards to year one. It is encoded assuming all minutes
|
|
1897
|
+
* are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
|
|
1898
|
+
* table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
|
|
1899
|
+
* 9999-12-31T23:59:59.999999999Z.
|
|
1900
|
+
*
|
|
1901
|
+
* For examples and further specifications, refer to the
|
|
1902
|
+
* {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
|
|
1903
|
+
*/
|
|
1904
|
+
class Timestamp {
|
|
1905
|
+
/**
|
|
1906
|
+
* Creates a new timestamp.
|
|
1907
|
+
*
|
|
1908
|
+
* @param seconds - The number of seconds of UTC time since Unix epoch
|
|
1909
|
+
* 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
|
|
1910
|
+
* 9999-12-31T23:59:59Z inclusive.
|
|
1911
|
+
* @param nanoseconds - The non-negative fractions of a second at nanosecond
|
|
1912
|
+
* resolution. Negative second values with fractions must still have
|
|
1913
|
+
* non-negative nanoseconds values that count forward in time. Must be
|
|
1914
|
+
* from 0 to 999,999,999 inclusive.
|
|
1915
|
+
*/
|
|
1916
|
+
constructor(
|
|
1917
|
+
/**
|
|
1918
|
+
* The number of seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z.
|
|
1919
|
+
*/
|
|
1920
|
+
seconds,
|
|
1921
|
+
/**
|
|
1922
|
+
* The fractions of a second at nanosecond resolution.*
|
|
1923
|
+
*/
|
|
1924
|
+
nanoseconds) {
|
|
1925
|
+
this.seconds = seconds;
|
|
1926
|
+
this.nanoseconds = nanoseconds;
|
|
1927
|
+
if (nanoseconds < 0) {
|
|
1928
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
|
|
1929
|
+
}
|
|
1930
|
+
if (nanoseconds >= 1e9) {
|
|
1931
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp nanoseconds out of range: ' + nanoseconds);
|
|
1932
|
+
}
|
|
1933
|
+
if (seconds < MIN_SECONDS) {
|
|
1934
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
|
|
1935
|
+
}
|
|
1936
|
+
// This will break in the year 10,000.
|
|
1937
|
+
if (seconds >= 253402300800) {
|
|
1938
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Timestamp seconds out of range: ' + seconds);
|
|
2026
1939
|
}
|
|
2027
1940
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
1941
|
+
/**
|
|
1942
|
+
* Creates a new timestamp with the current date, with millisecond precision.
|
|
1943
|
+
*
|
|
1944
|
+
* @returns a new timestamp representing the current date.
|
|
1945
|
+
*/
|
|
1946
|
+
static now() {
|
|
1947
|
+
return Timestamp.fromMillis(Date.now());
|
|
1948
|
+
}
|
|
1949
|
+
/**
|
|
1950
|
+
* Creates a new timestamp from the given date.
|
|
1951
|
+
*
|
|
1952
|
+
* @param date - The date to initialize the `Timestamp` from.
|
|
1953
|
+
* @returns A new `Timestamp` representing the same point in time as the given
|
|
1954
|
+
* date.
|
|
1955
|
+
*/
|
|
1956
|
+
static fromDate(date) {
|
|
1957
|
+
return Timestamp.fromMillis(date.getTime());
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Creates a new timestamp from the given number of milliseconds.
|
|
1961
|
+
*
|
|
1962
|
+
* @param milliseconds - Number of milliseconds since Unix epoch
|
|
1963
|
+
* 1970-01-01T00:00:00Z.
|
|
1964
|
+
* @returns A new `Timestamp` representing the same point in time as the given
|
|
1965
|
+
* number of milliseconds.
|
|
1966
|
+
*/
|
|
1967
|
+
static fromMillis(milliseconds) {
|
|
1968
|
+
const seconds = Math.floor(milliseconds / 1000);
|
|
1969
|
+
const nanos = Math.floor((milliseconds - seconds * 1000) * MS_TO_NANOS);
|
|
1970
|
+
return new Timestamp(seconds, nanos);
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Converts a `Timestamp` to a JavaScript `Date` object. This conversion
|
|
1974
|
+
* causes a loss of precision since `Date` objects only support millisecond
|
|
1975
|
+
* precision.
|
|
1976
|
+
*
|
|
1977
|
+
* @returns JavaScript `Date` object representing the same point in time as
|
|
1978
|
+
* this `Timestamp`, with millisecond precision.
|
|
1979
|
+
*/
|
|
1980
|
+
toDate() {
|
|
1981
|
+
return new Date(this.toMillis());
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Converts a `Timestamp` to a numeric timestamp (in milliseconds since
|
|
1985
|
+
* epoch). This operation causes a loss of precision.
|
|
1986
|
+
*
|
|
1987
|
+
* @returns The point in time corresponding to this timestamp, represented as
|
|
1988
|
+
* the number of milliseconds since Unix epoch 1970-01-01T00:00:00Z.
|
|
1989
|
+
*/
|
|
1990
|
+
toMillis() {
|
|
1991
|
+
return this.seconds * 1000 + this.nanoseconds / MS_TO_NANOS;
|
|
1992
|
+
}
|
|
1993
|
+
_compareTo(other) {
|
|
1994
|
+
if (this.seconds === other.seconds) {
|
|
1995
|
+
return primitiveComparator(this.nanoseconds, other.nanoseconds);
|
|
1996
|
+
}
|
|
1997
|
+
return primitiveComparator(this.seconds, other.seconds);
|
|
1998
|
+
}
|
|
1999
|
+
/**
|
|
2000
|
+
* Returns true if this `Timestamp` is equal to the provided one.
|
|
2001
|
+
*
|
|
2002
|
+
* @param other - The `Timestamp` to compare against.
|
|
2003
|
+
* @returns true if this `Timestamp` is equal to the provided one.
|
|
2004
|
+
*/
|
|
2005
|
+
isEqual(other) {
|
|
2006
|
+
return (other.seconds === this.seconds && other.nanoseconds === this.nanoseconds);
|
|
2007
|
+
}
|
|
2008
|
+
/** Returns a textual representation of this `Timestamp`. */
|
|
2009
|
+
toString() {
|
|
2010
|
+
return ('Timestamp(seconds=' +
|
|
2011
|
+
this.seconds +
|
|
2012
|
+
', nanoseconds=' +
|
|
2013
|
+
this.nanoseconds +
|
|
2014
|
+
')');
|
|
2015
|
+
}
|
|
2016
|
+
/** Returns a JSON-serializable representation of this `Timestamp`. */
|
|
2017
|
+
toJSON() {
|
|
2018
|
+
return { seconds: this.seconds, nanoseconds: this.nanoseconds };
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Converts this object to a primitive string, which allows `Timestamp` objects
|
|
2022
|
+
* to be compared using the `>`, `<=`, `>=` and `>` operators.
|
|
2023
|
+
*/
|
|
2024
|
+
valueOf() {
|
|
2025
|
+
// This method returns a string of the form <seconds>.<nanoseconds> where
|
|
2026
|
+
// <seconds> is translated to have a non-negative value and both <seconds>
|
|
2027
|
+
// and <nanoseconds> are left-padded with zeroes to be a consistent length.
|
|
2028
|
+
// Strings with this format then have a lexiographical ordering that matches
|
|
2029
|
+
// the expected ordering. The <seconds> translation is done to avoid having
|
|
2030
|
+
// a leading negative sign (i.e. a leading '-' character) in its string
|
|
2031
|
+
// representation, which would affect its lexiographical ordering.
|
|
2032
|
+
const adjustedSeconds = this.seconds - MIN_SECONDS;
|
|
2033
|
+
// Note: Up to 12 decimal digits are required to represent all valid
|
|
2034
|
+
// 'seconds' values.
|
|
2035
|
+
const formattedSeconds = String(adjustedSeconds).padStart(12, '0');
|
|
2036
|
+
const formattedNanoseconds = String(this.nanoseconds).padStart(9, '0');
|
|
2037
|
+
return formattedSeconds + '.' + formattedNanoseconds;
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/**
|
|
2042
|
+
* @license
|
|
2043
|
+
* Copyright 2020 Google LLC
|
|
2044
|
+
*
|
|
2045
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2046
|
+
* you may not use this file except in compliance with the License.
|
|
2047
|
+
* You may obtain a copy of the License at
|
|
2048
|
+
*
|
|
2049
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2050
|
+
*
|
|
2051
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2052
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2053
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2054
|
+
* See the License for the specific language governing permissions and
|
|
2055
|
+
* limitations under the License.
|
|
2056
|
+
*/
|
|
2057
|
+
/**
|
|
2058
|
+
* Represents a locally-applied ServerTimestamp.
|
|
2059
|
+
*
|
|
2060
|
+
* Server Timestamps are backed by MapValues that contain an internal field
|
|
2061
|
+
* `__type__` with a value of `server_timestamp`. The previous value and local
|
|
2062
|
+
* write time are stored in its `__previous_value__` and `__local_write_time__`
|
|
2063
|
+
* fields respectively.
|
|
2064
|
+
*
|
|
2065
|
+
* Notes:
|
|
2066
|
+
* - ServerTimestampValue instances are created as the result of applying a
|
|
2067
|
+
* transform. They can only exist in the local view of a document. Therefore
|
|
2068
|
+
* they do not need to be parsed or serialized.
|
|
2069
|
+
* - When evaluated locally (e.g. for snapshot.data()), they by default
|
|
2070
|
+
* evaluate to `null`. This behavior can be configured by passing custom
|
|
2071
|
+
* FieldValueOptions to value().
|
|
2072
|
+
* - With respect to other ServerTimestampValues, they sort by their
|
|
2073
|
+
* localWriteTime.
|
|
2074
|
+
*/
|
|
2075
|
+
const SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';
|
|
2076
|
+
const TYPE_KEY = '__type__';
|
|
2077
|
+
const PREVIOUS_VALUE_KEY = '__previous_value__';
|
|
2078
|
+
const LOCAL_WRITE_TIME_KEY = '__local_write_time__';
|
|
2079
|
+
function isServerTimestamp(value) {
|
|
2080
|
+
var _a, _b;
|
|
2081
|
+
const type = (_b = (((_a = value === null || value === void 0 ? void 0 : value.mapValue) === null || _a === void 0 ? void 0 : _a.fields) || {})[TYPE_KEY]) === null || _b === void 0 ? void 0 : _b.stringValue;
|
|
2082
|
+
return type === SERVER_TIMESTAMP_SENTINEL;
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* Returns the value of the field before this ServerTimestamp was set.
|
|
2086
|
+
*
|
|
2087
|
+
* Preserving the previous values allows the user to display the last resoled
|
|
2088
|
+
* value until the backend responds with the timestamp.
|
|
2089
|
+
*/
|
|
2090
|
+
function getPreviousValue(value) {
|
|
2091
|
+
const previousValue = value.mapValue.fields[PREVIOUS_VALUE_KEY];
|
|
2092
|
+
if (isServerTimestamp(previousValue)) {
|
|
2093
|
+
return getPreviousValue(previousValue);
|
|
2094
|
+
}
|
|
2095
|
+
return previousValue;
|
|
2096
|
+
}
|
|
2097
|
+
/**
|
|
2098
|
+
* Returns the local time at which this timestamp was first set.
|
|
2099
|
+
*/
|
|
2100
|
+
function getLocalWriteTime(value) {
|
|
2101
|
+
const localWriteTime = normalizeTimestamp(value.mapValue.fields[LOCAL_WRITE_TIME_KEY].timestampValue);
|
|
2102
|
+
return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
/**
|
|
2106
|
+
* @license
|
|
2107
|
+
* Copyright 2020 Google LLC
|
|
2108
|
+
*
|
|
2109
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2110
|
+
* you may not use this file except in compliance with the License.
|
|
2111
|
+
* You may obtain a copy of the License at
|
|
2112
|
+
*
|
|
2113
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2114
|
+
*
|
|
2115
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2116
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2117
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2118
|
+
* See the License for the specific language governing permissions and
|
|
2119
|
+
* limitations under the License.
|
|
2120
|
+
*/
|
|
2121
|
+
const MAX_VALUE_TYPE = '__max__';
|
|
2122
|
+
const MAX_VALUE = {
|
|
2123
|
+
mapValue: {
|
|
2124
|
+
fields: {
|
|
2125
|
+
'__type__': { stringValue: MAX_VALUE_TYPE }
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
/** Extracts the backend's type order for the provided value. */
|
|
2130
|
+
function typeOrder(value) {
|
|
2131
|
+
if ('nullValue' in value) {
|
|
2132
|
+
return 0 /* TypeOrder.NullValue */;
|
|
2133
|
+
}
|
|
2134
|
+
else if ('booleanValue' in value) {
|
|
2135
|
+
return 1 /* TypeOrder.BooleanValue */;
|
|
2136
|
+
}
|
|
2137
|
+
else if ('integerValue' in value || 'doubleValue' in value) {
|
|
2138
|
+
return 2 /* TypeOrder.NumberValue */;
|
|
2139
|
+
}
|
|
2140
|
+
else if ('timestampValue' in value) {
|
|
2141
|
+
return 3 /* TypeOrder.TimestampValue */;
|
|
2142
|
+
}
|
|
2143
|
+
else if ('stringValue' in value) {
|
|
2144
|
+
return 5 /* TypeOrder.StringValue */;
|
|
2145
|
+
}
|
|
2146
|
+
else if ('bytesValue' in value) {
|
|
2147
|
+
return 6 /* TypeOrder.BlobValue */;
|
|
2148
|
+
}
|
|
2149
|
+
else if ('referenceValue' in value) {
|
|
2150
|
+
return 7 /* TypeOrder.RefValue */;
|
|
2151
|
+
}
|
|
2152
|
+
else if ('geoPointValue' in value) {
|
|
2153
|
+
return 8 /* TypeOrder.GeoPointValue */;
|
|
2154
|
+
}
|
|
2155
|
+
else if ('arrayValue' in value) {
|
|
2156
|
+
return 9 /* TypeOrder.ArrayValue */;
|
|
2157
|
+
}
|
|
2158
|
+
else if ('mapValue' in value) {
|
|
2159
|
+
if (isServerTimestamp(value)) {
|
|
2160
|
+
return 4 /* TypeOrder.ServerTimestampValue */;
|
|
2161
|
+
}
|
|
2162
|
+
else if (isMaxValue(value)) {
|
|
2163
|
+
return 9007199254740991 /* TypeOrder.MaxValue */;
|
|
2164
|
+
}
|
|
2165
|
+
return 10 /* TypeOrder.ObjectValue */;
|
|
2166
|
+
}
|
|
2167
|
+
else {
|
|
2168
|
+
return fail();
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
/** Tests `left` and `right` for equality based on the backend semantics. */
|
|
2172
|
+
function valueEquals(left, right) {
|
|
2173
|
+
if (left === right) {
|
|
2174
|
+
return true;
|
|
2175
|
+
}
|
|
2176
|
+
const leftType = typeOrder(left);
|
|
2177
|
+
const rightType = typeOrder(right);
|
|
2178
|
+
if (leftType !== rightType) {
|
|
2179
|
+
return false;
|
|
2180
|
+
}
|
|
2181
|
+
switch (leftType) {
|
|
2182
|
+
case 0 /* TypeOrder.NullValue */:
|
|
2183
|
+
return true;
|
|
2184
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
2185
|
+
return left.booleanValue === right.booleanValue;
|
|
2186
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
2187
|
+
return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
|
|
2188
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
2189
|
+
return timestampEquals(left, right);
|
|
2190
|
+
case 5 /* TypeOrder.StringValue */:
|
|
2191
|
+
return left.stringValue === right.stringValue;
|
|
2192
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
2193
|
+
return blobEquals(left, right);
|
|
2194
|
+
case 7 /* TypeOrder.RefValue */:
|
|
2195
|
+
return left.referenceValue === right.referenceValue;
|
|
2196
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
2197
|
+
return geoPointEquals(left, right);
|
|
2198
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
2199
|
+
return numberEquals(left, right);
|
|
2200
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
2201
|
+
return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
|
|
2202
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
2203
|
+
return objectEquals(left, right);
|
|
2204
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
2205
|
+
return true;
|
|
2206
|
+
default:
|
|
2207
|
+
return fail();
|
|
2208
|
+
}
|
|
2209
|
+
}
|
|
2210
|
+
function timestampEquals(left, right) {
|
|
2211
|
+
if (typeof left.timestampValue === 'string' &&
|
|
2212
|
+
typeof right.timestampValue === 'string' &&
|
|
2213
|
+
left.timestampValue.length === right.timestampValue.length) {
|
|
2214
|
+
// Use string equality for ISO 8601 timestamps
|
|
2215
|
+
return left.timestampValue === right.timestampValue;
|
|
2216
|
+
}
|
|
2217
|
+
const leftTimestamp = normalizeTimestamp(left.timestampValue);
|
|
2218
|
+
const rightTimestamp = normalizeTimestamp(right.timestampValue);
|
|
2219
|
+
return (leftTimestamp.seconds === rightTimestamp.seconds &&
|
|
2220
|
+
leftTimestamp.nanos === rightTimestamp.nanos);
|
|
2221
|
+
}
|
|
2222
|
+
function geoPointEquals(left, right) {
|
|
2223
|
+
return (normalizeNumber(left.geoPointValue.latitude) ===
|
|
2224
|
+
normalizeNumber(right.geoPointValue.latitude) &&
|
|
2225
|
+
normalizeNumber(left.geoPointValue.longitude) ===
|
|
2226
|
+
normalizeNumber(right.geoPointValue.longitude));
|
|
2227
|
+
}
|
|
2228
|
+
function blobEquals(left, right) {
|
|
2229
|
+
return normalizeByteString(left.bytesValue).isEqual(normalizeByteString(right.bytesValue));
|
|
2230
|
+
}
|
|
2231
|
+
function numberEquals(left, right) {
|
|
2232
|
+
if ('integerValue' in left && 'integerValue' in right) {
|
|
2233
|
+
return (normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue));
|
|
2234
|
+
}
|
|
2235
|
+
else if ('doubleValue' in left && 'doubleValue' in right) {
|
|
2236
|
+
const n1 = normalizeNumber(left.doubleValue);
|
|
2237
|
+
const n2 = normalizeNumber(right.doubleValue);
|
|
2238
|
+
if (n1 === n2) {
|
|
2239
|
+
return isNegativeZero(n1) === isNegativeZero(n2);
|
|
2240
|
+
}
|
|
2241
|
+
else {
|
|
2242
|
+
return isNaN(n1) && isNaN(n2);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
return false;
|
|
2246
|
+
}
|
|
2247
|
+
function objectEquals(left, right) {
|
|
2248
|
+
const leftMap = left.mapValue.fields || {};
|
|
2249
|
+
const rightMap = right.mapValue.fields || {};
|
|
2250
|
+
if (objectSize(leftMap) !== objectSize(rightMap)) {
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2253
|
+
for (const key in leftMap) {
|
|
2254
|
+
if (leftMap.hasOwnProperty(key)) {
|
|
2255
|
+
if (rightMap[key] === undefined ||
|
|
2256
|
+
!valueEquals(leftMap[key], rightMap[key])) {
|
|
2257
|
+
return false;
|
|
2036
2258
|
}
|
|
2037
2259
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2260
|
+
}
|
|
2261
|
+
return true;
|
|
2262
|
+
}
|
|
2263
|
+
/** Returns true if the ArrayValue contains the specified element. */
|
|
2264
|
+
function arrayValueContains(haystack, needle) {
|
|
2265
|
+
return ((haystack.values || []).find(v => valueEquals(v, needle)) !== undefined);
|
|
2266
|
+
}
|
|
2267
|
+
function valueCompare(left, right) {
|
|
2268
|
+
if (left === right) {
|
|
2269
|
+
return 0;
|
|
2270
|
+
}
|
|
2271
|
+
const leftType = typeOrder(left);
|
|
2272
|
+
const rightType = typeOrder(right);
|
|
2273
|
+
if (leftType !== rightType) {
|
|
2274
|
+
return primitiveComparator(leftType, rightType);
|
|
2275
|
+
}
|
|
2276
|
+
switch (leftType) {
|
|
2277
|
+
case 0 /* TypeOrder.NullValue */:
|
|
2278
|
+
case 9007199254740991 /* TypeOrder.MaxValue */:
|
|
2279
|
+
return 0;
|
|
2280
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
2281
|
+
return primitiveComparator(left.booleanValue, right.booleanValue);
|
|
2282
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
2283
|
+
return compareNumbers(left, right);
|
|
2284
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
2285
|
+
return compareTimestamps(left.timestampValue, right.timestampValue);
|
|
2286
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
2287
|
+
return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
|
|
2288
|
+
case 5 /* TypeOrder.StringValue */:
|
|
2289
|
+
return primitiveComparator(left.stringValue, right.stringValue);
|
|
2290
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
2291
|
+
return compareBlobs(left.bytesValue, right.bytesValue);
|
|
2292
|
+
case 7 /* TypeOrder.RefValue */:
|
|
2293
|
+
return compareReferences(left.referenceValue, right.referenceValue);
|
|
2294
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
2295
|
+
return compareGeoPoints(left.geoPointValue, right.geoPointValue);
|
|
2296
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
2297
|
+
return compareArrays(left.arrayValue, right.arrayValue);
|
|
2298
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
2299
|
+
return compareMaps(left.mapValue, right.mapValue);
|
|
2300
|
+
default:
|
|
2301
|
+
throw fail();
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
function compareNumbers(left, right) {
|
|
2305
|
+
const leftNumber = normalizeNumber(left.integerValue || left.doubleValue);
|
|
2306
|
+
const rightNumber = normalizeNumber(right.integerValue || right.doubleValue);
|
|
2307
|
+
if (leftNumber < rightNumber) {
|
|
2308
|
+
return -1;
|
|
2309
|
+
}
|
|
2310
|
+
else if (leftNumber > rightNumber) {
|
|
2311
|
+
return 1;
|
|
2312
|
+
}
|
|
2313
|
+
else if (leftNumber === rightNumber) {
|
|
2314
|
+
return 0;
|
|
2315
|
+
}
|
|
2316
|
+
else {
|
|
2317
|
+
// one or both are NaN.
|
|
2318
|
+
if (isNaN(leftNumber)) {
|
|
2319
|
+
return isNaN(rightNumber) ? 0 : -1;
|
|
2320
|
+
}
|
|
2321
|
+
else {
|
|
2322
|
+
return 1;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
function compareTimestamps(left, right) {
|
|
2327
|
+
if (typeof left === 'string' &&
|
|
2328
|
+
typeof right === 'string' &&
|
|
2329
|
+
left.length === right.length) {
|
|
2330
|
+
return primitiveComparator(left, right);
|
|
2331
|
+
}
|
|
2332
|
+
const leftTimestamp = normalizeTimestamp(left);
|
|
2333
|
+
const rightTimestamp = normalizeTimestamp(right);
|
|
2334
|
+
const comparison = primitiveComparator(leftTimestamp.seconds, rightTimestamp.seconds);
|
|
2335
|
+
if (comparison !== 0) {
|
|
2336
|
+
return comparison;
|
|
2337
|
+
}
|
|
2338
|
+
return primitiveComparator(leftTimestamp.nanos, rightTimestamp.nanos);
|
|
2339
|
+
}
|
|
2340
|
+
function compareReferences(leftPath, rightPath) {
|
|
2341
|
+
const leftSegments = leftPath.split('/');
|
|
2342
|
+
const rightSegments = rightPath.split('/');
|
|
2343
|
+
for (let i = 0; i < leftSegments.length && i < rightSegments.length; i++) {
|
|
2344
|
+
const comparison = primitiveComparator(leftSegments[i], rightSegments[i]);
|
|
2345
|
+
if (comparison !== 0) {
|
|
2346
|
+
return comparison;
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
return primitiveComparator(leftSegments.length, rightSegments.length);
|
|
2350
|
+
}
|
|
2351
|
+
function compareGeoPoints(left, right) {
|
|
2352
|
+
const comparison = primitiveComparator(normalizeNumber(left.latitude), normalizeNumber(right.latitude));
|
|
2353
|
+
if (comparison !== 0) {
|
|
2354
|
+
return comparison;
|
|
2355
|
+
}
|
|
2356
|
+
return primitiveComparator(normalizeNumber(left.longitude), normalizeNumber(right.longitude));
|
|
2357
|
+
}
|
|
2358
|
+
function compareBlobs(left, right) {
|
|
2359
|
+
const leftBytes = normalizeByteString(left);
|
|
2360
|
+
const rightBytes = normalizeByteString(right);
|
|
2361
|
+
return leftBytes.compareTo(rightBytes);
|
|
2362
|
+
}
|
|
2363
|
+
function compareArrays(left, right) {
|
|
2364
|
+
const leftArray = left.values || [];
|
|
2365
|
+
const rightArray = right.values || [];
|
|
2366
|
+
for (let i = 0; i < leftArray.length && i < rightArray.length; ++i) {
|
|
2367
|
+
const compare = valueCompare(leftArray[i], rightArray[i]);
|
|
2368
|
+
if (compare) {
|
|
2369
|
+
return compare;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
return primitiveComparator(leftArray.length, rightArray.length);
|
|
2373
|
+
}
|
|
2374
|
+
function compareMaps(left, right) {
|
|
2375
|
+
if (left === MAX_VALUE.mapValue && right === MAX_VALUE.mapValue) {
|
|
2376
|
+
return 0;
|
|
2377
|
+
}
|
|
2378
|
+
else if (left === MAX_VALUE.mapValue) {
|
|
2379
|
+
return 1;
|
|
2380
|
+
}
|
|
2381
|
+
else if (right === MAX_VALUE.mapValue) {
|
|
2382
|
+
return -1;
|
|
2383
|
+
}
|
|
2384
|
+
const leftMap = left.fields || {};
|
|
2385
|
+
const leftKeys = Object.keys(leftMap);
|
|
2386
|
+
const rightMap = right.fields || {};
|
|
2387
|
+
const rightKeys = Object.keys(rightMap);
|
|
2388
|
+
// Even though MapValues are likely sorted correctly based on their insertion
|
|
2389
|
+
// order (e.g. when received from the backend), local modifications can bring
|
|
2390
|
+
// elements out of order. We need to re-sort the elements to ensure that
|
|
2391
|
+
// canonical IDs are independent of insertion order.
|
|
2392
|
+
leftKeys.sort();
|
|
2393
|
+
rightKeys.sort();
|
|
2394
|
+
for (let i = 0; i < leftKeys.length && i < rightKeys.length; ++i) {
|
|
2395
|
+
const keyCompare = primitiveComparator(leftKeys[i], rightKeys[i]);
|
|
2396
|
+
if (keyCompare !== 0) {
|
|
2397
|
+
return keyCompare;
|
|
2398
|
+
}
|
|
2399
|
+
const compare = valueCompare(leftMap[leftKeys[i]], rightMap[rightKeys[i]]);
|
|
2400
|
+
if (compare !== 0) {
|
|
2401
|
+
return compare;
|
|
2044
2402
|
}
|
|
2045
|
-
return result;
|
|
2046
|
-
}
|
|
2047
|
-
hasNext() {
|
|
2048
|
-
return this.nodeStack.length > 0;
|
|
2049
2403
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2404
|
+
return primitiveComparator(leftKeys.length, rightKeys.length);
|
|
2405
|
+
}
|
|
2406
|
+
/** Returns a reference value for the provided database and key. */
|
|
2407
|
+
function refValue(databaseId, key) {
|
|
2408
|
+
return {
|
|
2409
|
+
referenceValue: `projects/${databaseId.projectId}/databases/${databaseId.database}/documents/${key.path.canonicalString()}`
|
|
2410
|
+
};
|
|
2411
|
+
}
|
|
2412
|
+
/** Returns true if `value` is an ArrayValue. */
|
|
2413
|
+
function isArray(value) {
|
|
2414
|
+
return !!value && 'arrayValue' in value;
|
|
2415
|
+
}
|
|
2416
|
+
/** Returns true if `value` is a NullValue. */
|
|
2417
|
+
function isNullValue(value) {
|
|
2418
|
+
return !!value && 'nullValue' in value;
|
|
2419
|
+
}
|
|
2420
|
+
/** Returns true if `value` is NaN. */
|
|
2421
|
+
function isNanValue(value) {
|
|
2422
|
+
return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue));
|
|
2423
|
+
}
|
|
2424
|
+
/** Returns true if `value` is a MapValue. */
|
|
2425
|
+
function isMapValue(value) {
|
|
2426
|
+
return !!value && 'mapValue' in value;
|
|
2427
|
+
}
|
|
2428
|
+
/** Creates a deep copy of `source`. */
|
|
2429
|
+
function deepClone(source) {
|
|
2430
|
+
if (source.geoPointValue) {
|
|
2431
|
+
return { geoPointValue: Object.assign({}, source.geoPointValue) };
|
|
2056
2432
|
}
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
constructor(key, value, color, left, right) {
|
|
2061
|
-
this.key = key;
|
|
2062
|
-
this.value = value;
|
|
2063
|
-
this.color = color != null ? color : LLRBNode.RED;
|
|
2064
|
-
this.left = left != null ? left : LLRBNode.EMPTY;
|
|
2065
|
-
this.right = right != null ? right : LLRBNode.EMPTY;
|
|
2066
|
-
this.size = this.left.size + 1 + this.right.size;
|
|
2433
|
+
else if (source.timestampValue &&
|
|
2434
|
+
typeof source.timestampValue === 'object') {
|
|
2435
|
+
return { timestampValue: Object.assign({}, source.timestampValue) };
|
|
2067
2436
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2437
|
+
else if (source.mapValue) {
|
|
2438
|
+
const target = { mapValue: { fields: {} } };
|
|
2439
|
+
forEach(source.mapValue.fields, (key, val) => (target.mapValue.fields[key] = deepClone(val)));
|
|
2440
|
+
return target;
|
|
2071
2441
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2442
|
+
else if (source.arrayValue) {
|
|
2443
|
+
const target = { arrayValue: { values: [] } };
|
|
2444
|
+
for (let i = 0; i < (source.arrayValue.values || []).length; ++i) {
|
|
2445
|
+
target.arrayValue.values[i] = deepClone(source.arrayValue.values[i]);
|
|
2446
|
+
}
|
|
2447
|
+
return target;
|
|
2074
2448
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
// Returns the first truthy value returned by action, or the last falsey
|
|
2078
|
-
// value returned by action.
|
|
2079
|
-
inorderTraversal(action) {
|
|
2080
|
-
return (this.left.inorderTraversal(action) ||
|
|
2081
|
-
action(this.key, this.value) ||
|
|
2082
|
-
this.right.inorderTraversal(action));
|
|
2449
|
+
else {
|
|
2450
|
+
return Object.assign({}, source);
|
|
2083
2451
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2452
|
+
}
|
|
2453
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
2454
|
+
function isMaxValue(value) {
|
|
2455
|
+
return ((((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue ===
|
|
2456
|
+
MAX_VALUE_TYPE);
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
/**
|
|
2460
|
+
* @license
|
|
2461
|
+
* Copyright 2022 Google LLC
|
|
2462
|
+
*
|
|
2463
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2464
|
+
* you may not use this file except in compliance with the License.
|
|
2465
|
+
* You may obtain a copy of the License at
|
|
2466
|
+
*
|
|
2467
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2468
|
+
*
|
|
2469
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2470
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2471
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2472
|
+
* See the License for the specific language governing permissions and
|
|
2473
|
+
* limitations under the License.
|
|
2474
|
+
*/
|
|
2475
|
+
/**
|
|
2476
|
+
* Represents a bound of a query.
|
|
2477
|
+
*
|
|
2478
|
+
* The bound is specified with the given components representing a position and
|
|
2479
|
+
* whether it's just before or just after the position (relative to whatever the
|
|
2480
|
+
* query order is).
|
|
2481
|
+
*
|
|
2482
|
+
* The position represents a logical index position for a query. It's a prefix
|
|
2483
|
+
* of values for the (potentially implicit) order by clauses of a query.
|
|
2484
|
+
*
|
|
2485
|
+
* Bound provides a function to determine whether a document comes before or
|
|
2486
|
+
* after a bound. This is influenced by whether the position is just before or
|
|
2487
|
+
* just after the provided values.
|
|
2488
|
+
*/
|
|
2489
|
+
class Bound {
|
|
2490
|
+
constructor(position, inclusive) {
|
|
2491
|
+
this.position = position;
|
|
2492
|
+
this.inclusive = inclusive;
|
|
2092
2493
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
}
|
|
2098
|
-
else {
|
|
2099
|
-
return this.left.min();
|
|
2100
|
-
}
|
|
2494
|
+
}
|
|
2495
|
+
function boundEquals(left, right) {
|
|
2496
|
+
if (left === null) {
|
|
2497
|
+
return right === null;
|
|
2101
2498
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
return this.min().key;
|
|
2499
|
+
else if (right === null) {
|
|
2500
|
+
return false;
|
|
2105
2501
|
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
return this.key;
|
|
2110
|
-
}
|
|
2111
|
-
else {
|
|
2112
|
-
return this.right.maxKey();
|
|
2113
|
-
}
|
|
2502
|
+
if (left.inclusive !== right.inclusive ||
|
|
2503
|
+
left.position.length !== right.position.length) {
|
|
2504
|
+
return false;
|
|
2114
2505
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
n = n.copy(null, null, null, n.left.insert(key, value, comparator), null);
|
|
2121
|
-
}
|
|
2122
|
-
else if (cmp === 0) {
|
|
2123
|
-
n = n.copy(null, value, null, null, null);
|
|
2124
|
-
}
|
|
2125
|
-
else {
|
|
2126
|
-
n = n.copy(null, null, null, null, n.right.insert(key, value, comparator));
|
|
2506
|
+
for (let i = 0; i < left.position.length; i++) {
|
|
2507
|
+
const leftPosition = left.position[i];
|
|
2508
|
+
const rightPosition = right.position[i];
|
|
2509
|
+
if (!valueEquals(leftPosition, rightPosition)) {
|
|
2510
|
+
return false;
|
|
2127
2511
|
}
|
|
2128
|
-
return n.fixUp();
|
|
2129
2512
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2513
|
+
return true;
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
/**
|
|
2517
|
+
* @license
|
|
2518
|
+
* Copyright 2022 Google LLC
|
|
2519
|
+
*
|
|
2520
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2521
|
+
* you may not use this file except in compliance with the License.
|
|
2522
|
+
* You may obtain a copy of the License at
|
|
2523
|
+
*
|
|
2524
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2525
|
+
*
|
|
2526
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2527
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2528
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2529
|
+
* See the License for the specific language governing permissions and
|
|
2530
|
+
* limitations under the License.
|
|
2531
|
+
*/
|
|
2532
|
+
class Filter {
|
|
2533
|
+
}
|
|
2534
|
+
class FieldFilter extends Filter {
|
|
2535
|
+
constructor(field, op, value) {
|
|
2536
|
+
super();
|
|
2537
|
+
this.field = field;
|
|
2538
|
+
this.op = op;
|
|
2539
|
+
this.value = value;
|
|
2140
2540
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
if (
|
|
2146
|
-
if (
|
|
2147
|
-
|
|
2148
|
-
}
|
|
2149
|
-
n = n.copy(null, null, null, n.left.remove(key, comparator), null);
|
|
2150
|
-
}
|
|
2151
|
-
else {
|
|
2152
|
-
if (n.left.isRed()) {
|
|
2153
|
-
n = n.rotateRight();
|
|
2154
|
-
}
|
|
2155
|
-
if (!n.right.isEmpty() && !n.right.isRed() && !n.right.left.isRed()) {
|
|
2156
|
-
n = n.moveRedRight();
|
|
2541
|
+
/**
|
|
2542
|
+
* Creates a filter based on the provided arguments.
|
|
2543
|
+
*/
|
|
2544
|
+
static create(field, op, value) {
|
|
2545
|
+
if (field.isKeyField()) {
|
|
2546
|
+
if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
2547
|
+
return this.createKeyFieldInFilter(field, op, value);
|
|
2157
2548
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
return LLRBNode.EMPTY;
|
|
2161
|
-
}
|
|
2162
|
-
else {
|
|
2163
|
-
smallest = n.right.min();
|
|
2164
|
-
n = n.copy(smallest.key, smallest.value, null, null, n.right.removeMin());
|
|
2165
|
-
}
|
|
2549
|
+
else {
|
|
2550
|
+
return new KeyFieldFilter(field, op, value);
|
|
2166
2551
|
}
|
|
2167
|
-
n = n.copy(null, null, null, null, n.right.remove(key, comparator));
|
|
2168
2552
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
isRed() {
|
|
2172
|
-
return this.color;
|
|
2173
|
-
}
|
|
2174
|
-
// Returns new tree after performing any needed rotations.
|
|
2175
|
-
fixUp() {
|
|
2176
|
-
let n = this;
|
|
2177
|
-
if (n.right.isRed() && !n.left.isRed()) {
|
|
2178
|
-
n = n.rotateLeft();
|
|
2553
|
+
else if (op === "array-contains" /* Operator.ARRAY_CONTAINS */) {
|
|
2554
|
+
return new ArrayContainsFilter(field, value);
|
|
2179
2555
|
}
|
|
2180
|
-
if (
|
|
2181
|
-
|
|
2556
|
+
else if (op === "in" /* Operator.IN */) {
|
|
2557
|
+
return new InFilter(field, value);
|
|
2182
2558
|
}
|
|
2183
|
-
if (
|
|
2184
|
-
|
|
2559
|
+
else if (op === "not-in" /* Operator.NOT_IN */) {
|
|
2560
|
+
return new NotInFilter(field, value);
|
|
2561
|
+
}
|
|
2562
|
+
else if (op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
2563
|
+
return new ArrayContainsAnyFilter(field, value);
|
|
2564
|
+
}
|
|
2565
|
+
else {
|
|
2566
|
+
return new FieldFilter(field, op, value);
|
|
2185
2567
|
}
|
|
2186
|
-
return n;
|
|
2187
2568
|
}
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2569
|
+
static createKeyFieldInFilter(field, op, value) {
|
|
2570
|
+
return op === "in" /* Operator.IN */
|
|
2571
|
+
? new KeyFieldInFilter(field, value)
|
|
2572
|
+
: new KeyFieldNotInFilter(field, value);
|
|
2573
|
+
}
|
|
2574
|
+
matches(doc) {
|
|
2575
|
+
const other = doc.data.field(this.field);
|
|
2576
|
+
// Types do not have to match in NOT_EQUAL filters.
|
|
2577
|
+
if (this.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
2578
|
+
return (other !== null &&
|
|
2579
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
2194
2580
|
}
|
|
2195
|
-
|
|
2581
|
+
// Only compare types with matching backend order (such as double and int).
|
|
2582
|
+
return (other !== null &&
|
|
2583
|
+
typeOrder(this.value) === typeOrder(other) &&
|
|
2584
|
+
this.matchesComparison(valueCompare(other, this.value)));
|
|
2196
2585
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2586
|
+
matchesComparison(comparison) {
|
|
2587
|
+
switch (this.op) {
|
|
2588
|
+
case "<" /* Operator.LESS_THAN */:
|
|
2589
|
+
return comparison < 0;
|
|
2590
|
+
case "<=" /* Operator.LESS_THAN_OR_EQUAL */:
|
|
2591
|
+
return comparison <= 0;
|
|
2592
|
+
case "==" /* Operator.EQUAL */:
|
|
2593
|
+
return comparison === 0;
|
|
2594
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
2595
|
+
return comparison !== 0;
|
|
2596
|
+
case ">" /* Operator.GREATER_THAN */:
|
|
2597
|
+
return comparison > 0;
|
|
2598
|
+
case ">=" /* Operator.GREATER_THAN_OR_EQUAL */:
|
|
2599
|
+
return comparison >= 0;
|
|
2600
|
+
default:
|
|
2601
|
+
return fail();
|
|
2202
2602
|
}
|
|
2203
|
-
return n;
|
|
2204
2603
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2604
|
+
isInequality() {
|
|
2605
|
+
return ([
|
|
2606
|
+
"<" /* Operator.LESS_THAN */,
|
|
2607
|
+
"<=" /* Operator.LESS_THAN_OR_EQUAL */,
|
|
2608
|
+
">" /* Operator.GREATER_THAN */,
|
|
2609
|
+
">=" /* Operator.GREATER_THAN_OR_EQUAL */,
|
|
2610
|
+
"!=" /* Operator.NOT_EQUAL */,
|
|
2611
|
+
"not-in" /* Operator.NOT_IN */
|
|
2612
|
+
].indexOf(this.op) >= 0);
|
|
2208
2613
|
}
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
return this.left.copy(null, null, this.color, null, nr);
|
|
2614
|
+
getFlattenedFilters() {
|
|
2615
|
+
return [this];
|
|
2212
2616
|
}
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
const right = this.right.copy(null, null, !this.right.color, null, null);
|
|
2216
|
-
return this.copy(null, null, !this.color, left, right);
|
|
2617
|
+
getFilters() {
|
|
2618
|
+
return [this];
|
|
2217
2619
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2620
|
+
getFirstInequalityField() {
|
|
2621
|
+
if (this.isInequality()) {
|
|
2622
|
+
return this.field;
|
|
2623
|
+
}
|
|
2624
|
+
return null;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
class CompositeFilter extends Filter {
|
|
2628
|
+
constructor(filters, op) {
|
|
2629
|
+
super();
|
|
2630
|
+
this.filters = filters;
|
|
2631
|
+
this.op = op;
|
|
2632
|
+
this.memoizedFlattenedFilters = null;
|
|
2633
|
+
}
|
|
2634
|
+
/**
|
|
2635
|
+
* Creates a filter based on the provided arguments.
|
|
2636
|
+
*/
|
|
2637
|
+
static create(filters, op) {
|
|
2638
|
+
return new CompositeFilter(filters, op);
|
|
2639
|
+
}
|
|
2640
|
+
matches(doc) {
|
|
2641
|
+
if (compositeFilterIsConjunction(this)) {
|
|
2642
|
+
// For conjunctions, all filters must match, so return false if any filter doesn't match.
|
|
2643
|
+
return this.filters.find(filter => !filter.matches(doc)) === undefined;
|
|
2223
2644
|
}
|
|
2224
2645
|
else {
|
|
2225
|
-
|
|
2646
|
+
// For disjunctions, at least one filter should match.
|
|
2647
|
+
return this.filters.find(filter => filter.matches(doc)) !== undefined;
|
|
2226
2648
|
}
|
|
2227
2649
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
if (this.isRed() && this.left.isRed()) {
|
|
2232
|
-
throw fail();
|
|
2233
|
-
}
|
|
2234
|
-
if (this.right.isRed()) {
|
|
2235
|
-
throw fail();
|
|
2650
|
+
getFlattenedFilters() {
|
|
2651
|
+
if (this.memoizedFlattenedFilters !== null) {
|
|
2652
|
+
return this.memoizedFlattenedFilters;
|
|
2236
2653
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2654
|
+
this.memoizedFlattenedFilters = this.filters.reduce((result, subfilter) => {
|
|
2655
|
+
return result.concat(subfilter.getFlattenedFilters());
|
|
2656
|
+
}, []);
|
|
2657
|
+
return this.memoizedFlattenedFilters;
|
|
2658
|
+
}
|
|
2659
|
+
// Returns a mutable copy of `this.filters`
|
|
2660
|
+
getFilters() {
|
|
2661
|
+
return Object.assign([], this.filters);
|
|
2662
|
+
}
|
|
2663
|
+
getFirstInequalityField() {
|
|
2664
|
+
const found = this.findFirstMatchingFilter(filter => filter.isInequality());
|
|
2665
|
+
if (found !== null) {
|
|
2666
|
+
return found.field;
|
|
2240
2667
|
}
|
|
2241
|
-
|
|
2242
|
-
|
|
2668
|
+
return null;
|
|
2669
|
+
}
|
|
2670
|
+
// Performs a depth-first search to find and return the first FieldFilter in the composite filter
|
|
2671
|
+
// that satisfies the predicate. Returns `null` if none of the FieldFilters satisfy the
|
|
2672
|
+
// predicate.
|
|
2673
|
+
findFirstMatchingFilter(predicate) {
|
|
2674
|
+
for (const fieldFilter of this.getFlattenedFilters()) {
|
|
2675
|
+
if (predicate(fieldFilter)) {
|
|
2676
|
+
return fieldFilter;
|
|
2677
|
+
}
|
|
2243
2678
|
}
|
|
2679
|
+
return null;
|
|
2244
2680
|
}
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
class LLRBEmptyNode {
|
|
2253
|
-
constructor() {
|
|
2254
|
-
this.size = 0;
|
|
2681
|
+
}
|
|
2682
|
+
function compositeFilterIsConjunction(compositeFilter) {
|
|
2683
|
+
return compositeFilter.op === "and" /* CompositeOperator.AND */;
|
|
2684
|
+
}
|
|
2685
|
+
function filterEquals(f1, f2) {
|
|
2686
|
+
if (f1 instanceof FieldFilter) {
|
|
2687
|
+
return fieldFilterEquals(f1, f2);
|
|
2255
2688
|
}
|
|
2256
|
-
|
|
2257
|
-
|
|
2689
|
+
else if (f1 instanceof CompositeFilter) {
|
|
2690
|
+
return compositeFilterEquals(f1, f2);
|
|
2258
2691
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2692
|
+
else {
|
|
2693
|
+
fail();
|
|
2261
2694
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2695
|
+
}
|
|
2696
|
+
function fieldFilterEquals(f1, f2) {
|
|
2697
|
+
return (f2 instanceof FieldFilter &&
|
|
2698
|
+
f1.op === f2.op &&
|
|
2699
|
+
f1.field.isEqual(f2.field) &&
|
|
2700
|
+
valueEquals(f1.value, f2.value));
|
|
2701
|
+
}
|
|
2702
|
+
function compositeFilterEquals(f1, f2) {
|
|
2703
|
+
if (f2 instanceof CompositeFilter &&
|
|
2704
|
+
f1.op === f2.op &&
|
|
2705
|
+
f1.filters.length === f2.filters.length) {
|
|
2706
|
+
const subFiltersMatch = f1.filters.reduce((result, f1Filter, index) => result && filterEquals(f1Filter, f2.filters[index]), true);
|
|
2707
|
+
return subFiltersMatch;
|
|
2264
2708
|
}
|
|
2265
|
-
|
|
2266
|
-
|
|
2709
|
+
return false;
|
|
2710
|
+
}
|
|
2711
|
+
/** Filter that matches on key fields (i.e. '__name__'). */
|
|
2712
|
+
class KeyFieldFilter extends FieldFilter {
|
|
2713
|
+
constructor(field, op, value) {
|
|
2714
|
+
super(field, op, value);
|
|
2715
|
+
this.key = DocumentKey.fromName(value.referenceValue);
|
|
2267
2716
|
}
|
|
2268
|
-
|
|
2269
|
-
|
|
2717
|
+
matches(doc) {
|
|
2718
|
+
const comparison = DocumentKey.comparator(doc.key, this.key);
|
|
2719
|
+
return this.matchesComparison(comparison);
|
|
2270
2720
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2721
|
+
}
|
|
2722
|
+
/** Filter that matches on key fields within an array. */
|
|
2723
|
+
class KeyFieldInFilter extends FieldFilter {
|
|
2724
|
+
constructor(field, value) {
|
|
2725
|
+
super(field, "in" /* Operator.IN */, value);
|
|
2726
|
+
this.keys = extractDocumentKeysFromArrayValue("in" /* Operator.IN */, value);
|
|
2274
2727
|
}
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
return new LLRBNode(key, value);
|
|
2728
|
+
matches(doc) {
|
|
2729
|
+
return this.keys.some(key => key.isEqual(doc.key));
|
|
2278
2730
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2731
|
+
}
|
|
2732
|
+
/** Filter that matches on key fields not present within an array. */
|
|
2733
|
+
class KeyFieldNotInFilter extends FieldFilter {
|
|
2734
|
+
constructor(field, value) {
|
|
2735
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
2736
|
+
this.keys = extractDocumentKeysFromArrayValue("not-in" /* Operator.NOT_IN */, value);
|
|
2737
|
+
}
|
|
2738
|
+
matches(doc) {
|
|
2739
|
+
return !this.keys.some(key => key.isEqual(doc.key));
|
|
2740
|
+
}
|
|
2741
|
+
}
|
|
2742
|
+
function extractDocumentKeysFromArrayValue(op, value) {
|
|
2743
|
+
var _a;
|
|
2744
|
+
return (((_a = value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) || []).map(v => {
|
|
2745
|
+
return DocumentKey.fromName(v.referenceValue);
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
/** A Filter that implements the array-contains operator. */
|
|
2749
|
+
class ArrayContainsFilter extends FieldFilter {
|
|
2750
|
+
constructor(field, value) {
|
|
2751
|
+
super(field, "array-contains" /* Operator.ARRAY_CONTAINS */, value);
|
|
2752
|
+
}
|
|
2753
|
+
matches(doc) {
|
|
2754
|
+
const other = doc.data.field(this.field);
|
|
2755
|
+
return isArray(other) && arrayValueContains(other.arrayValue, this.value);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
/** A Filter that implements the IN operator. */
|
|
2759
|
+
class InFilter extends FieldFilter {
|
|
2760
|
+
constructor(field, value) {
|
|
2761
|
+
super(field, "in" /* Operator.IN */, value);
|
|
2762
|
+
}
|
|
2763
|
+
matches(doc) {
|
|
2764
|
+
const other = doc.data.field(this.field);
|
|
2765
|
+
return other !== null && arrayValueContains(this.value.arrayValue, other);
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
/** A Filter that implements the not-in operator. */
|
|
2769
|
+
class NotInFilter extends FieldFilter {
|
|
2770
|
+
constructor(field, value) {
|
|
2771
|
+
super(field, "not-in" /* Operator.NOT_IN */, value);
|
|
2772
|
+
}
|
|
2773
|
+
matches(doc) {
|
|
2774
|
+
if (arrayValueContains(this.value.arrayValue, { nullValue: 'NULL_VALUE' })) {
|
|
2775
|
+
return false;
|
|
2776
|
+
}
|
|
2777
|
+
const other = doc.data.field(this.field);
|
|
2778
|
+
return other !== null && !arrayValueContains(this.value.arrayValue, other);
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
/** A Filter that implements the array-contains-any operator. */
|
|
2782
|
+
class ArrayContainsAnyFilter extends FieldFilter {
|
|
2783
|
+
constructor(field, value) {
|
|
2784
|
+
super(field, "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, value);
|
|
2785
|
+
}
|
|
2786
|
+
matches(doc) {
|
|
2787
|
+
const other = doc.data.field(this.field);
|
|
2788
|
+
if (!isArray(other) || !other.arrayValue.values) {
|
|
2789
|
+
return false;
|
|
2790
|
+
}
|
|
2791
|
+
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
/**
|
|
2796
|
+
* @license
|
|
2797
|
+
* Copyright 2022 Google LLC
|
|
2798
|
+
*
|
|
2799
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2800
|
+
* you may not use this file except in compliance with the License.
|
|
2801
|
+
* You may obtain a copy of the License at
|
|
2802
|
+
*
|
|
2803
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2804
|
+
*
|
|
2805
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2806
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2807
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2808
|
+
* See the License for the specific language governing permissions and
|
|
2809
|
+
* limitations under the License.
|
|
2810
|
+
*/
|
|
2811
|
+
/**
|
|
2812
|
+
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
2813
|
+
*/
|
|
2814
|
+
class OrderBy {
|
|
2815
|
+
constructor(field, dir = "asc" /* Direction.ASCENDING */) {
|
|
2816
|
+
this.field = field;
|
|
2817
|
+
this.dir = dir;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
function orderByEquals(left, right) {
|
|
2821
|
+
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
/**
|
|
2825
|
+
* @license
|
|
2826
|
+
* Copyright 2017 Google LLC
|
|
2827
|
+
*
|
|
2828
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2829
|
+
* you may not use this file except in compliance with the License.
|
|
2830
|
+
* You may obtain a copy of the License at
|
|
2831
|
+
*
|
|
2832
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2833
|
+
*
|
|
2834
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
2835
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2836
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2837
|
+
* See the License for the specific language governing permissions and
|
|
2838
|
+
* limitations under the License.
|
|
2839
|
+
*/
|
|
2840
|
+
/**
|
|
2841
|
+
* A version of a document in Firestore. This corresponds to the version
|
|
2842
|
+
* timestamp, such as update_time or read_time.
|
|
2843
|
+
*/
|
|
2844
|
+
class SnapshotVersion {
|
|
2845
|
+
constructor(timestamp) {
|
|
2846
|
+
this.timestamp = timestamp;
|
|
2282
2847
|
}
|
|
2283
|
-
|
|
2284
|
-
return
|
|
2848
|
+
static fromTimestamp(value) {
|
|
2849
|
+
return new SnapshotVersion(value);
|
|
2285
2850
|
}
|
|
2286
|
-
|
|
2287
|
-
return
|
|
2851
|
+
static min() {
|
|
2852
|
+
return new SnapshotVersion(new Timestamp(0, 0));
|
|
2288
2853
|
}
|
|
2289
|
-
|
|
2290
|
-
return
|
|
2854
|
+
static max() {
|
|
2855
|
+
return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
|
|
2291
2856
|
}
|
|
2292
|
-
|
|
2293
|
-
return
|
|
2857
|
+
compareTo(other) {
|
|
2858
|
+
return this.timestamp._compareTo(other.timestamp);
|
|
2294
2859
|
}
|
|
2295
|
-
|
|
2296
|
-
return
|
|
2860
|
+
isEqual(other) {
|
|
2861
|
+
return this.timestamp.isEqual(other.timestamp);
|
|
2297
2862
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2863
|
+
/** Returns a number representation of the version for use in spec tests. */
|
|
2864
|
+
toMicroseconds() {
|
|
2865
|
+
// Convert to microseconds.
|
|
2866
|
+
return this.timestamp.seconds * 1e6 + this.timestamp.nanoseconds / 1000;
|
|
2300
2867
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
return true;
|
|
2868
|
+
toString() {
|
|
2869
|
+
return 'SnapshotVersion(' + this.timestamp.toString() + ')';
|
|
2304
2870
|
}
|
|
2305
|
-
|
|
2306
|
-
return
|
|
2871
|
+
toTimestamp() {
|
|
2872
|
+
return this.timestamp;
|
|
2307
2873
|
}
|
|
2308
|
-
}
|
|
2309
|
-
LLRBNode.EMPTY = new LLRBEmptyNode();
|
|
2874
|
+
}
|
|
2310
2875
|
|
|
2311
2876
|
/**
|
|
2312
2877
|
* @license
|
|
@@ -2324,484 +2889,445 @@ LLRBNode.EMPTY = new LLRBEmptyNode();
|
|
|
2324
2889
|
* See the License for the specific language governing permissions and
|
|
2325
2890
|
* limitations under the License.
|
|
2326
2891
|
*/
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
* NOTE: if provided comparator returns 0 for two elements, we consider them to
|
|
2332
|
-
* be equal!
|
|
2333
|
-
*/
|
|
2334
|
-
class SortedSet {
|
|
2335
|
-
constructor(comparator) {
|
|
2892
|
+
// An immutable sorted map implementation, based on a Left-leaning Red-Black
|
|
2893
|
+
// tree.
|
|
2894
|
+
class SortedMap {
|
|
2895
|
+
constructor(comparator, root) {
|
|
2336
2896
|
this.comparator = comparator;
|
|
2337
|
-
this.
|
|
2897
|
+
this.root = root ? root : LLRBNode.EMPTY;
|
|
2338
2898
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2899
|
+
// Returns a copy of the map, with the specified key/value added or replaced.
|
|
2900
|
+
insert(key, value) {
|
|
2901
|
+
return new SortedMap(this.comparator, this.root
|
|
2902
|
+
.insert(key, value, this.comparator)
|
|
2903
|
+
.copy(null, null, LLRBNode.BLACK, null, null));
|
|
2341
2904
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2905
|
+
// Returns a copy of the map, with the specified key removed.
|
|
2906
|
+
remove(key) {
|
|
2907
|
+
return new SortedMap(this.comparator, this.root
|
|
2908
|
+
.remove(key, this.comparator)
|
|
2909
|
+
.copy(null, null, LLRBNode.BLACK, null, null));
|
|
2344
2910
|
}
|
|
2345
|
-
|
|
2346
|
-
|
|
2911
|
+
// Returns the value of the node with the given key, or null.
|
|
2912
|
+
get(key) {
|
|
2913
|
+
let node = this.root;
|
|
2914
|
+
while (!node.isEmpty()) {
|
|
2915
|
+
const cmp = this.comparator(key, node.key);
|
|
2916
|
+
if (cmp === 0) {
|
|
2917
|
+
return node.value;
|
|
2918
|
+
}
|
|
2919
|
+
else if (cmp < 0) {
|
|
2920
|
+
node = node.left;
|
|
2921
|
+
}
|
|
2922
|
+
else if (cmp > 0) {
|
|
2923
|
+
node = node.right;
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
return null;
|
|
2927
|
+
}
|
|
2928
|
+
// Returns the index of the element in this sorted map, or -1 if it doesn't
|
|
2929
|
+
// exist.
|
|
2930
|
+
indexOf(key) {
|
|
2931
|
+
// Number of nodes that were pruned when descending right
|
|
2932
|
+
let prunedNodes = 0;
|
|
2933
|
+
let node = this.root;
|
|
2934
|
+
while (!node.isEmpty()) {
|
|
2935
|
+
const cmp = this.comparator(key, node.key);
|
|
2936
|
+
if (cmp === 0) {
|
|
2937
|
+
return prunedNodes + node.left.size;
|
|
2938
|
+
}
|
|
2939
|
+
else if (cmp < 0) {
|
|
2940
|
+
node = node.left;
|
|
2941
|
+
}
|
|
2942
|
+
else {
|
|
2943
|
+
// Count all nodes left of the node plus the node itself
|
|
2944
|
+
prunedNodes += node.left.size + 1;
|
|
2945
|
+
node = node.right;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
// Node not found
|
|
2949
|
+
return -1;
|
|
2950
|
+
}
|
|
2951
|
+
isEmpty() {
|
|
2952
|
+
return this.root.isEmpty();
|
|
2347
2953
|
}
|
|
2954
|
+
// Returns the total number of nodes in the map.
|
|
2348
2955
|
get size() {
|
|
2349
|
-
return this.
|
|
2956
|
+
return this.root.size;
|
|
2350
2957
|
}
|
|
2351
|
-
|
|
2352
|
-
|
|
2958
|
+
// Returns the minimum key in the map.
|
|
2959
|
+
minKey() {
|
|
2960
|
+
return this.root.minKey();
|
|
2353
2961
|
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
this.
|
|
2357
|
-
|
|
2962
|
+
// Returns the maximum key in the map.
|
|
2963
|
+
maxKey() {
|
|
2964
|
+
return this.root.maxKey();
|
|
2965
|
+
}
|
|
2966
|
+
// Traverses the map in key order and calls the specified action function
|
|
2967
|
+
// for each key/value pair. If action returns true, traversal is aborted.
|
|
2968
|
+
// Returns the first truthy value returned by action, or the last falsey
|
|
2969
|
+
// value returned by action.
|
|
2970
|
+
inorderTraversal(action) {
|
|
2971
|
+
return this.root.inorderTraversal(action);
|
|
2972
|
+
}
|
|
2973
|
+
forEach(fn) {
|
|
2974
|
+
this.inorderTraversal((k, v) => {
|
|
2975
|
+
fn(k, v);
|
|
2358
2976
|
return false;
|
|
2359
2977
|
});
|
|
2360
2978
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
}
|
|
2369
|
-
cb(elem.key);
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
/**
|
|
2373
|
-
* Iterates over `elem`s such that: start <= elem until false is returned.
|
|
2374
|
-
*/
|
|
2375
|
-
forEachWhile(cb, start) {
|
|
2376
|
-
let iter;
|
|
2377
|
-
if (start !== undefined) {
|
|
2378
|
-
iter = this.data.getIteratorFrom(start);
|
|
2379
|
-
}
|
|
2380
|
-
else {
|
|
2381
|
-
iter = this.data.getIterator();
|
|
2382
|
-
}
|
|
2383
|
-
while (iter.hasNext()) {
|
|
2384
|
-
const elem = iter.getNext();
|
|
2385
|
-
const result = cb(elem.key);
|
|
2386
|
-
if (!result) {
|
|
2387
|
-
return;
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2979
|
+
toString() {
|
|
2980
|
+
const descriptions = [];
|
|
2981
|
+
this.inorderTraversal((k, v) => {
|
|
2982
|
+
descriptions.push(`${k}:${v}`);
|
|
2983
|
+
return false;
|
|
2984
|
+
});
|
|
2985
|
+
return `{${descriptions.join(', ')}}`;
|
|
2390
2986
|
}
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2987
|
+
// Traverses the map in reverse key order and calls the specified action
|
|
2988
|
+
// function for each key/value pair. If action returns true, traversal is
|
|
2989
|
+
// aborted.
|
|
2990
|
+
// Returns the first truthy value returned by action, or the last falsey
|
|
2991
|
+
// value returned by action.
|
|
2992
|
+
reverseTraversal(action) {
|
|
2993
|
+
return this.root.reverseTraversal(action);
|
|
2395
2994
|
}
|
|
2995
|
+
// Returns an iterator over the SortedMap.
|
|
2396
2996
|
getIterator() {
|
|
2397
|
-
return new
|
|
2997
|
+
return new SortedMapIterator(this.root, null, this.comparator, false);
|
|
2398
2998
|
}
|
|
2399
2999
|
getIteratorFrom(key) {
|
|
2400
|
-
return new
|
|
2401
|
-
}
|
|
2402
|
-
/** Inserts or updates an element */
|
|
2403
|
-
add(elem) {
|
|
2404
|
-
return this.copy(this.data.remove(elem).insert(elem, true));
|
|
3000
|
+
return new SortedMapIterator(this.root, key, this.comparator, false);
|
|
2405
3001
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
if (!this.has(elem)) {
|
|
2409
|
-
return this;
|
|
2410
|
-
}
|
|
2411
|
-
return this.copy(this.data.remove(elem));
|
|
3002
|
+
getReverseIterator() {
|
|
3003
|
+
return new SortedMapIterator(this.root, null, this.comparator, true);
|
|
2412
3004
|
}
|
|
2413
|
-
|
|
2414
|
-
return this.
|
|
3005
|
+
getReverseIteratorFrom(key) {
|
|
3006
|
+
return new SortedMapIterator(this.root, key, this.comparator, true);
|
|
2415
3007
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
3008
|
+
} // end SortedMap
|
|
3009
|
+
// An iterator over an LLRBNode.
|
|
3010
|
+
class SortedMapIterator {
|
|
3011
|
+
constructor(node, startKey, comparator, isReverse) {
|
|
3012
|
+
this.isReverse = isReverse;
|
|
3013
|
+
this.nodeStack = [];
|
|
3014
|
+
let cmp = 1;
|
|
3015
|
+
while (!node.isEmpty()) {
|
|
3016
|
+
cmp = startKey ? comparator(node.key, startKey) : 1;
|
|
3017
|
+
// flip the comparison if we're going in reverse
|
|
3018
|
+
if (startKey && isReverse) {
|
|
3019
|
+
cmp *= -1;
|
|
3020
|
+
}
|
|
3021
|
+
if (cmp < 0) {
|
|
3022
|
+
// This node is less than our start key. ignore it
|
|
3023
|
+
if (this.isReverse) {
|
|
3024
|
+
node = node.left;
|
|
3025
|
+
}
|
|
3026
|
+
else {
|
|
3027
|
+
node = node.right;
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
else if (cmp === 0) {
|
|
3031
|
+
// This node is exactly equal to our start key. Push it on the stack,
|
|
3032
|
+
// but stop iterating;
|
|
3033
|
+
this.nodeStack.push(node);
|
|
3034
|
+
break;
|
|
3035
|
+
}
|
|
3036
|
+
else {
|
|
3037
|
+
// This node is greater than our start key, add it to the stack and move
|
|
3038
|
+
// to the next one
|
|
3039
|
+
this.nodeStack.push(node);
|
|
3040
|
+
if (this.isReverse) {
|
|
3041
|
+
node = node.right;
|
|
3042
|
+
}
|
|
3043
|
+
else {
|
|
3044
|
+
node = node.left;
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
2422
3047
|
}
|
|
2423
|
-
other.forEach(elem => {
|
|
2424
|
-
result = result.add(elem);
|
|
2425
|
-
});
|
|
2426
|
-
return result;
|
|
2427
3048
|
}
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
3049
|
+
getNext() {
|
|
3050
|
+
let node = this.nodeStack.pop();
|
|
3051
|
+
const result = { key: node.key, value: node.value };
|
|
3052
|
+
if (this.isReverse) {
|
|
3053
|
+
node = node.left;
|
|
3054
|
+
while (!node.isEmpty()) {
|
|
3055
|
+
this.nodeStack.push(node);
|
|
3056
|
+
node = node.right;
|
|
3057
|
+
}
|
|
2434
3058
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
if (this.comparator(thisElem, otherElem) !== 0) {
|
|
2441
|
-
return false;
|
|
3059
|
+
else {
|
|
3060
|
+
node = node.right;
|
|
3061
|
+
while (!node.isEmpty()) {
|
|
3062
|
+
this.nodeStack.push(node);
|
|
3063
|
+
node = node.left;
|
|
2442
3064
|
}
|
|
2443
3065
|
}
|
|
2444
|
-
return
|
|
2445
|
-
}
|
|
2446
|
-
toArray() {
|
|
2447
|
-
const res = [];
|
|
2448
|
-
this.forEach(targetId => {
|
|
2449
|
-
res.push(targetId);
|
|
2450
|
-
});
|
|
2451
|
-
return res;
|
|
3066
|
+
return result;
|
|
2452
3067
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
this.forEach(elem => result.push(elem));
|
|
2456
|
-
return 'SortedSet(' + result.toString() + ')';
|
|
3068
|
+
hasNext() {
|
|
3069
|
+
return this.nodeStack.length > 0;
|
|
2457
3070
|
}
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
3071
|
+
peek() {
|
|
3072
|
+
if (this.nodeStack.length === 0) {
|
|
3073
|
+
return null;
|
|
3074
|
+
}
|
|
3075
|
+
const node = this.nodeStack[this.nodeStack.length - 1];
|
|
3076
|
+
return { key: node.key, value: node.value };
|
|
2462
3077
|
}
|
|
2463
|
-
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
3078
|
+
} // end SortedMapIterator
|
|
3079
|
+
// Represents a node in a Left-leaning Red-Black tree.
|
|
3080
|
+
class LLRBNode {
|
|
3081
|
+
constructor(key, value, color, left, right) {
|
|
3082
|
+
this.key = key;
|
|
3083
|
+
this.value = value;
|
|
3084
|
+
this.color = color != null ? color : LLRBNode.RED;
|
|
3085
|
+
this.left = left != null ? left : LLRBNode.EMPTY;
|
|
3086
|
+
this.right = right != null ? right : LLRBNode.EMPTY;
|
|
3087
|
+
this.size = this.left.size + 1 + this.right.size;
|
|
2467
3088
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
3089
|
+
// Returns a copy of the current node, optionally replacing pieces of it.
|
|
3090
|
+
copy(key, value, color, left, right) {
|
|
3091
|
+
return new LLRBNode(key != null ? key : this.key, value != null ? value : this.value, color != null ? color : this.color, left != null ? left : this.left, right != null ? right : this.right);
|
|
2470
3092
|
}
|
|
2471
|
-
|
|
2472
|
-
return
|
|
3093
|
+
isEmpty() {
|
|
3094
|
+
return false;
|
|
2473
3095
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
* You may obtain a copy of the License at
|
|
2483
|
-
*
|
|
2484
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2485
|
-
*
|
|
2486
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2487
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2488
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2489
|
-
* See the License for the specific language governing permissions and
|
|
2490
|
-
* limitations under the License.
|
|
2491
|
-
*/
|
|
2492
|
-
/**
|
|
2493
|
-
* Provides a set of fields that can be used to partially patch a document.
|
|
2494
|
-
* FieldMask is used in conjunction with ObjectValue.
|
|
2495
|
-
* Examples:
|
|
2496
|
-
* foo - Overwrites foo entirely with the provided value. If foo is not
|
|
2497
|
-
* present in the companion ObjectValue, the field is deleted.
|
|
2498
|
-
* foo.bar - Overwrites only the field bar of the object foo.
|
|
2499
|
-
* If foo is not an object, foo is replaced with an object
|
|
2500
|
-
* containing foo
|
|
2501
|
-
*/
|
|
2502
|
-
class FieldMask {
|
|
2503
|
-
constructor(fields) {
|
|
2504
|
-
this.fields = fields;
|
|
2505
|
-
// TODO(dimond): validation of FieldMask
|
|
2506
|
-
// Sort the field mask to support `FieldMask.isEqual()` and assert below.
|
|
2507
|
-
fields.sort(FieldPath$1.comparator);
|
|
3096
|
+
// Traverses the tree in key order and calls the specified action function
|
|
3097
|
+
// for each node. If action returns true, traversal is aborted.
|
|
3098
|
+
// Returns the first truthy value returned by action, or the last falsey
|
|
3099
|
+
// value returned by action.
|
|
3100
|
+
inorderTraversal(action) {
|
|
3101
|
+
return (this.left.inorderTraversal(action) ||
|
|
3102
|
+
action(this.key, this.value) ||
|
|
3103
|
+
this.right.inorderTraversal(action));
|
|
2508
3104
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
3105
|
+
// Traverses the tree in reverse key order and calls the specified action
|
|
3106
|
+
// function for each node. If action returns true, traversal is aborted.
|
|
3107
|
+
// Returns the first truthy value returned by action, or the last falsey
|
|
3108
|
+
// value returned by action.
|
|
3109
|
+
reverseTraversal(action) {
|
|
3110
|
+
return (this.right.reverseTraversal(action) ||
|
|
3111
|
+
action(this.key, this.value) ||
|
|
3112
|
+
this.left.reverseTraversal(action));
|
|
2511
3113
|
}
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
unionWith(extraFields) {
|
|
2517
|
-
let mergedMaskSet = new SortedSet(FieldPath$1.comparator);
|
|
2518
|
-
for (const fieldPath of this.fields) {
|
|
2519
|
-
mergedMaskSet = mergedMaskSet.add(fieldPath);
|
|
2520
|
-
}
|
|
2521
|
-
for (const fieldPath of extraFields) {
|
|
2522
|
-
mergedMaskSet = mergedMaskSet.add(fieldPath);
|
|
3114
|
+
// Returns the minimum node in the tree.
|
|
3115
|
+
min() {
|
|
3116
|
+
if (this.left.isEmpty()) {
|
|
3117
|
+
return this;
|
|
2523
3118
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
/**
|
|
2527
|
-
* Verifies that `fieldPath` is included by at least one field in this field
|
|
2528
|
-
* mask.
|
|
2529
|
-
*
|
|
2530
|
-
* This is an O(n) operation, where `n` is the size of the field mask.
|
|
2531
|
-
*/
|
|
2532
|
-
covers(fieldPath) {
|
|
2533
|
-
for (const fieldMaskPath of this.fields) {
|
|
2534
|
-
if (fieldMaskPath.isPrefixOf(fieldPath)) {
|
|
2535
|
-
return true;
|
|
2536
|
-
}
|
|
3119
|
+
else {
|
|
3120
|
+
return this.left.min();
|
|
2537
3121
|
}
|
|
2538
|
-
return false;
|
|
2539
3122
|
}
|
|
2540
|
-
|
|
2541
|
-
|
|
3123
|
+
// Returns the maximum key in the tree.
|
|
3124
|
+
minKey() {
|
|
3125
|
+
return this.min().key;
|
|
2542
3126
|
}
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
* You may obtain a copy of the License at
|
|
2552
|
-
*
|
|
2553
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2554
|
-
*
|
|
2555
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2556
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2557
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2558
|
-
* See the License for the specific language governing permissions and
|
|
2559
|
-
* limitations under the License.
|
|
2560
|
-
*/
|
|
2561
|
-
/** Converts a Base64 encoded string to a binary string. */
|
|
2562
|
-
function decodeBase64(encoded) {
|
|
2563
|
-
// Note: We used to validate the base64 string here via a regular expression.
|
|
2564
|
-
// This was removed to improve the performance of indexing.
|
|
2565
|
-
return Buffer.from(encoded, 'base64').toString('binary');
|
|
2566
|
-
}
|
|
2567
|
-
/** Converts a binary string to a Base64 encoded string. */
|
|
2568
|
-
function encodeBase64(raw) {
|
|
2569
|
-
return Buffer.from(raw, 'binary').toString('base64');
|
|
2570
|
-
}
|
|
2571
|
-
|
|
2572
|
-
/**
|
|
2573
|
-
* @license
|
|
2574
|
-
* Copyright 2020 Google LLC
|
|
2575
|
-
*
|
|
2576
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2577
|
-
* you may not use this file except in compliance with the License.
|
|
2578
|
-
* You may obtain a copy of the License at
|
|
2579
|
-
*
|
|
2580
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2581
|
-
*
|
|
2582
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2583
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2584
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2585
|
-
* See the License for the specific language governing permissions and
|
|
2586
|
-
* limitations under the License.
|
|
2587
|
-
*/
|
|
2588
|
-
/**
|
|
2589
|
-
* Immutable class that represents a "proto" byte string.
|
|
2590
|
-
*
|
|
2591
|
-
* Proto byte strings can either be Base64-encoded strings or Uint8Arrays when
|
|
2592
|
-
* sent on the wire. This class abstracts away this differentiation by holding
|
|
2593
|
-
* the proto byte string in a common class that must be converted into a string
|
|
2594
|
-
* before being sent as a proto.
|
|
2595
|
-
* @internal
|
|
2596
|
-
*/
|
|
2597
|
-
class ByteString {
|
|
2598
|
-
constructor(binaryString) {
|
|
2599
|
-
this.binaryString = binaryString;
|
|
3127
|
+
// Returns the maximum key in the tree.
|
|
3128
|
+
maxKey() {
|
|
3129
|
+
if (this.right.isEmpty()) {
|
|
3130
|
+
return this.key;
|
|
3131
|
+
}
|
|
3132
|
+
else {
|
|
3133
|
+
return this.right.maxKey();
|
|
3134
|
+
}
|
|
2600
3135
|
}
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
3136
|
+
// Returns new tree, with the key/value added.
|
|
3137
|
+
insert(key, value, comparator) {
|
|
3138
|
+
let n = this;
|
|
3139
|
+
const cmp = comparator(key, n.key);
|
|
3140
|
+
if (cmp < 0) {
|
|
3141
|
+
n = n.copy(null, null, null, n.left.insert(key, value, comparator), null);
|
|
3142
|
+
}
|
|
3143
|
+
else if (cmp === 0) {
|
|
3144
|
+
n = n.copy(null, value, null, null, null);
|
|
3145
|
+
}
|
|
3146
|
+
else {
|
|
3147
|
+
n = n.copy(null, null, null, null, n.right.insert(key, value, comparator));
|
|
3148
|
+
}
|
|
3149
|
+
return n.fixUp();
|
|
2604
3150
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
3151
|
+
removeMin() {
|
|
3152
|
+
if (this.left.isEmpty()) {
|
|
3153
|
+
return LLRBNode.EMPTY;
|
|
3154
|
+
}
|
|
3155
|
+
let n = this;
|
|
3156
|
+
if (!n.left.isRed() && !n.left.left.isRed()) {
|
|
3157
|
+
n = n.moveRedLeft();
|
|
3158
|
+
}
|
|
3159
|
+
n = n.copy(null, null, null, n.left.removeMin(), null);
|
|
3160
|
+
return n.fixUp();
|
|
2610
3161
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
3162
|
+
// Returns new tree, with the specified item removed.
|
|
3163
|
+
remove(key, comparator) {
|
|
3164
|
+
let smallest;
|
|
3165
|
+
let n = this;
|
|
3166
|
+
if (comparator(key, n.key) < 0) {
|
|
3167
|
+
if (!n.left.isEmpty() && !n.left.isRed() && !n.left.left.isRed()) {
|
|
3168
|
+
n = n.moveRedLeft();
|
|
3169
|
+
}
|
|
3170
|
+
n = n.copy(null, null, null, n.left.remove(key, comparator), null);
|
|
3171
|
+
}
|
|
3172
|
+
else {
|
|
3173
|
+
if (n.left.isRed()) {
|
|
3174
|
+
n = n.rotateRight();
|
|
3175
|
+
}
|
|
3176
|
+
if (!n.right.isEmpty() && !n.right.isRed() && !n.right.left.isRed()) {
|
|
3177
|
+
n = n.moveRedRight();
|
|
3178
|
+
}
|
|
3179
|
+
if (comparator(key, n.key) === 0) {
|
|
3180
|
+
if (n.right.isEmpty()) {
|
|
3181
|
+
return LLRBNode.EMPTY;
|
|
2617
3182
|
}
|
|
2618
3183
|
else {
|
|
2619
|
-
|
|
3184
|
+
smallest = n.right.min();
|
|
3185
|
+
n = n.copy(smallest.key, smallest.value, null, null, n.right.removeMin());
|
|
2620
3186
|
}
|
|
2621
3187
|
}
|
|
2622
|
-
|
|
3188
|
+
n = n.copy(null, null, null, null, n.right.remove(key, comparator));
|
|
3189
|
+
}
|
|
3190
|
+
return n.fixUp();
|
|
2623
3191
|
}
|
|
2624
|
-
|
|
2625
|
-
return
|
|
3192
|
+
isRed() {
|
|
3193
|
+
return this.color;
|
|
2626
3194
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
3195
|
+
// Returns new tree after performing any needed rotations.
|
|
3196
|
+
fixUp() {
|
|
3197
|
+
let n = this;
|
|
3198
|
+
if (n.right.isRed() && !n.left.isRed()) {
|
|
3199
|
+
n = n.rotateLeft();
|
|
3200
|
+
}
|
|
3201
|
+
if (n.left.isRed() && n.left.left.isRed()) {
|
|
3202
|
+
n = n.rotateRight();
|
|
3203
|
+
}
|
|
3204
|
+
if (n.left.isRed() && n.right.isRed()) {
|
|
3205
|
+
n = n.colorFlip();
|
|
3206
|
+
}
|
|
3207
|
+
return n;
|
|
2629
3208
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
3209
|
+
moveRedLeft() {
|
|
3210
|
+
let n = this.colorFlip();
|
|
3211
|
+
if (n.right.left.isRed()) {
|
|
3212
|
+
n = n.copy(null, null, null, null, n.right.rotateRight());
|
|
3213
|
+
n = n.rotateLeft();
|
|
3214
|
+
n = n.colorFlip();
|
|
3215
|
+
}
|
|
3216
|
+
return n;
|
|
2632
3217
|
}
|
|
2633
|
-
|
|
2634
|
-
|
|
3218
|
+
moveRedRight() {
|
|
3219
|
+
let n = this.colorFlip();
|
|
3220
|
+
if (n.left.left.isRed()) {
|
|
3221
|
+
n = n.rotateRight();
|
|
3222
|
+
n = n.colorFlip();
|
|
3223
|
+
}
|
|
3224
|
+
return n;
|
|
2635
3225
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
3226
|
+
rotateLeft() {
|
|
3227
|
+
const nl = this.copy(null, null, LLRBNode.RED, null, this.right.left);
|
|
3228
|
+
return this.right.copy(null, null, this.color, nl, null);
|
|
2638
3229
|
}
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
* Helper function to convert an Uint8array to a binary string.
|
|
2643
|
-
*/
|
|
2644
|
-
function binaryStringFromUint8Array(array) {
|
|
2645
|
-
let binaryString = '';
|
|
2646
|
-
for (let i = 0; i < array.length; ++i) {
|
|
2647
|
-
binaryString += String.fromCharCode(array[i]);
|
|
3230
|
+
rotateRight() {
|
|
3231
|
+
const nr = this.copy(null, null, LLRBNode.RED, this.left.right, null);
|
|
3232
|
+
return this.left.copy(null, null, this.color, null, nr);
|
|
2648
3233
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
*/
|
|
2654
|
-
function uint8ArrayFromBinaryString(binaryString) {
|
|
2655
|
-
const buffer = new Uint8Array(binaryString.length);
|
|
2656
|
-
for (let i = 0; i < binaryString.length; i++) {
|
|
2657
|
-
buffer[i] = binaryString.charCodeAt(i);
|
|
3234
|
+
colorFlip() {
|
|
3235
|
+
const left = this.left.copy(null, null, !this.left.color, null, null);
|
|
3236
|
+
const right = this.right.copy(null, null, !this.right.color, null, null);
|
|
3237
|
+
return this.copy(null, null, !this.color, left, right);
|
|
2658
3238
|
}
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
* you may not use this file except in compliance with the License.
|
|
2668
|
-
* You may obtain a copy of the License at
|
|
2669
|
-
*
|
|
2670
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2671
|
-
*
|
|
2672
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2673
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2674
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2675
|
-
* See the License for the specific language governing permissions and
|
|
2676
|
-
* limitations under the License.
|
|
2677
|
-
*/
|
|
2678
|
-
// A RegExp matching ISO 8601 UTC timestamps with optional fraction.
|
|
2679
|
-
const ISO_TIMESTAMP_REG_EXP = new RegExp(/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.(\d+))?Z$/);
|
|
2680
|
-
/**
|
|
2681
|
-
* Converts the possible Proto values for a timestamp value into a "seconds and
|
|
2682
|
-
* nanos" representation.
|
|
2683
|
-
*/
|
|
2684
|
-
function normalizeTimestamp(date) {
|
|
2685
|
-
hardAssert(!!date);
|
|
2686
|
-
// The json interface (for the browser) will return an iso timestamp string,
|
|
2687
|
-
// while the proto js library (for node) will return a
|
|
2688
|
-
// google.protobuf.Timestamp instance.
|
|
2689
|
-
if (typeof date === 'string') {
|
|
2690
|
-
// The date string can have higher precision (nanos) than the Date class
|
|
2691
|
-
// (millis), so we do some custom parsing here.
|
|
2692
|
-
// Parse the nanos right out of the string.
|
|
2693
|
-
let nanos = 0;
|
|
2694
|
-
const fraction = ISO_TIMESTAMP_REG_EXP.exec(date);
|
|
2695
|
-
hardAssert(!!fraction);
|
|
2696
|
-
if (fraction[1]) {
|
|
2697
|
-
// Pad the fraction out to 9 digits (nanos).
|
|
2698
|
-
let nanoStr = fraction[1];
|
|
2699
|
-
nanoStr = (nanoStr + '000000000').substr(0, 9);
|
|
2700
|
-
nanos = Number(nanoStr);
|
|
3239
|
+
// For testing.
|
|
3240
|
+
checkMaxDepth() {
|
|
3241
|
+
const blackDepth = this.check();
|
|
3242
|
+
if (Math.pow(2.0, blackDepth) <= this.size + 1) {
|
|
3243
|
+
return true;
|
|
3244
|
+
}
|
|
3245
|
+
else {
|
|
3246
|
+
return false;
|
|
2701
3247
|
}
|
|
2702
|
-
// Parse the date to get the seconds.
|
|
2703
|
-
const parsedDate = new Date(date);
|
|
2704
|
-
const seconds = Math.floor(parsedDate.getTime() / 1000);
|
|
2705
|
-
return { seconds, nanos };
|
|
2706
3248
|
}
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
3249
|
+
// In a balanced RB tree, the black-depth (number of black nodes) from root to
|
|
3250
|
+
// leaves is equal on both sides. This function verifies that or asserts.
|
|
3251
|
+
check() {
|
|
3252
|
+
if (this.isRed() && this.left.isRed()) {
|
|
3253
|
+
throw fail();
|
|
3254
|
+
}
|
|
3255
|
+
if (this.right.isRed()) {
|
|
3256
|
+
throw fail();
|
|
3257
|
+
}
|
|
3258
|
+
const blackDepth = this.left.check();
|
|
3259
|
+
if (blackDepth !== this.right.check()) {
|
|
3260
|
+
throw fail();
|
|
3261
|
+
}
|
|
3262
|
+
else {
|
|
3263
|
+
return blackDepth + (this.isRed() ? 0 : 1);
|
|
3264
|
+
}
|
|
2714
3265
|
}
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
3266
|
+
} // end LLRBNode
|
|
3267
|
+
// Empty node is shared between all LLRB trees.
|
|
3268
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3269
|
+
LLRBNode.EMPTY = null;
|
|
3270
|
+
LLRBNode.RED = true;
|
|
3271
|
+
LLRBNode.BLACK = false;
|
|
3272
|
+
// Represents an empty node (a leaf node in the Red-Black Tree).
|
|
3273
|
+
class LLRBEmptyNode {
|
|
3274
|
+
constructor() {
|
|
3275
|
+
this.size = 0;
|
|
2724
3276
|
}
|
|
2725
|
-
|
|
2726
|
-
|
|
3277
|
+
get key() {
|
|
3278
|
+
throw fail();
|
|
3279
|
+
}
|
|
3280
|
+
get value() {
|
|
3281
|
+
throw fail();
|
|
3282
|
+
}
|
|
3283
|
+
get color() {
|
|
3284
|
+
throw fail();
|
|
3285
|
+
}
|
|
3286
|
+
get left() {
|
|
3287
|
+
throw fail();
|
|
3288
|
+
}
|
|
3289
|
+
get right() {
|
|
3290
|
+
throw fail();
|
|
3291
|
+
}
|
|
3292
|
+
// Returns a copy of the current node.
|
|
3293
|
+
copy(key, value, color, left, right) {
|
|
3294
|
+
return this;
|
|
3295
|
+
}
|
|
3296
|
+
// Returns a copy of the tree, with the specified key/value added.
|
|
3297
|
+
insert(key, value, comparator) {
|
|
3298
|
+
return new LLRBNode(key, value);
|
|
3299
|
+
}
|
|
3300
|
+
// Returns a copy of the tree, with the specified key removed.
|
|
3301
|
+
remove(key, comparator) {
|
|
3302
|
+
return this;
|
|
3303
|
+
}
|
|
3304
|
+
isEmpty() {
|
|
3305
|
+
return true;
|
|
3306
|
+
}
|
|
3307
|
+
inorderTraversal(action) {
|
|
3308
|
+
return false;
|
|
3309
|
+
}
|
|
3310
|
+
reverseTraversal(action) {
|
|
3311
|
+
return false;
|
|
3312
|
+
}
|
|
3313
|
+
minKey() {
|
|
3314
|
+
return null;
|
|
2727
3315
|
}
|
|
2728
|
-
|
|
2729
|
-
return
|
|
3316
|
+
maxKey() {
|
|
3317
|
+
return null;
|
|
2730
3318
|
}
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
function normalizeByteString(blob) {
|
|
2734
|
-
if (typeof blob === 'string') {
|
|
2735
|
-
return ByteString.fromBase64String(blob);
|
|
3319
|
+
isRed() {
|
|
3320
|
+
return false;
|
|
2736
3321
|
}
|
|
2737
|
-
|
|
2738
|
-
|
|
3322
|
+
// For testing.
|
|
3323
|
+
checkMaxDepth() {
|
|
3324
|
+
return true;
|
|
2739
3325
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
/**
|
|
2743
|
-
* @license
|
|
2744
|
-
* Copyright 2020 Google LLC
|
|
2745
|
-
*
|
|
2746
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2747
|
-
* you may not use this file except in compliance with the License.
|
|
2748
|
-
* You may obtain a copy of the License at
|
|
2749
|
-
*
|
|
2750
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
2751
|
-
*
|
|
2752
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
2753
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
2754
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2755
|
-
* See the License for the specific language governing permissions and
|
|
2756
|
-
* limitations under the License.
|
|
2757
|
-
*/
|
|
2758
|
-
/**
|
|
2759
|
-
* Represents a locally-applied ServerTimestamp.
|
|
2760
|
-
*
|
|
2761
|
-
* Server Timestamps are backed by MapValues that contain an internal field
|
|
2762
|
-
* `__type__` with a value of `server_timestamp`. The previous value and local
|
|
2763
|
-
* write time are stored in its `__previous_value__` and `__local_write_time__`
|
|
2764
|
-
* fields respectively.
|
|
2765
|
-
*
|
|
2766
|
-
* Notes:
|
|
2767
|
-
* - ServerTimestampValue instances are created as the result of applying a
|
|
2768
|
-
* transform. They can only exist in the local view of a document. Therefore
|
|
2769
|
-
* they do not need to be parsed or serialized.
|
|
2770
|
-
* - When evaluated locally (e.g. for snapshot.data()), they by default
|
|
2771
|
-
* evaluate to `null`. This behavior can be configured by passing custom
|
|
2772
|
-
* FieldValueOptions to value().
|
|
2773
|
-
* - With respect to other ServerTimestampValues, they sort by their
|
|
2774
|
-
* localWriteTime.
|
|
2775
|
-
*/
|
|
2776
|
-
const SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';
|
|
2777
|
-
const TYPE_KEY = '__type__';
|
|
2778
|
-
const PREVIOUS_VALUE_KEY = '__previous_value__';
|
|
2779
|
-
const LOCAL_WRITE_TIME_KEY = '__local_write_time__';
|
|
2780
|
-
function isServerTimestamp(value) {
|
|
2781
|
-
var _a, _b;
|
|
2782
|
-
const type = (_b = (((_a = value === null || value === void 0 ? void 0 : value.mapValue) === null || _a === void 0 ? void 0 : _a.fields) || {})[TYPE_KEY]) === null || _b === void 0 ? void 0 : _b.stringValue;
|
|
2783
|
-
return type === SERVER_TIMESTAMP_SENTINEL;
|
|
2784
|
-
}
|
|
2785
|
-
/**
|
|
2786
|
-
* Returns the value of the field before this ServerTimestamp was set.
|
|
2787
|
-
*
|
|
2788
|
-
* Preserving the previous values allows the user to display the last resoled
|
|
2789
|
-
* value until the backend responds with the timestamp.
|
|
2790
|
-
*/
|
|
2791
|
-
function getPreviousValue(value) {
|
|
2792
|
-
const previousValue = value.mapValue.fields[PREVIOUS_VALUE_KEY];
|
|
2793
|
-
if (isServerTimestamp(previousValue)) {
|
|
2794
|
-
return getPreviousValue(previousValue);
|
|
3326
|
+
check() {
|
|
3327
|
+
return 0;
|
|
2795
3328
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
/**
|
|
2799
|
-
* Returns the local time at which this timestamp was first set.
|
|
2800
|
-
*/
|
|
2801
|
-
function getLocalWriteTime(value) {
|
|
2802
|
-
const localWriteTime = normalizeTimestamp(value.mapValue.fields[LOCAL_WRITE_TIME_KEY].timestampValue);
|
|
2803
|
-
return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
|
|
2804
|
-
}
|
|
3329
|
+
} // end LLRBEmptyNode
|
|
3330
|
+
LLRBNode.EMPTY = new LLRBEmptyNode();
|
|
2805
3331
|
|
|
2806
3332
|
/**
|
|
2807
3333
|
* @license
|
|
@@ -2820,381 +3346,221 @@ function getLocalWriteTime(value) {
|
|
|
2820
3346
|
* limitations under the License.
|
|
2821
3347
|
*/
|
|
2822
3348
|
/**
|
|
2823
|
-
*
|
|
2824
|
-
|
|
2825
|
-
function isNullOrUndefined(value) {
|
|
2826
|
-
return value === null || value === undefined;
|
|
2827
|
-
}
|
|
2828
|
-
/** Returns whether the value represents -0. */
|
|
2829
|
-
function isNegativeZero(value) {
|
|
2830
|
-
// Detect if the value is -0.0. Based on polyfill from
|
|
2831
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
|
2832
|
-
return value === 0 && 1 / value === 1 / -0;
|
|
2833
|
-
}
|
|
2834
|
-
/**
|
|
2835
|
-
* Returns whether a value is an integer and in the safe integer range
|
|
2836
|
-
* @param value - The value to test for being an integer and in the safe range
|
|
2837
|
-
*/
|
|
2838
|
-
function isSafeInteger(value) {
|
|
2839
|
-
return (typeof value === 'number' &&
|
|
2840
|
-
Number.isInteger(value) &&
|
|
2841
|
-
!isNegativeZero(value) &&
|
|
2842
|
-
value <= Number.MAX_SAFE_INTEGER &&
|
|
2843
|
-
value >= Number.MIN_SAFE_INTEGER);
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
/**
|
|
2847
|
-
* @license
|
|
2848
|
-
* Copyright 2020 Google LLC
|
|
2849
|
-
*
|
|
2850
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
2851
|
-
* you may not use this file except in compliance with the License.
|
|
2852
|
-
* You may obtain a copy of the License at
|
|
2853
|
-
*
|
|
2854
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3349
|
+
* SortedSet is an immutable (copy-on-write) collection that holds elements
|
|
3350
|
+
* in order specified by the provided comparator.
|
|
2855
3351
|
*
|
|
2856
|
-
*
|
|
2857
|
-
*
|
|
2858
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
2859
|
-
* See the License for the specific language governing permissions and
|
|
2860
|
-
* limitations under the License.
|
|
3352
|
+
* NOTE: if provided comparator returns 0 for two elements, we consider them to
|
|
3353
|
+
* be equal!
|
|
2861
3354
|
*/
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
'__type__': { stringValue: MAX_VALUE_TYPE }
|
|
2867
|
-
}
|
|
2868
|
-
}
|
|
2869
|
-
};
|
|
2870
|
-
/** Extracts the backend's type order for the provided value. */
|
|
2871
|
-
function typeOrder(value) {
|
|
2872
|
-
if ('nullValue' in value) {
|
|
2873
|
-
return 0 /* NullValue */;
|
|
2874
|
-
}
|
|
2875
|
-
else if ('booleanValue' in value) {
|
|
2876
|
-
return 1 /* BooleanValue */;
|
|
2877
|
-
}
|
|
2878
|
-
else if ('integerValue' in value || 'doubleValue' in value) {
|
|
2879
|
-
return 2 /* NumberValue */;
|
|
2880
|
-
}
|
|
2881
|
-
else if ('timestampValue' in value) {
|
|
2882
|
-
return 3 /* TimestampValue */;
|
|
2883
|
-
}
|
|
2884
|
-
else if ('stringValue' in value) {
|
|
2885
|
-
return 5 /* StringValue */;
|
|
2886
|
-
}
|
|
2887
|
-
else if ('bytesValue' in value) {
|
|
2888
|
-
return 6 /* BlobValue */;
|
|
2889
|
-
}
|
|
2890
|
-
else if ('referenceValue' in value) {
|
|
2891
|
-
return 7 /* RefValue */;
|
|
2892
|
-
}
|
|
2893
|
-
else if ('geoPointValue' in value) {
|
|
2894
|
-
return 8 /* GeoPointValue */;
|
|
2895
|
-
}
|
|
2896
|
-
else if ('arrayValue' in value) {
|
|
2897
|
-
return 9 /* ArrayValue */;
|
|
2898
|
-
}
|
|
2899
|
-
else if ('mapValue' in value) {
|
|
2900
|
-
if (isServerTimestamp(value)) {
|
|
2901
|
-
return 4 /* ServerTimestampValue */;
|
|
2902
|
-
}
|
|
2903
|
-
else if (isMaxValue(value)) {
|
|
2904
|
-
return 9007199254740991 /* MaxValue */;
|
|
2905
|
-
}
|
|
2906
|
-
return 10 /* ObjectValue */;
|
|
2907
|
-
}
|
|
2908
|
-
else {
|
|
2909
|
-
return fail();
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
/** Tests `left` and `right` for equality based on the backend semantics. */
|
|
2913
|
-
function valueEquals(left, right) {
|
|
2914
|
-
if (left === right) {
|
|
2915
|
-
return true;
|
|
2916
|
-
}
|
|
2917
|
-
const leftType = typeOrder(left);
|
|
2918
|
-
const rightType = typeOrder(right);
|
|
2919
|
-
if (leftType !== rightType) {
|
|
2920
|
-
return false;
|
|
2921
|
-
}
|
|
2922
|
-
switch (leftType) {
|
|
2923
|
-
case 0 /* NullValue */:
|
|
2924
|
-
return true;
|
|
2925
|
-
case 1 /* BooleanValue */:
|
|
2926
|
-
return left.booleanValue === right.booleanValue;
|
|
2927
|
-
case 4 /* ServerTimestampValue */:
|
|
2928
|
-
return getLocalWriteTime(left).isEqual(getLocalWriteTime(right));
|
|
2929
|
-
case 3 /* TimestampValue */:
|
|
2930
|
-
return timestampEquals(left, right);
|
|
2931
|
-
case 5 /* StringValue */:
|
|
2932
|
-
return left.stringValue === right.stringValue;
|
|
2933
|
-
case 6 /* BlobValue */:
|
|
2934
|
-
return blobEquals(left, right);
|
|
2935
|
-
case 7 /* RefValue */:
|
|
2936
|
-
return left.referenceValue === right.referenceValue;
|
|
2937
|
-
case 8 /* GeoPointValue */:
|
|
2938
|
-
return geoPointEquals(left, right);
|
|
2939
|
-
case 2 /* NumberValue */:
|
|
2940
|
-
return numberEquals(left, right);
|
|
2941
|
-
case 9 /* ArrayValue */:
|
|
2942
|
-
return arrayEquals(left.arrayValue.values || [], right.arrayValue.values || [], valueEquals);
|
|
2943
|
-
case 10 /* ObjectValue */:
|
|
2944
|
-
return objectEquals(left, right);
|
|
2945
|
-
case 9007199254740991 /* MaxValue */:
|
|
2946
|
-
return true;
|
|
2947
|
-
default:
|
|
2948
|
-
return fail();
|
|
2949
|
-
}
|
|
2950
|
-
}
|
|
2951
|
-
function timestampEquals(left, right) {
|
|
2952
|
-
if (typeof left.timestampValue === 'string' &&
|
|
2953
|
-
typeof right.timestampValue === 'string' &&
|
|
2954
|
-
left.timestampValue.length === right.timestampValue.length) {
|
|
2955
|
-
// Use string equality for ISO 8601 timestamps
|
|
2956
|
-
return left.timestampValue === right.timestampValue;
|
|
2957
|
-
}
|
|
2958
|
-
const leftTimestamp = normalizeTimestamp(left.timestampValue);
|
|
2959
|
-
const rightTimestamp = normalizeTimestamp(right.timestampValue);
|
|
2960
|
-
return (leftTimestamp.seconds === rightTimestamp.seconds &&
|
|
2961
|
-
leftTimestamp.nanos === rightTimestamp.nanos);
|
|
2962
|
-
}
|
|
2963
|
-
function geoPointEquals(left, right) {
|
|
2964
|
-
return (normalizeNumber(left.geoPointValue.latitude) ===
|
|
2965
|
-
normalizeNumber(right.geoPointValue.latitude) &&
|
|
2966
|
-
normalizeNumber(left.geoPointValue.longitude) ===
|
|
2967
|
-
normalizeNumber(right.geoPointValue.longitude));
|
|
2968
|
-
}
|
|
2969
|
-
function blobEquals(left, right) {
|
|
2970
|
-
return normalizeByteString(left.bytesValue).isEqual(normalizeByteString(right.bytesValue));
|
|
2971
|
-
}
|
|
2972
|
-
function numberEquals(left, right) {
|
|
2973
|
-
if ('integerValue' in left && 'integerValue' in right) {
|
|
2974
|
-
return (normalizeNumber(left.integerValue) === normalizeNumber(right.integerValue));
|
|
2975
|
-
}
|
|
2976
|
-
else if ('doubleValue' in left && 'doubleValue' in right) {
|
|
2977
|
-
const n1 = normalizeNumber(left.doubleValue);
|
|
2978
|
-
const n2 = normalizeNumber(right.doubleValue);
|
|
2979
|
-
if (n1 === n2) {
|
|
2980
|
-
return isNegativeZero(n1) === isNegativeZero(n2);
|
|
2981
|
-
}
|
|
2982
|
-
else {
|
|
2983
|
-
return isNaN(n1) && isNaN(n2);
|
|
2984
|
-
}
|
|
2985
|
-
}
|
|
2986
|
-
return false;
|
|
2987
|
-
}
|
|
2988
|
-
function objectEquals(left, right) {
|
|
2989
|
-
const leftMap = left.mapValue.fields || {};
|
|
2990
|
-
const rightMap = right.mapValue.fields || {};
|
|
2991
|
-
if (objectSize(leftMap) !== objectSize(rightMap)) {
|
|
2992
|
-
return false;
|
|
3355
|
+
class SortedSet {
|
|
3356
|
+
constructor(comparator) {
|
|
3357
|
+
this.comparator = comparator;
|
|
3358
|
+
this.data = new SortedMap(this.comparator);
|
|
2993
3359
|
}
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
if (rightMap[key] === undefined ||
|
|
2997
|
-
!valueEquals(leftMap[key], rightMap[key])) {
|
|
2998
|
-
return false;
|
|
2999
|
-
}
|
|
3000
|
-
}
|
|
3360
|
+
has(elem) {
|
|
3361
|
+
return this.data.get(elem) !== null;
|
|
3001
3362
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
/** Returns true if the ArrayValue contains the specified element. */
|
|
3005
|
-
function arrayValueContains(haystack, needle) {
|
|
3006
|
-
return ((haystack.values || []).find(v => valueEquals(v, needle)) !== undefined);
|
|
3007
|
-
}
|
|
3008
|
-
function valueCompare(left, right) {
|
|
3009
|
-
if (left === right) {
|
|
3010
|
-
return 0;
|
|
3363
|
+
first() {
|
|
3364
|
+
return this.data.minKey();
|
|
3011
3365
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
if (leftType !== rightType) {
|
|
3015
|
-
return primitiveComparator(leftType, rightType);
|
|
3366
|
+
last() {
|
|
3367
|
+
return this.data.maxKey();
|
|
3016
3368
|
}
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
case 9007199254740991 /* MaxValue */:
|
|
3020
|
-
return 0;
|
|
3021
|
-
case 1 /* BooleanValue */:
|
|
3022
|
-
return primitiveComparator(left.booleanValue, right.booleanValue);
|
|
3023
|
-
case 2 /* NumberValue */:
|
|
3024
|
-
return compareNumbers(left, right);
|
|
3025
|
-
case 3 /* TimestampValue */:
|
|
3026
|
-
return compareTimestamps(left.timestampValue, right.timestampValue);
|
|
3027
|
-
case 4 /* ServerTimestampValue */:
|
|
3028
|
-
return compareTimestamps(getLocalWriteTime(left), getLocalWriteTime(right));
|
|
3029
|
-
case 5 /* StringValue */:
|
|
3030
|
-
return primitiveComparator(left.stringValue, right.stringValue);
|
|
3031
|
-
case 6 /* BlobValue */:
|
|
3032
|
-
return compareBlobs(left.bytesValue, right.bytesValue);
|
|
3033
|
-
case 7 /* RefValue */:
|
|
3034
|
-
return compareReferences(left.referenceValue, right.referenceValue);
|
|
3035
|
-
case 8 /* GeoPointValue */:
|
|
3036
|
-
return compareGeoPoints(left.geoPointValue, right.geoPointValue);
|
|
3037
|
-
case 9 /* ArrayValue */:
|
|
3038
|
-
return compareArrays(left.arrayValue, right.arrayValue);
|
|
3039
|
-
case 10 /* ObjectValue */:
|
|
3040
|
-
return compareMaps(left.mapValue, right.mapValue);
|
|
3041
|
-
default:
|
|
3042
|
-
throw fail();
|
|
3369
|
+
get size() {
|
|
3370
|
+
return this.data.size;
|
|
3043
3371
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
const leftNumber = normalizeNumber(left.integerValue || left.doubleValue);
|
|
3047
|
-
const rightNumber = normalizeNumber(right.integerValue || right.doubleValue);
|
|
3048
|
-
if (leftNumber < rightNumber) {
|
|
3049
|
-
return -1;
|
|
3372
|
+
indexOf(elem) {
|
|
3373
|
+
return this.data.indexOf(elem);
|
|
3050
3374
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3375
|
+
/** Iterates elements in order defined by "comparator" */
|
|
3376
|
+
forEach(cb) {
|
|
3377
|
+
this.data.inorderTraversal((k, v) => {
|
|
3378
|
+
cb(k);
|
|
3379
|
+
return false;
|
|
3380
|
+
});
|
|
3053
3381
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3382
|
+
/** Iterates over `elem`s such that: range[0] <= elem < range[1]. */
|
|
3383
|
+
forEachInRange(range, cb) {
|
|
3384
|
+
const iter = this.data.getIteratorFrom(range[0]);
|
|
3385
|
+
while (iter.hasNext()) {
|
|
3386
|
+
const elem = iter.getNext();
|
|
3387
|
+
if (this.comparator(elem.key, range[1]) >= 0) {
|
|
3388
|
+
return;
|
|
3389
|
+
}
|
|
3390
|
+
cb(elem.key);
|
|
3391
|
+
}
|
|
3056
3392
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3393
|
+
/**
|
|
3394
|
+
* Iterates over `elem`s such that: start <= elem until false is returned.
|
|
3395
|
+
*/
|
|
3396
|
+
forEachWhile(cb, start) {
|
|
3397
|
+
let iter;
|
|
3398
|
+
if (start !== undefined) {
|
|
3399
|
+
iter = this.data.getIteratorFrom(start);
|
|
3061
3400
|
}
|
|
3062
3401
|
else {
|
|
3063
|
-
|
|
3402
|
+
iter = this.data.getIterator();
|
|
3403
|
+
}
|
|
3404
|
+
while (iter.hasNext()) {
|
|
3405
|
+
const elem = iter.getNext();
|
|
3406
|
+
const result = cb(elem.key);
|
|
3407
|
+
if (!result) {
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3064
3410
|
}
|
|
3065
3411
|
}
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
left.length === right.length) {
|
|
3071
|
-
return primitiveComparator(left, right);
|
|
3412
|
+
/** Finds the least element greater than or equal to `elem`. */
|
|
3413
|
+
firstAfterOrEqual(elem) {
|
|
3414
|
+
const iter = this.data.getIteratorFrom(elem);
|
|
3415
|
+
return iter.hasNext() ? iter.getNext().key : null;
|
|
3072
3416
|
}
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
const comparison = primitiveComparator(leftTimestamp.seconds, rightTimestamp.seconds);
|
|
3076
|
-
if (comparison !== 0) {
|
|
3077
|
-
return comparison;
|
|
3417
|
+
getIterator() {
|
|
3418
|
+
return new SortedSetIterator(this.data.getIterator());
|
|
3078
3419
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
function compareReferences(leftPath, rightPath) {
|
|
3082
|
-
const leftSegments = leftPath.split('/');
|
|
3083
|
-
const rightSegments = rightPath.split('/');
|
|
3084
|
-
for (let i = 0; i < leftSegments.length && i < rightSegments.length; i++) {
|
|
3085
|
-
const comparison = primitiveComparator(leftSegments[i], rightSegments[i]);
|
|
3086
|
-
if (comparison !== 0) {
|
|
3087
|
-
return comparison;
|
|
3088
|
-
}
|
|
3420
|
+
getIteratorFrom(key) {
|
|
3421
|
+
return new SortedSetIterator(this.data.getIteratorFrom(key));
|
|
3089
3422
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
const comparison = primitiveComparator(normalizeNumber(left.latitude), normalizeNumber(right.latitude));
|
|
3094
|
-
if (comparison !== 0) {
|
|
3095
|
-
return comparison;
|
|
3423
|
+
/** Inserts or updates an element */
|
|
3424
|
+
add(elem) {
|
|
3425
|
+
return this.copy(this.data.remove(elem).insert(elem, true));
|
|
3096
3426
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
const rightBytes = normalizeByteString(right);
|
|
3102
|
-
return leftBytes.compareTo(rightBytes);
|
|
3103
|
-
}
|
|
3104
|
-
function compareArrays(left, right) {
|
|
3105
|
-
const leftArray = left.values || [];
|
|
3106
|
-
const rightArray = right.values || [];
|
|
3107
|
-
for (let i = 0; i < leftArray.length && i < rightArray.length; ++i) {
|
|
3108
|
-
const compare = valueCompare(leftArray[i], rightArray[i]);
|
|
3109
|
-
if (compare) {
|
|
3110
|
-
return compare;
|
|
3427
|
+
/** Deletes an element */
|
|
3428
|
+
delete(elem) {
|
|
3429
|
+
if (!this.has(elem)) {
|
|
3430
|
+
return this;
|
|
3111
3431
|
}
|
|
3432
|
+
return this.copy(this.data.remove(elem));
|
|
3112
3433
|
}
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
function compareMaps(left, right) {
|
|
3116
|
-
if (left === MAX_VALUE.mapValue && right === MAX_VALUE.mapValue) {
|
|
3117
|
-
return 0;
|
|
3118
|
-
}
|
|
3119
|
-
else if (left === MAX_VALUE.mapValue) {
|
|
3120
|
-
return 1;
|
|
3434
|
+
isEmpty() {
|
|
3435
|
+
return this.data.isEmpty();
|
|
3121
3436
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3437
|
+
unionWith(other) {
|
|
3438
|
+
let result = this;
|
|
3439
|
+
// Make sure `result` always refers to the larger one of the two sets.
|
|
3440
|
+
if (result.size < other.size) {
|
|
3441
|
+
result = other;
|
|
3442
|
+
other = this;
|
|
3443
|
+
}
|
|
3444
|
+
other.forEach(elem => {
|
|
3445
|
+
result = result.add(elem);
|
|
3446
|
+
});
|
|
3447
|
+
return result;
|
|
3124
3448
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
const rightKeys = Object.keys(rightMap);
|
|
3129
|
-
// Even though MapValues are likely sorted correctly based on their insertion
|
|
3130
|
-
// order (e.g. when received from the backend), local modifications can bring
|
|
3131
|
-
// elements out of order. We need to re-sort the elements to ensure that
|
|
3132
|
-
// canonical IDs are independent of insertion order.
|
|
3133
|
-
leftKeys.sort();
|
|
3134
|
-
rightKeys.sort();
|
|
3135
|
-
for (let i = 0; i < leftKeys.length && i < rightKeys.length; ++i) {
|
|
3136
|
-
const keyCompare = primitiveComparator(leftKeys[i], rightKeys[i]);
|
|
3137
|
-
if (keyCompare !== 0) {
|
|
3138
|
-
return keyCompare;
|
|
3449
|
+
isEqual(other) {
|
|
3450
|
+
if (!(other instanceof SortedSet)) {
|
|
3451
|
+
return false;
|
|
3139
3452
|
}
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3453
|
+
if (this.size !== other.size) {
|
|
3454
|
+
return false;
|
|
3455
|
+
}
|
|
3456
|
+
const thisIt = this.data.getIterator();
|
|
3457
|
+
const otherIt = other.data.getIterator();
|
|
3458
|
+
while (thisIt.hasNext()) {
|
|
3459
|
+
const thisElem = thisIt.getNext().key;
|
|
3460
|
+
const otherElem = otherIt.getNext().key;
|
|
3461
|
+
if (this.comparator(thisElem, otherElem) !== 0) {
|
|
3462
|
+
return false;
|
|
3463
|
+
}
|
|
3143
3464
|
}
|
|
3465
|
+
return true;
|
|
3466
|
+
}
|
|
3467
|
+
toArray() {
|
|
3468
|
+
const res = [];
|
|
3469
|
+
this.forEach(targetId => {
|
|
3470
|
+
res.push(targetId);
|
|
3471
|
+
});
|
|
3472
|
+
return res;
|
|
3473
|
+
}
|
|
3474
|
+
toString() {
|
|
3475
|
+
const result = [];
|
|
3476
|
+
this.forEach(elem => result.push(elem));
|
|
3477
|
+
return 'SortedSet(' + result.toString() + ')';
|
|
3478
|
+
}
|
|
3479
|
+
copy(data) {
|
|
3480
|
+
const result = new SortedSet(this.comparator);
|
|
3481
|
+
result.data = data;
|
|
3482
|
+
return result;
|
|
3144
3483
|
}
|
|
3145
|
-
return primitiveComparator(leftKeys.length, rightKeys.length);
|
|
3146
|
-
}
|
|
3147
|
-
/** Returns a reference value for the provided database and key. */
|
|
3148
|
-
function refValue(databaseId, key) {
|
|
3149
|
-
return {
|
|
3150
|
-
referenceValue: `projects/${databaseId.projectId}/databases/${databaseId.database}/documents/${key.path.canonicalString()}`
|
|
3151
|
-
};
|
|
3152
|
-
}
|
|
3153
|
-
/** Returns true if `value` is an ArrayValue. */
|
|
3154
|
-
function isArray(value) {
|
|
3155
|
-
return !!value && 'arrayValue' in value;
|
|
3156
|
-
}
|
|
3157
|
-
/** Returns true if `value` is a NullValue. */
|
|
3158
|
-
function isNullValue(value) {
|
|
3159
|
-
return !!value && 'nullValue' in value;
|
|
3160
|
-
}
|
|
3161
|
-
/** Returns true if `value` is NaN. */
|
|
3162
|
-
function isNanValue(value) {
|
|
3163
|
-
return !!value && 'doubleValue' in value && isNaN(Number(value.doubleValue));
|
|
3164
|
-
}
|
|
3165
|
-
/** Returns true if `value` is a MapValue. */
|
|
3166
|
-
function isMapValue(value) {
|
|
3167
|
-
return !!value && 'mapValue' in value;
|
|
3168
3484
|
}
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3485
|
+
class SortedSetIterator {
|
|
3486
|
+
constructor(iter) {
|
|
3487
|
+
this.iter = iter;
|
|
3488
|
+
}
|
|
3489
|
+
getNext() {
|
|
3490
|
+
return this.iter.getNext().key;
|
|
3491
|
+
}
|
|
3492
|
+
hasNext() {
|
|
3493
|
+
return this.iter.hasNext();
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
/**
|
|
3498
|
+
* @license
|
|
3499
|
+
* Copyright 2020 Google LLC
|
|
3500
|
+
*
|
|
3501
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3502
|
+
* you may not use this file except in compliance with the License.
|
|
3503
|
+
* You may obtain a copy of the License at
|
|
3504
|
+
*
|
|
3505
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3506
|
+
*
|
|
3507
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3508
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3509
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3510
|
+
* See the License for the specific language governing permissions and
|
|
3511
|
+
* limitations under the License.
|
|
3512
|
+
*/
|
|
3513
|
+
/**
|
|
3514
|
+
* Provides a set of fields that can be used to partially patch a document.
|
|
3515
|
+
* FieldMask is used in conjunction with ObjectValue.
|
|
3516
|
+
* Examples:
|
|
3517
|
+
* foo - Overwrites foo entirely with the provided value. If foo is not
|
|
3518
|
+
* present in the companion ObjectValue, the field is deleted.
|
|
3519
|
+
* foo.bar - Overwrites only the field bar of the object foo.
|
|
3520
|
+
* If foo is not an object, foo is replaced with an object
|
|
3521
|
+
* containing foo
|
|
3522
|
+
*/
|
|
3523
|
+
class FieldMask {
|
|
3524
|
+
constructor(fields) {
|
|
3525
|
+
this.fields = fields;
|
|
3526
|
+
// TODO(dimond): validation of FieldMask
|
|
3527
|
+
// Sort the field mask to support `FieldMask.isEqual()` and assert below.
|
|
3528
|
+
fields.sort(FieldPath$1.comparator);
|
|
3173
3529
|
}
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
return { timestampValue: Object.assign({}, source.timestampValue) };
|
|
3530
|
+
static empty() {
|
|
3531
|
+
return new FieldMask([]);
|
|
3177
3532
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3533
|
+
/**
|
|
3534
|
+
* Returns a new FieldMask object that is the result of adding all the given
|
|
3535
|
+
* fields paths to this field mask.
|
|
3536
|
+
*/
|
|
3537
|
+
unionWith(extraFields) {
|
|
3538
|
+
let mergedMaskSet = new SortedSet(FieldPath$1.comparator);
|
|
3539
|
+
for (const fieldPath of this.fields) {
|
|
3540
|
+
mergedMaskSet = mergedMaskSet.add(fieldPath);
|
|
3541
|
+
}
|
|
3542
|
+
for (const fieldPath of extraFields) {
|
|
3543
|
+
mergedMaskSet = mergedMaskSet.add(fieldPath);
|
|
3544
|
+
}
|
|
3545
|
+
return new FieldMask(mergedMaskSet.toArray());
|
|
3182
3546
|
}
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3547
|
+
/**
|
|
3548
|
+
* Verifies that `fieldPath` is included by at least one field in this field
|
|
3549
|
+
* mask.
|
|
3550
|
+
*
|
|
3551
|
+
* This is an O(n) operation, where `n` is the size of the field mask.
|
|
3552
|
+
*/
|
|
3553
|
+
covers(fieldPath) {
|
|
3554
|
+
for (const fieldMaskPath of this.fields) {
|
|
3555
|
+
if (fieldMaskPath.isPrefixOf(fieldPath)) {
|
|
3556
|
+
return true;
|
|
3557
|
+
}
|
|
3187
3558
|
}
|
|
3188
|
-
return
|
|
3559
|
+
return false;
|
|
3189
3560
|
}
|
|
3190
|
-
|
|
3191
|
-
return
|
|
3561
|
+
isEqual(other) {
|
|
3562
|
+
return arrayEquals(this.fields, other.fields, (l, r) => l.isEqual(r));
|
|
3192
3563
|
}
|
|
3193
|
-
}
|
|
3194
|
-
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
3195
|
-
function isMaxValue(value) {
|
|
3196
|
-
return ((((value.mapValue || {}).fields || {})['__type__'] || {}).stringValue ===
|
|
3197
|
-
MAX_VALUE_TYPE);
|
|
3198
3564
|
}
|
|
3199
3565
|
|
|
3200
3566
|
/**
|
|
@@ -3360,11 +3726,12 @@ class ObjectValue {
|
|
|
3360
3726
|
* from all views.
|
|
3361
3727
|
*/
|
|
3362
3728
|
class MutableDocument {
|
|
3363
|
-
constructor(key, documentType, version, readTime, data, documentState) {
|
|
3729
|
+
constructor(key, documentType, version, readTime, createTime, data, documentState) {
|
|
3364
3730
|
this.key = key;
|
|
3365
3731
|
this.documentType = documentType;
|
|
3366
3732
|
this.version = version;
|
|
3367
3733
|
this.readTime = readTime;
|
|
3734
|
+
this.createTime = createTime;
|
|
3368
3735
|
this.data = data;
|
|
3369
3736
|
this.documentState = documentState;
|
|
3370
3737
|
}
|
|
@@ -3373,18 +3740,27 @@ class MutableDocument {
|
|
|
3373
3740
|
* base document for mutations.
|
|
3374
3741
|
*/
|
|
3375
3742
|
static newInvalidDocument(documentKey) {
|
|
3376
|
-
return new MutableDocument(documentKey, 0 /* INVALID */,
|
|
3743
|
+
return new MutableDocument(documentKey, 0 /* DocumentType.INVALID */,
|
|
3744
|
+
/* version */ SnapshotVersion.min(),
|
|
3745
|
+
/* readTime */ SnapshotVersion.min(),
|
|
3746
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
3377
3747
|
}
|
|
3378
3748
|
/**
|
|
3379
3749
|
* Creates a new document that is known to exist with the given data at the
|
|
3380
3750
|
* given version.
|
|
3381
3751
|
*/
|
|
3382
|
-
static newFoundDocument(documentKey, version, value) {
|
|
3383
|
-
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */,
|
|
3752
|
+
static newFoundDocument(documentKey, version, createTime, value) {
|
|
3753
|
+
return new MutableDocument(documentKey, 1 /* DocumentType.FOUND_DOCUMENT */,
|
|
3754
|
+
/* version */ version,
|
|
3755
|
+
/* readTime */ SnapshotVersion.min(),
|
|
3756
|
+
/* createTime */ createTime, value, 0 /* DocumentState.SYNCED */);
|
|
3384
3757
|
}
|
|
3385
3758
|
/** Creates a new document that is known to not exist at the given version. */
|
|
3386
3759
|
static newNoDocument(documentKey, version) {
|
|
3387
|
-
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */,
|
|
3760
|
+
return new MutableDocument(documentKey, 2 /* DocumentType.NO_DOCUMENT */,
|
|
3761
|
+
/* version */ version,
|
|
3762
|
+
/* readTime */ SnapshotVersion.min(),
|
|
3763
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 0 /* DocumentState.SYNCED */);
|
|
3388
3764
|
}
|
|
3389
3765
|
/**
|
|
3390
3766
|
* Creates a new document that is known to exist at the given version but
|
|
@@ -3392,17 +3768,30 @@ class MutableDocument {
|
|
|
3392
3768
|
* base document).
|
|
3393
3769
|
*/
|
|
3394
3770
|
static newUnknownDocument(documentKey, version) {
|
|
3395
|
-
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */,
|
|
3771
|
+
return new MutableDocument(documentKey, 3 /* DocumentType.UNKNOWN_DOCUMENT */,
|
|
3772
|
+
/* version */ version,
|
|
3773
|
+
/* readTime */ SnapshotVersion.min(),
|
|
3774
|
+
/* createTime */ SnapshotVersion.min(), ObjectValue.empty(), 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */);
|
|
3396
3775
|
}
|
|
3397
3776
|
/**
|
|
3398
3777
|
* Changes the document type to indicate that it exists and that its version
|
|
3399
3778
|
* and data are known.
|
|
3400
3779
|
*/
|
|
3401
3780
|
convertToFoundDocument(version, value) {
|
|
3781
|
+
// If a document is switching state from being an invalid or deleted
|
|
3782
|
+
// document to a valid (FOUND_DOCUMENT) document, either due to receiving an
|
|
3783
|
+
// update from Watch or due to applying a local set mutation on top
|
|
3784
|
+
// of a deleted document, our best guess about its createTime would be the
|
|
3785
|
+
// version at which the document transitioned to a FOUND_DOCUMENT.
|
|
3786
|
+
if (this.createTime.isEqual(SnapshotVersion.min()) &&
|
|
3787
|
+
(this.documentType === 2 /* DocumentType.NO_DOCUMENT */ ||
|
|
3788
|
+
this.documentType === 0 /* DocumentType.INVALID */)) {
|
|
3789
|
+
this.createTime = version;
|
|
3790
|
+
}
|
|
3402
3791
|
this.version = version;
|
|
3403
|
-
this.documentType = 1 /* FOUND_DOCUMENT */;
|
|
3792
|
+
this.documentType = 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
3404
3793
|
this.data = value;
|
|
3405
|
-
this.documentState = 0 /* SYNCED */;
|
|
3794
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
3406
3795
|
return this;
|
|
3407
3796
|
}
|
|
3408
3797
|
/**
|
|
@@ -3411,9 +3800,9 @@ class MutableDocument {
|
|
|
3411
3800
|
*/
|
|
3412
3801
|
convertToNoDocument(version) {
|
|
3413
3802
|
this.version = version;
|
|
3414
|
-
this.documentType = 2 /* NO_DOCUMENT */;
|
|
3803
|
+
this.documentType = 2 /* DocumentType.NO_DOCUMENT */;
|
|
3415
3804
|
this.data = ObjectValue.empty();
|
|
3416
|
-
this.documentState = 0 /* SYNCED */;
|
|
3805
|
+
this.documentState = 0 /* DocumentState.SYNCED */;
|
|
3417
3806
|
return this;
|
|
3418
3807
|
}
|
|
3419
3808
|
/**
|
|
@@ -3423,17 +3812,17 @@ class MutableDocument {
|
|
|
3423
3812
|
*/
|
|
3424
3813
|
convertToUnknownDocument(version) {
|
|
3425
3814
|
this.version = version;
|
|
3426
|
-
this.documentType = 3 /* UNKNOWN_DOCUMENT */;
|
|
3815
|
+
this.documentType = 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
3427
3816
|
this.data = ObjectValue.empty();
|
|
3428
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
3817
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
3429
3818
|
return this;
|
|
3430
3819
|
}
|
|
3431
3820
|
setHasCommittedMutations() {
|
|
3432
|
-
this.documentState = 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
3821
|
+
this.documentState = 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
3433
3822
|
return this;
|
|
3434
3823
|
}
|
|
3435
3824
|
setHasLocalMutations() {
|
|
3436
|
-
this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
|
|
3825
|
+
this.documentState = 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
3437
3826
|
this.version = SnapshotVersion.min();
|
|
3438
3827
|
return this;
|
|
3439
3828
|
}
|
|
@@ -3442,337 +3831,115 @@ class MutableDocument {
|
|
|
3442
3831
|
return this;
|
|
3443
3832
|
}
|
|
3444
3833
|
get hasLocalMutations() {
|
|
3445
|
-
return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
|
|
3446
|
-
}
|
|
3447
|
-
get hasCommittedMutations() {
|
|
3448
|
-
return this.documentState === 2 /* HAS_COMMITTED_MUTATIONS */;
|
|
3449
|
-
}
|
|
3450
|
-
get hasPendingWrites() {
|
|
3451
|
-
return this.hasLocalMutations || this.hasCommittedMutations;
|
|
3452
|
-
}
|
|
3453
|
-
isValidDocument() {
|
|
3454
|
-
return this.documentType !== 0 /* INVALID */;
|
|
3455
|
-
}
|
|
3456
|
-
isFoundDocument() {
|
|
3457
|
-
return this.documentType === 1 /* FOUND_DOCUMENT */;
|
|
3458
|
-
}
|
|
3459
|
-
isNoDocument() {
|
|
3460
|
-
return this.documentType === 2 /* NO_DOCUMENT */;
|
|
3461
|
-
}
|
|
3462
|
-
isUnknownDocument() {
|
|
3463
|
-
return this.documentType === 3 /* UNKNOWN_DOCUMENT */;
|
|
3464
|
-
}
|
|
3465
|
-
isEqual(other) {
|
|
3466
|
-
return (other instanceof MutableDocument &&
|
|
3467
|
-
this.key.isEqual(other.key) &&
|
|
3468
|
-
this.version.isEqual(other.version) &&
|
|
3469
|
-
this.documentType === other.documentType &&
|
|
3470
|
-
this.documentState === other.documentState &&
|
|
3471
|
-
this.data.isEqual(other.data));
|
|
3472
|
-
}
|
|
3473
|
-
mutableCopy() {
|
|
3474
|
-
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
|
|
3475
|
-
}
|
|
3476
|
-
toString() {
|
|
3477
|
-
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
3478
|
-
`{documentType: ${this.documentType}}), ` +
|
|
3479
|
-
`{documentState: ${this.documentState}})`);
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
3482
|
-
|
|
3483
|
-
/**
|
|
3484
|
-
* @license
|
|
3485
|
-
* Copyright 2019 Google LLC
|
|
3486
|
-
*
|
|
3487
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3488
|
-
* you may not use this file except in compliance with the License.
|
|
3489
|
-
* You may obtain a copy of the License at
|
|
3490
|
-
*
|
|
3491
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3492
|
-
*
|
|
3493
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
3494
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3495
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3496
|
-
* See the License for the specific language governing permissions and
|
|
3497
|
-
* limitations under the License.
|
|
3498
|
-
*/
|
|
3499
|
-
// Visible for testing
|
|
3500
|
-
class TargetImpl {
|
|
3501
|
-
constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
3502
|
-
this.path = path;
|
|
3503
|
-
this.collectionGroup = collectionGroup;
|
|
3504
|
-
this.orderBy = orderBy;
|
|
3505
|
-
this.filters = filters;
|
|
3506
|
-
this.limit = limit;
|
|
3507
|
-
this.startAt = startAt;
|
|
3508
|
-
this.endAt = endAt;
|
|
3509
|
-
this.memoizedCanonicalId = null;
|
|
3510
|
-
}
|
|
3511
|
-
}
|
|
3512
|
-
/**
|
|
3513
|
-
* Initializes a Target with a path and optional additional query constraints.
|
|
3514
|
-
* Path must currently be empty if this is a collection group query.
|
|
3515
|
-
*
|
|
3516
|
-
* NOTE: you should always construct `Target` from `Query.toTarget` instead of
|
|
3517
|
-
* using this factory method, because `Query` provides an implicit `orderBy`
|
|
3518
|
-
* property.
|
|
3519
|
-
*/
|
|
3520
|
-
function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
3521
|
-
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
3522
|
-
}
|
|
3523
|
-
function targetEquals(left, right) {
|
|
3524
|
-
if (left.limit !== right.limit) {
|
|
3525
|
-
return false;
|
|
3526
|
-
}
|
|
3527
|
-
if (left.orderBy.length !== right.orderBy.length) {
|
|
3528
|
-
return false;
|
|
3529
|
-
}
|
|
3530
|
-
for (let i = 0; i < left.orderBy.length; i++) {
|
|
3531
|
-
if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
|
|
3532
|
-
return false;
|
|
3533
|
-
}
|
|
3534
|
-
}
|
|
3535
|
-
if (left.filters.length !== right.filters.length) {
|
|
3536
|
-
return false;
|
|
3537
|
-
}
|
|
3538
|
-
for (let i = 0; i < left.filters.length; i++) {
|
|
3539
|
-
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
3540
|
-
return false;
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
if (left.collectionGroup !== right.collectionGroup) {
|
|
3544
|
-
return false;
|
|
3545
|
-
}
|
|
3546
|
-
if (!left.path.isEqual(right.path)) {
|
|
3547
|
-
return false;
|
|
3548
|
-
}
|
|
3549
|
-
if (!boundEquals(left.startAt, right.startAt)) {
|
|
3550
|
-
return false;
|
|
3551
|
-
}
|
|
3552
|
-
return boundEquals(left.endAt, right.endAt);
|
|
3553
|
-
}
|
|
3554
|
-
class Filter {
|
|
3555
|
-
}
|
|
3556
|
-
class FieldFilter extends Filter {
|
|
3557
|
-
constructor(field, op, value) {
|
|
3558
|
-
super();
|
|
3559
|
-
this.field = field;
|
|
3560
|
-
this.op = op;
|
|
3561
|
-
this.value = value;
|
|
3562
|
-
}
|
|
3563
|
-
/**
|
|
3564
|
-
* Creates a filter based on the provided arguments.
|
|
3565
|
-
*/
|
|
3566
|
-
static create(field, op, value) {
|
|
3567
|
-
if (field.isKeyField()) {
|
|
3568
|
-
if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
3569
|
-
return this.createKeyFieldInFilter(field, op, value);
|
|
3570
|
-
}
|
|
3571
|
-
else {
|
|
3572
|
-
return new KeyFieldFilter(field, op, value);
|
|
3573
|
-
}
|
|
3574
|
-
}
|
|
3575
|
-
else if (op === "array-contains" /* ARRAY_CONTAINS */) {
|
|
3576
|
-
return new ArrayContainsFilter(field, value);
|
|
3577
|
-
}
|
|
3578
|
-
else if (op === "in" /* IN */) {
|
|
3579
|
-
return new InFilter(field, value);
|
|
3580
|
-
}
|
|
3581
|
-
else if (op === "not-in" /* NOT_IN */) {
|
|
3582
|
-
return new NotInFilter(field, value);
|
|
3583
|
-
}
|
|
3584
|
-
else if (op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
3585
|
-
return new ArrayContainsAnyFilter(field, value);
|
|
3586
|
-
}
|
|
3587
|
-
else {
|
|
3588
|
-
return new FieldFilter(field, op, value);
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
static createKeyFieldInFilter(field, op, value) {
|
|
3592
|
-
return op === "in" /* IN */
|
|
3593
|
-
? new KeyFieldInFilter(field, value)
|
|
3594
|
-
: new KeyFieldNotInFilter(field, value);
|
|
3595
|
-
}
|
|
3596
|
-
matches(doc) {
|
|
3597
|
-
const other = doc.data.field(this.field);
|
|
3598
|
-
// Types do not have to match in NOT_EQUAL filters.
|
|
3599
|
-
if (this.op === "!=" /* NOT_EQUAL */) {
|
|
3600
|
-
return (other !== null &&
|
|
3601
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
3602
|
-
}
|
|
3603
|
-
// Only compare types with matching backend order (such as double and int).
|
|
3604
|
-
return (other !== null &&
|
|
3605
|
-
typeOrder(this.value) === typeOrder(other) &&
|
|
3606
|
-
this.matchesComparison(valueCompare(other, this.value)));
|
|
3607
|
-
}
|
|
3608
|
-
matchesComparison(comparison) {
|
|
3609
|
-
switch (this.op) {
|
|
3610
|
-
case "<" /* LESS_THAN */:
|
|
3611
|
-
return comparison < 0;
|
|
3612
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
3613
|
-
return comparison <= 0;
|
|
3614
|
-
case "==" /* EQUAL */:
|
|
3615
|
-
return comparison === 0;
|
|
3616
|
-
case "!=" /* NOT_EQUAL */:
|
|
3617
|
-
return comparison !== 0;
|
|
3618
|
-
case ">" /* GREATER_THAN */:
|
|
3619
|
-
return comparison > 0;
|
|
3620
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
3621
|
-
return comparison >= 0;
|
|
3622
|
-
default:
|
|
3623
|
-
return fail();
|
|
3624
|
-
}
|
|
3625
|
-
}
|
|
3626
|
-
isInequality() {
|
|
3627
|
-
return ([
|
|
3628
|
-
"<" /* LESS_THAN */,
|
|
3629
|
-
"<=" /* LESS_THAN_OR_EQUAL */,
|
|
3630
|
-
">" /* GREATER_THAN */,
|
|
3631
|
-
">=" /* GREATER_THAN_OR_EQUAL */,
|
|
3632
|
-
"!=" /* NOT_EQUAL */,
|
|
3633
|
-
"not-in" /* NOT_IN */
|
|
3634
|
-
].indexOf(this.op) >= 0);
|
|
3635
|
-
}
|
|
3636
|
-
}
|
|
3637
|
-
function filterEquals(f1, f2) {
|
|
3638
|
-
return (f1.op === f2.op &&
|
|
3639
|
-
f1.field.isEqual(f2.field) &&
|
|
3640
|
-
valueEquals(f1.value, f2.value));
|
|
3641
|
-
}
|
|
3642
|
-
/** Filter that matches on key fields (i.e. '__name__'). */
|
|
3643
|
-
class KeyFieldFilter extends FieldFilter {
|
|
3644
|
-
constructor(field, op, value) {
|
|
3645
|
-
super(field, op, value);
|
|
3646
|
-
this.key = DocumentKey.fromName(value.referenceValue);
|
|
3647
|
-
}
|
|
3648
|
-
matches(doc) {
|
|
3649
|
-
const comparison = DocumentKey.comparator(doc.key, this.key);
|
|
3650
|
-
return this.matchesComparison(comparison);
|
|
3651
|
-
}
|
|
3652
|
-
}
|
|
3653
|
-
/** Filter that matches on key fields within an array. */
|
|
3654
|
-
class KeyFieldInFilter extends FieldFilter {
|
|
3655
|
-
constructor(field, value) {
|
|
3656
|
-
super(field, "in" /* IN */, value);
|
|
3657
|
-
this.keys = extractDocumentKeysFromArrayValue("in" /* IN */, value);
|
|
3658
|
-
}
|
|
3659
|
-
matches(doc) {
|
|
3660
|
-
return this.keys.some(key => key.isEqual(doc.key));
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
/** Filter that matches on key fields not present within an array. */
|
|
3664
|
-
class KeyFieldNotInFilter extends FieldFilter {
|
|
3665
|
-
constructor(field, value) {
|
|
3666
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
3667
|
-
this.keys = extractDocumentKeysFromArrayValue("not-in" /* NOT_IN */, value);
|
|
3834
|
+
return this.documentState === 1 /* DocumentState.HAS_LOCAL_MUTATIONS */;
|
|
3668
3835
|
}
|
|
3669
|
-
|
|
3670
|
-
return
|
|
3836
|
+
get hasCommittedMutations() {
|
|
3837
|
+
return this.documentState === 2 /* DocumentState.HAS_COMMITTED_MUTATIONS */;
|
|
3671
3838
|
}
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
var _a;
|
|
3675
|
-
return (((_a = value.arrayValue) === null || _a === void 0 ? void 0 : _a.values) || []).map(v => {
|
|
3676
|
-
return DocumentKey.fromName(v.referenceValue);
|
|
3677
|
-
});
|
|
3678
|
-
}
|
|
3679
|
-
/** A Filter that implements the array-contains operator. */
|
|
3680
|
-
class ArrayContainsFilter extends FieldFilter {
|
|
3681
|
-
constructor(field, value) {
|
|
3682
|
-
super(field, "array-contains" /* ARRAY_CONTAINS */, value);
|
|
3839
|
+
get hasPendingWrites() {
|
|
3840
|
+
return this.hasLocalMutations || this.hasCommittedMutations;
|
|
3683
3841
|
}
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
return isArray(other) && arrayValueContains(other.arrayValue, this.value);
|
|
3842
|
+
isValidDocument() {
|
|
3843
|
+
return this.documentType !== 0 /* DocumentType.INVALID */;
|
|
3687
3844
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
class InFilter extends FieldFilter {
|
|
3691
|
-
constructor(field, value) {
|
|
3692
|
-
super(field, "in" /* IN */, value);
|
|
3845
|
+
isFoundDocument() {
|
|
3846
|
+
return this.documentType === 1 /* DocumentType.FOUND_DOCUMENT */;
|
|
3693
3847
|
}
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
return other !== null && arrayValueContains(this.value.arrayValue, other);
|
|
3848
|
+
isNoDocument() {
|
|
3849
|
+
return this.documentType === 2 /* DocumentType.NO_DOCUMENT */;
|
|
3697
3850
|
}
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
class NotInFilter extends FieldFilter {
|
|
3701
|
-
constructor(field, value) {
|
|
3702
|
-
super(field, "not-in" /* NOT_IN */, value);
|
|
3851
|
+
isUnknownDocument() {
|
|
3852
|
+
return this.documentType === 3 /* DocumentType.UNKNOWN_DOCUMENT */;
|
|
3703
3853
|
}
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3854
|
+
isEqual(other) {
|
|
3855
|
+
return (other instanceof MutableDocument &&
|
|
3856
|
+
this.key.isEqual(other.key) &&
|
|
3857
|
+
this.version.isEqual(other.version) &&
|
|
3858
|
+
this.documentType === other.documentType &&
|
|
3859
|
+
this.documentState === other.documentState &&
|
|
3860
|
+
this.data.isEqual(other.data));
|
|
3710
3861
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
class ArrayContainsAnyFilter extends FieldFilter {
|
|
3714
|
-
constructor(field, value) {
|
|
3715
|
-
super(field, "array-contains-any" /* ARRAY_CONTAINS_ANY */, value);
|
|
3862
|
+
mutableCopy() {
|
|
3863
|
+
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.createTime, this.data.clone(), this.documentState);
|
|
3716
3864
|
}
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
3865
|
+
toString() {
|
|
3866
|
+
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
3867
|
+
`{createTime: ${this.createTime}}), ` +
|
|
3868
|
+
`{documentType: ${this.documentType}}), ` +
|
|
3869
|
+
`{documentState: ${this.documentState}})`);
|
|
3723
3870
|
}
|
|
3724
|
-
}
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3725
3873
|
/**
|
|
3726
|
-
*
|
|
3874
|
+
* @license
|
|
3875
|
+
* Copyright 2019 Google LLC
|
|
3727
3876
|
*
|
|
3728
|
-
*
|
|
3729
|
-
*
|
|
3730
|
-
*
|
|
3877
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3878
|
+
* you may not use this file except in compliance with the License.
|
|
3879
|
+
* You may obtain a copy of the License at
|
|
3731
3880
|
*
|
|
3732
|
-
*
|
|
3733
|
-
* of values for the (potentially implicit) order by clauses of a query.
|
|
3881
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3734
3882
|
*
|
|
3735
|
-
*
|
|
3736
|
-
*
|
|
3737
|
-
*
|
|
3883
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3884
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3885
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3886
|
+
* See the License for the specific language governing permissions and
|
|
3887
|
+
* limitations under the License.
|
|
3738
3888
|
*/
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
this.
|
|
3889
|
+
// Visible for testing
|
|
3890
|
+
class TargetImpl {
|
|
3891
|
+
constructor(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
3892
|
+
this.path = path;
|
|
3893
|
+
this.collectionGroup = collectionGroup;
|
|
3894
|
+
this.orderBy = orderBy;
|
|
3895
|
+
this.filters = filters;
|
|
3896
|
+
this.limit = limit;
|
|
3897
|
+
this.startAt = startAt;
|
|
3898
|
+
this.endAt = endAt;
|
|
3899
|
+
this.memoizedCanonicalId = null;
|
|
3743
3900
|
}
|
|
3744
3901
|
}
|
|
3745
3902
|
/**
|
|
3746
|
-
*
|
|
3903
|
+
* Initializes a Target with a path and optional additional query constraints.
|
|
3904
|
+
* Path must currently be empty if this is a collection group query.
|
|
3905
|
+
*
|
|
3906
|
+
* NOTE: you should always construct `Target` from `Query.toTarget` instead of
|
|
3907
|
+
* using this factory method, because `Query` provides an implicit `orderBy`
|
|
3908
|
+
* property.
|
|
3747
3909
|
*/
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
this.field = field;
|
|
3751
|
-
this.dir = dir;
|
|
3752
|
-
}
|
|
3753
|
-
}
|
|
3754
|
-
function orderByEquals(left, right) {
|
|
3755
|
-
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
3910
|
+
function newTarget(path, collectionGroup = null, orderBy = [], filters = [], limit = null, startAt = null, endAt = null) {
|
|
3911
|
+
return new TargetImpl(path, collectionGroup, orderBy, filters, limit, startAt, endAt);
|
|
3756
3912
|
}
|
|
3757
|
-
function
|
|
3758
|
-
if (left
|
|
3759
|
-
return
|
|
3913
|
+
function targetEquals(left, right) {
|
|
3914
|
+
if (left.limit !== right.limit) {
|
|
3915
|
+
return false;
|
|
3760
3916
|
}
|
|
3761
|
-
|
|
3917
|
+
if (left.orderBy.length !== right.orderBy.length) {
|
|
3762
3918
|
return false;
|
|
3763
3919
|
}
|
|
3764
|
-
|
|
3765
|
-
left.
|
|
3920
|
+
for (let i = 0; i < left.orderBy.length; i++) {
|
|
3921
|
+
if (!orderByEquals(left.orderBy[i], right.orderBy[i])) {
|
|
3922
|
+
return false;
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
if (left.filters.length !== right.filters.length) {
|
|
3766
3926
|
return false;
|
|
3767
3927
|
}
|
|
3768
|
-
for (let i = 0; i < left.
|
|
3769
|
-
|
|
3770
|
-
const rightPosition = right.position[i];
|
|
3771
|
-
if (!valueEquals(leftPosition, rightPosition)) {
|
|
3928
|
+
for (let i = 0; i < left.filters.length; i++) {
|
|
3929
|
+
if (!filterEquals(left.filters[i], right.filters[i])) {
|
|
3772
3930
|
return false;
|
|
3773
3931
|
}
|
|
3774
3932
|
}
|
|
3775
|
-
|
|
3933
|
+
if (left.collectionGroup !== right.collectionGroup) {
|
|
3934
|
+
return false;
|
|
3935
|
+
}
|
|
3936
|
+
if (!left.path.isEqual(right.path)) {
|
|
3937
|
+
return false;
|
|
3938
|
+
}
|
|
3939
|
+
if (!boundEquals(left.startAt, right.startAt)) {
|
|
3940
|
+
return false;
|
|
3941
|
+
}
|
|
3942
|
+
return boundEquals(left.endAt, right.endAt);
|
|
3776
3943
|
}
|
|
3777
3944
|
|
|
3778
3945
|
/**
|
|
@@ -3803,7 +3970,7 @@ class QueryImpl {
|
|
|
3803
3970
|
* Initializes a Query with a path and optional additional query constraints.
|
|
3804
3971
|
* Path must currently be empty if this is a collection group query.
|
|
3805
3972
|
*/
|
|
3806
|
-
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* First */, startAt = null, endAt = null) {
|
|
3973
|
+
constructor(path, collectionGroup = null, explicitOrderBy = [], filters = [], limit = null, limitType = "F" /* LimitType.First */, startAt = null, endAt = null) {
|
|
3807
3974
|
this.path = path;
|
|
3808
3975
|
this.collectionGroup = collectionGroup;
|
|
3809
3976
|
this.explicitOrderBy = explicitOrderBy;
|
|
@@ -3830,20 +3997,9 @@ function getFirstOrderByField(query) {
|
|
|
3830
3997
|
}
|
|
3831
3998
|
function getInequalityFilterField(query) {
|
|
3832
3999
|
for (const filter of query.filters) {
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
}
|
|
3837
|
-
return null;
|
|
3838
|
-
}
|
|
3839
|
-
/**
|
|
3840
|
-
* Checks if any of the provided Operators are included in the query and
|
|
3841
|
-
* returns the first one that is, or null if none are.
|
|
3842
|
-
*/
|
|
3843
|
-
function findFilterOperator(query, operators) {
|
|
3844
|
-
for (const filter of query.filters) {
|
|
3845
|
-
if (operators.indexOf(filter.op) >= 0) {
|
|
3846
|
-
return filter.op;
|
|
4000
|
+
const result = filter.getFirstInequalityField();
|
|
4001
|
+
if (result !== null) {
|
|
4002
|
+
return result;
|
|
3847
4003
|
}
|
|
3848
4004
|
}
|
|
3849
4005
|
return null;
|
|
@@ -3880,7 +4036,7 @@ function queryOrderBy(query) {
|
|
|
3880
4036
|
if (!inequalityField.isKeyField()) {
|
|
3881
4037
|
queryImpl.memoizedOrderBy.push(new OrderBy(inequalityField));
|
|
3882
4038
|
}
|
|
3883
|
-
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* ASCENDING */));
|
|
4039
|
+
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), "asc" /* Direction.ASCENDING */));
|
|
3884
4040
|
}
|
|
3885
4041
|
else {
|
|
3886
4042
|
let foundKeyOrdering = false;
|
|
@@ -3896,7 +4052,7 @@ function queryOrderBy(query) {
|
|
|
3896
4052
|
const lastDirection = queryImpl.explicitOrderBy.length > 0
|
|
3897
4053
|
? queryImpl.explicitOrderBy[queryImpl.explicitOrderBy.length - 1]
|
|
3898
4054
|
.dir
|
|
3899
|
-
: "asc" /* ASCENDING */;
|
|
4055
|
+
: "asc" /* Direction.ASCENDING */;
|
|
3900
4056
|
queryImpl.memoizedOrderBy.push(new OrderBy(FieldPath$1.keyField(), lastDirection));
|
|
3901
4057
|
}
|
|
3902
4058
|
}
|
|
@@ -3909,16 +4065,16 @@ function queryOrderBy(query) {
|
|
|
3909
4065
|
function queryToTarget(query) {
|
|
3910
4066
|
const queryImpl = debugCast(query);
|
|
3911
4067
|
if (!queryImpl.memoizedTarget) {
|
|
3912
|
-
if (queryImpl.limitType === "F" /* First */) {
|
|
4068
|
+
if (queryImpl.limitType === "F" /* LimitType.First */) {
|
|
3913
4069
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, queryOrderBy(queryImpl), queryImpl.filters, queryImpl.limit, queryImpl.startAt, queryImpl.endAt);
|
|
3914
4070
|
}
|
|
3915
4071
|
else {
|
|
3916
4072
|
// Flip the orderBy directions since we want the last results
|
|
3917
4073
|
const orderBys = [];
|
|
3918
4074
|
for (const orderBy of queryOrderBy(queryImpl)) {
|
|
3919
|
-
const dir = orderBy.dir === "desc" /* DESCENDING */
|
|
3920
|
-
? "asc" /* ASCENDING */
|
|
3921
|
-
: "desc" /* DESCENDING */;
|
|
4075
|
+
const dir = orderBy.dir === "desc" /* Direction.DESCENDING */
|
|
4076
|
+
? "asc" /* Direction.ASCENDING */
|
|
4077
|
+
: "desc" /* Direction.DESCENDING */;
|
|
3922
4078
|
orderBys.push(new OrderBy(orderBy.field, dir));
|
|
3923
4079
|
}
|
|
3924
4080
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
@@ -3935,6 +4091,8 @@ function queryToTarget(query) {
|
|
|
3935
4091
|
return queryImpl.memoizedTarget;
|
|
3936
4092
|
}
|
|
3937
4093
|
function queryWithAddedFilter(query, filter) {
|
|
4094
|
+
filter.getFirstInequalityField();
|
|
4095
|
+
getInequalityFilterField(query);
|
|
3938
4096
|
const newFilters = query.filters.concat([filter]);
|
|
3939
4097
|
return new QueryImpl(query.path, query.collectionGroup, query.explicitOrderBy.slice(), newFilters, query.limit, query.limitType, query.startAt, query.endAt);
|
|
3940
4098
|
}
|
|
@@ -4174,7 +4332,7 @@ class SetMutation extends Mutation {
|
|
|
4174
4332
|
this.value = value;
|
|
4175
4333
|
this.precondition = precondition;
|
|
4176
4334
|
this.fieldTransforms = fieldTransforms;
|
|
4177
|
-
this.type = 0 /* Set */;
|
|
4335
|
+
this.type = 0 /* MutationType.Set */;
|
|
4178
4336
|
}
|
|
4179
4337
|
getFieldMask() {
|
|
4180
4338
|
return null;
|
|
@@ -4201,7 +4359,7 @@ class PatchMutation extends Mutation {
|
|
|
4201
4359
|
this.fieldMask = fieldMask;
|
|
4202
4360
|
this.precondition = precondition;
|
|
4203
4361
|
this.fieldTransforms = fieldTransforms;
|
|
4204
|
-
this.type = 1 /* Patch */;
|
|
4362
|
+
this.type = 1 /* MutationType.Patch */;
|
|
4205
4363
|
}
|
|
4206
4364
|
getFieldMask() {
|
|
4207
4365
|
return this.fieldMask;
|
|
@@ -4213,7 +4371,7 @@ class DeleteMutation extends Mutation {
|
|
|
4213
4371
|
super();
|
|
4214
4372
|
this.key = key;
|
|
4215
4373
|
this.precondition = precondition;
|
|
4216
|
-
this.type = 2 /* Delete */;
|
|
4374
|
+
this.type = 2 /* MutationType.Delete */;
|
|
4217
4375
|
this.fieldTransforms = [];
|
|
4218
4376
|
}
|
|
4219
4377
|
getFieldMask() {
|
|
@@ -4232,7 +4390,7 @@ class VerifyMutation extends Mutation {
|
|
|
4232
4390
|
super();
|
|
4233
4391
|
this.key = key;
|
|
4234
4392
|
this.precondition = precondition;
|
|
4235
|
-
this.type = 3 /* Verify */;
|
|
4393
|
+
this.type = 3 /* MutationType.Verify */;
|
|
4236
4394
|
this.fieldTransforms = [];
|
|
4237
4395
|
}
|
|
4238
4396
|
getFieldMask() {
|
|
@@ -4258,22 +4416,28 @@ class VerifyMutation extends Mutation {
|
|
|
4258
4416
|
*/
|
|
4259
4417
|
const DIRECTIONS = (() => {
|
|
4260
4418
|
const dirs = {};
|
|
4261
|
-
dirs["asc" /* ASCENDING */] = 'ASCENDING';
|
|
4262
|
-
dirs["desc" /* DESCENDING */] = 'DESCENDING';
|
|
4419
|
+
dirs["asc" /* Direction.ASCENDING */] = 'ASCENDING';
|
|
4420
|
+
dirs["desc" /* Direction.DESCENDING */] = 'DESCENDING';
|
|
4263
4421
|
return dirs;
|
|
4264
4422
|
})();
|
|
4265
4423
|
const OPERATORS = (() => {
|
|
4266
4424
|
const ops = {};
|
|
4267
|
-
ops["<" /* LESS_THAN */] = 'LESS_THAN';
|
|
4268
|
-
ops["<=" /* LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
4269
|
-
ops[">" /* GREATER_THAN */] = 'GREATER_THAN';
|
|
4270
|
-
ops[">=" /* GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
4271
|
-
ops["==" /* EQUAL */] = 'EQUAL';
|
|
4272
|
-
ops["!=" /* NOT_EQUAL */] = 'NOT_EQUAL';
|
|
4273
|
-
ops["array-contains" /* ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
4274
|
-
ops["in" /* IN */] = 'IN';
|
|
4275
|
-
ops["not-in" /* NOT_IN */] = 'NOT_IN';
|
|
4276
|
-
ops["array-contains-any" /* ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
4425
|
+
ops["<" /* Operator.LESS_THAN */] = 'LESS_THAN';
|
|
4426
|
+
ops["<=" /* Operator.LESS_THAN_OR_EQUAL */] = 'LESS_THAN_OR_EQUAL';
|
|
4427
|
+
ops[">" /* Operator.GREATER_THAN */] = 'GREATER_THAN';
|
|
4428
|
+
ops[">=" /* Operator.GREATER_THAN_OR_EQUAL */] = 'GREATER_THAN_OR_EQUAL';
|
|
4429
|
+
ops["==" /* Operator.EQUAL */] = 'EQUAL';
|
|
4430
|
+
ops["!=" /* Operator.NOT_EQUAL */] = 'NOT_EQUAL';
|
|
4431
|
+
ops["array-contains" /* Operator.ARRAY_CONTAINS */] = 'ARRAY_CONTAINS';
|
|
4432
|
+
ops["in" /* Operator.IN */] = 'IN';
|
|
4433
|
+
ops["not-in" /* Operator.NOT_IN */] = 'NOT_IN';
|
|
4434
|
+
ops["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */] = 'ARRAY_CONTAINS_ANY';
|
|
4435
|
+
return ops;
|
|
4436
|
+
})();
|
|
4437
|
+
const COMPOSITE_OPERATORS = (() => {
|
|
4438
|
+
const ops = {};
|
|
4439
|
+
ops["and" /* CompositeOperator.AND */] = 'AND';
|
|
4440
|
+
ops["or" /* CompositeOperator.OR */] = 'OR';
|
|
4277
4441
|
return ops;
|
|
4278
4442
|
})();
|
|
4279
4443
|
function assertPresent(value, description) {
|
|
@@ -4425,8 +4589,14 @@ function toMutationDocument(serializer, key, fields) {
|
|
|
4425
4589
|
function fromDocument(serializer, document, hasCommittedMutations) {
|
|
4426
4590
|
const key = fromName(serializer, document.name);
|
|
4427
4591
|
const version = fromVersion(document.updateTime);
|
|
4592
|
+
// If we read a document from persistence that is missing createTime, it's due
|
|
4593
|
+
// to older SDK versions not storing this information. In such cases, we'll
|
|
4594
|
+
// set the createTime to zero. This can be removed in the long term.
|
|
4595
|
+
const createTime = document.createTime
|
|
4596
|
+
? fromVersion(document.createTime)
|
|
4597
|
+
: SnapshotVersion.min();
|
|
4428
4598
|
const data = new ObjectValue({ mapValue: { fields: document.fields } });
|
|
4429
|
-
const result = MutableDocument.newFoundDocument(key, version, data);
|
|
4599
|
+
const result = MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
4430
4600
|
if (hasCommittedMutations) {
|
|
4431
4601
|
result.setHasCommittedMutations();
|
|
4432
4602
|
}
|
|
@@ -4438,8 +4608,11 @@ function fromFound(serializer, doc) {
|
|
|
4438
4608
|
assertPresent(doc.found.updateTime);
|
|
4439
4609
|
const key = fromName(serializer, doc.found.name);
|
|
4440
4610
|
const version = fromVersion(doc.found.updateTime);
|
|
4611
|
+
const createTime = doc.found.createTime
|
|
4612
|
+
? fromVersion(doc.found.createTime)
|
|
4613
|
+
: SnapshotVersion.min();
|
|
4441
4614
|
const data = new ObjectValue({ mapValue: { fields: doc.found.fields } });
|
|
4442
|
-
return MutableDocument.newFoundDocument(key, version, data);
|
|
4615
|
+
return MutableDocument.newFoundDocument(key, version, createTime, data);
|
|
4443
4616
|
}
|
|
4444
4617
|
function fromMissing(serializer, result) {
|
|
4445
4618
|
hardAssert(!!result.missing);
|
|
@@ -4553,7 +4726,7 @@ function toQueryTarget(serializer, target) {
|
|
|
4553
4726
|
result.parent = toQueryPath(serializer, path.popLast());
|
|
4554
4727
|
result.structuredQuery.from = [{ collectionId: path.lastSegment() }];
|
|
4555
4728
|
}
|
|
4556
|
-
const where =
|
|
4729
|
+
const where = toFilters(target.filters);
|
|
4557
4730
|
if (where) {
|
|
4558
4731
|
result.structuredQuery.where = where;
|
|
4559
4732
|
}
|
|
@@ -4588,17 +4761,11 @@ function toRunAggregationQueryRequest(serializer, target) {
|
|
|
4588
4761
|
parent: queryTarget.parent
|
|
4589
4762
|
};
|
|
4590
4763
|
}
|
|
4591
|
-
function
|
|
4764
|
+
function toFilters(filters) {
|
|
4592
4765
|
if (filters.length === 0) {
|
|
4593
4766
|
return;
|
|
4594
4767
|
}
|
|
4595
|
-
|
|
4596
|
-
return toUnaryOrFieldFilter(filter);
|
|
4597
|
-
});
|
|
4598
|
-
if (protos.length === 1) {
|
|
4599
|
-
return protos[0];
|
|
4600
|
-
}
|
|
4601
|
-
return { compositeFilter: { op: 'AND', filters: protos } };
|
|
4768
|
+
return toFilter(CompositeFilter.create(filters, "and" /* CompositeOperator.AND */));
|
|
4602
4769
|
}
|
|
4603
4770
|
function toOrder(orderBys) {
|
|
4604
4771
|
if (orderBys.length === 0) {
|
|
@@ -4626,6 +4793,9 @@ function toDirection(dir) {
|
|
|
4626
4793
|
function toOperatorName(op) {
|
|
4627
4794
|
return OPERATORS[op];
|
|
4628
4795
|
}
|
|
4796
|
+
function toCompositeOperatorName(op) {
|
|
4797
|
+
return COMPOSITE_OPERATORS[op];
|
|
4798
|
+
}
|
|
4629
4799
|
function toFieldPathReference(path) {
|
|
4630
4800
|
return { fieldPath: path.canonicalString() };
|
|
4631
4801
|
}
|
|
@@ -4637,8 +4807,31 @@ function toPropertyOrder(orderBy) {
|
|
|
4637
4807
|
};
|
|
4638
4808
|
}
|
|
4639
4809
|
// visible for testing
|
|
4810
|
+
function toFilter(filter) {
|
|
4811
|
+
if (filter instanceof FieldFilter) {
|
|
4812
|
+
return toUnaryOrFieldFilter(filter);
|
|
4813
|
+
}
|
|
4814
|
+
else if (filter instanceof CompositeFilter) {
|
|
4815
|
+
return toCompositeFilter(filter);
|
|
4816
|
+
}
|
|
4817
|
+
else {
|
|
4818
|
+
return fail();
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
function toCompositeFilter(filter) {
|
|
4822
|
+
const protos = filter.getFilters().map(filter => toFilter(filter));
|
|
4823
|
+
if (protos.length === 1) {
|
|
4824
|
+
return protos[0];
|
|
4825
|
+
}
|
|
4826
|
+
return {
|
|
4827
|
+
compositeFilter: {
|
|
4828
|
+
op: toCompositeOperatorName(filter.op),
|
|
4829
|
+
filters: protos
|
|
4830
|
+
}
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4640
4833
|
function toUnaryOrFieldFilter(filter) {
|
|
4641
|
-
if (filter.op === "==" /* EQUAL */) {
|
|
4834
|
+
if (filter.op === "==" /* Operator.EQUAL */) {
|
|
4642
4835
|
if (isNanValue(filter.value)) {
|
|
4643
4836
|
return {
|
|
4644
4837
|
unaryFilter: {
|
|
@@ -4656,7 +4849,7 @@ function toUnaryOrFieldFilter(filter) {
|
|
|
4656
4849
|
};
|
|
4657
4850
|
}
|
|
4658
4851
|
}
|
|
4659
|
-
else if (filter.op === "!=" /* NOT_EQUAL */) {
|
|
4852
|
+
else if (filter.op === "!=" /* Operator.NOT_EQUAL */) {
|
|
4660
4853
|
if (isNanValue(filter.value)) {
|
|
4661
4854
|
return {
|
|
4662
4855
|
unaryFilter: {
|
|
@@ -6036,12 +6229,12 @@ class ParsedUpdateData {
|
|
|
6036
6229
|
}
|
|
6037
6230
|
function isWrite(dataSource) {
|
|
6038
6231
|
switch (dataSource) {
|
|
6039
|
-
case 0 /* Set */: // fall through
|
|
6040
|
-
case 2 /* MergeSet */: // fall through
|
|
6041
|
-
case 1 /* Update */:
|
|
6232
|
+
case 0 /* UserDataSource.Set */: // fall through
|
|
6233
|
+
case 2 /* UserDataSource.MergeSet */: // fall through
|
|
6234
|
+
case 1 /* UserDataSource.Update */:
|
|
6042
6235
|
return true;
|
|
6043
|
-
case 3 /* Argument */:
|
|
6044
|
-
case 4 /* ArrayArgument */:
|
|
6236
|
+
case 3 /* UserDataSource.Argument */:
|
|
6237
|
+
case 4 /* UserDataSource.ArrayArgument */:
|
|
6045
6238
|
return false;
|
|
6046
6239
|
default:
|
|
6047
6240
|
throw fail();
|
|
@@ -6166,8 +6359,8 @@ function newUserDataReader(firestore) {
|
|
|
6166
6359
|
/** Parse document data from a set() call. */
|
|
6167
6360
|
function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter, options = {}) {
|
|
6168
6361
|
const context = userDataReader.createContext(options.merge || options.mergeFields
|
|
6169
|
-
? 2 /* MergeSet */
|
|
6170
|
-
: 0 /* Set */, methodName, targetDoc, hasConverter);
|
|
6362
|
+
? 2 /* UserDataSource.MergeSet */
|
|
6363
|
+
: 0 /* UserDataSource.Set */, methodName, targetDoc, hasConverter);
|
|
6171
6364
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
6172
6365
|
const updateData = parseObject(input, context);
|
|
6173
6366
|
let fieldMask;
|
|
@@ -6198,12 +6391,12 @@ function parseSetData(userDataReader, methodName, targetDoc, input, hasConverter
|
|
|
6198
6391
|
}
|
|
6199
6392
|
class DeleteFieldValueImpl extends FieldValue {
|
|
6200
6393
|
_toFieldTransform(context) {
|
|
6201
|
-
if (context.dataSource === 2 /* MergeSet */) {
|
|
6394
|
+
if (context.dataSource === 2 /* UserDataSource.MergeSet */) {
|
|
6202
6395
|
// No transform to add for a delete, but we need to add it to our
|
|
6203
6396
|
// fieldMask so it gets deleted.
|
|
6204
6397
|
context.fieldMask.push(context.path);
|
|
6205
6398
|
}
|
|
6206
|
-
else if (context.dataSource === 1 /* Update */) {
|
|
6399
|
+
else if (context.dataSource === 1 /* UserDataSource.Update */) {
|
|
6207
6400
|
throw context.createError(`${this._methodName}() can only appear at the top level ` +
|
|
6208
6401
|
'of your update data');
|
|
6209
6402
|
}
|
|
@@ -6236,7 +6429,7 @@ class DeleteFieldValueImpl extends FieldValue {
|
|
|
6236
6429
|
*/
|
|
6237
6430
|
function createSentinelChildContext(fieldValue, context, arrayElement) {
|
|
6238
6431
|
return new ParseContextImpl({
|
|
6239
|
-
dataSource: 3 /* Argument */,
|
|
6432
|
+
dataSource: 3 /* UserDataSource.Argument */,
|
|
6240
6433
|
targetDoc: context.settings.targetDoc,
|
|
6241
6434
|
methodName: fieldValue._methodName,
|
|
6242
6435
|
arrayElement
|
|
@@ -6300,7 +6493,7 @@ class NumericIncrementFieldValueImpl extends FieldValue {
|
|
|
6300
6493
|
}
|
|
6301
6494
|
/** Parse update data from an update() call. */
|
|
6302
6495
|
function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
6303
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
6496
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
6304
6497
|
validatePlainObject('Data must be an object, but it was:', context, input);
|
|
6305
6498
|
const fieldMaskPaths = [];
|
|
6306
6499
|
const updateData = ObjectValue.empty();
|
|
@@ -6327,7 +6520,7 @@ function parseUpdateData(userDataReader, methodName, targetDoc, input) {
|
|
|
6327
6520
|
}
|
|
6328
6521
|
/** Parse update data from a list of field/value arguments. */
|
|
6329
6522
|
function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value, moreFieldsAndValues) {
|
|
6330
|
-
const context = userDataReader.createContext(1 /* Update */, methodName, targetDoc);
|
|
6523
|
+
const context = userDataReader.createContext(1 /* UserDataSource.Update */, methodName, targetDoc);
|
|
6331
6524
|
const keys = [fieldPathFromArgument$1(methodName, field, targetDoc)];
|
|
6332
6525
|
const values = [value];
|
|
6333
6526
|
if (moreFieldsAndValues.length % 2 !== 0) {
|
|
@@ -6374,7 +6567,7 @@ function parseUpdateVarargs(userDataReader, methodName, targetDoc, field, value,
|
|
|
6374
6567
|
* contain additional arrays (e.g. the operand of an `in` query).
|
|
6375
6568
|
*/
|
|
6376
6569
|
function parseQueryValue(userDataReader, methodName, input, allowArrays = false) {
|
|
6377
|
-
const context = userDataReader.createContext(allowArrays ? 4 /* ArrayArgument */ : 3 /* Argument */, methodName);
|
|
6570
|
+
const context = userDataReader.createContext(allowArrays ? 4 /* UserDataSource.ArrayArgument */ : 3 /* UserDataSource.Argument */, methodName);
|
|
6378
6571
|
const parsed = parseData(input, context);
|
|
6379
6572
|
return parsed;
|
|
6380
6573
|
}
|
|
@@ -6424,7 +6617,7 @@ function parseData(input, context) {
|
|
|
6424
6617
|
// contain additional arrays (each representing an individual field
|
|
6425
6618
|
// value), so we disable this validation.
|
|
6426
6619
|
if (context.settings.arrayElement &&
|
|
6427
|
-
context.dataSource !== 4 /* ArrayArgument */) {
|
|
6620
|
+
context.dataSource !== 4 /* UserDataSource.ArrayArgument */) {
|
|
6428
6621
|
throw context.createError('Nested arrays are not supported');
|
|
6429
6622
|
}
|
|
6430
6623
|
return parseArray(input, context);
|
|
@@ -6866,164 +7059,334 @@ function fieldPathFromArgument(methodName, arg) {
|
|
|
6866
7059
|
* limitations under the License.
|
|
6867
7060
|
*/
|
|
6868
7061
|
function validateHasExplicitOrderByForLimitToLast(query) {
|
|
6869
|
-
if (query.limitType === "L" /* Last */ &&
|
|
7062
|
+
if (query.limitType === "L" /* LimitType.Last */ &&
|
|
6870
7063
|
query.explicitOrderBy.length === 0) {
|
|
6871
7064
|
throw new FirestoreError(Code.UNIMPLEMENTED, 'limitToLast() queries require specifying at least one orderBy() clause');
|
|
6872
7065
|
}
|
|
6873
7066
|
}
|
|
7067
|
+
/**
|
|
7068
|
+
* An `AppliableConstraint` is an abstraction of a constraint that can be applied
|
|
7069
|
+
* to a Firestore query.
|
|
7070
|
+
*/
|
|
7071
|
+
class AppliableConstraint {
|
|
7072
|
+
}
|
|
6874
7073
|
/**
|
|
6875
7074
|
* A `QueryConstraint` is used to narrow the set of documents returned by a
|
|
6876
7075
|
* Firestore query. `QueryConstraint`s are created by invoking {@link where},
|
|
6877
|
-
* {@link orderBy}, {@link
|
|
6878
|
-
* endBefore
|
|
7076
|
+
* {@link orderBy}, {@link startAt}, {@link startAfter}, {@link
|
|
7077
|
+
* endBefore}, {@link endAt}, {@link limit}, {@link limitToLast} and
|
|
6879
7078
|
* can then be passed to {@link query} to create a new query instance that
|
|
6880
7079
|
* also contains this `QueryConstraint`.
|
|
6881
7080
|
*/
|
|
6882
|
-
class QueryConstraint {
|
|
7081
|
+
class QueryConstraint extends AppliableConstraint {
|
|
6883
7082
|
}
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
* existing or new constraints.
|
|
6892
|
-
*/
|
|
6893
|
-
function query(query, ...queryConstraints) {
|
|
7083
|
+
function query(query, queryConstraint, ...additionalQueryConstraints) {
|
|
7084
|
+
let queryConstraints = [];
|
|
7085
|
+
if (queryConstraint instanceof AppliableConstraint) {
|
|
7086
|
+
queryConstraints.push(queryConstraint);
|
|
7087
|
+
}
|
|
7088
|
+
queryConstraints = queryConstraints.concat(additionalQueryConstraints);
|
|
7089
|
+
validateQueryConstraintArray(queryConstraints);
|
|
6894
7090
|
for (const constraint of queryConstraints) {
|
|
6895
7091
|
query = constraint._apply(query);
|
|
6896
7092
|
}
|
|
6897
7093
|
return query;
|
|
6898
7094
|
}
|
|
6899
|
-
|
|
7095
|
+
/**
|
|
7096
|
+
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by
|
|
7097
|
+
* a Firestore query by filtering on one or more document fields.
|
|
7098
|
+
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then
|
|
7099
|
+
* be passed to {@link query} to create a new query instance that also contains
|
|
7100
|
+
* this `QueryFieldFilterConstraint`.
|
|
7101
|
+
*/
|
|
7102
|
+
class QueryFieldFilterConstraint extends QueryConstraint {
|
|
7103
|
+
/**
|
|
7104
|
+
* @internal
|
|
7105
|
+
*/
|
|
6900
7106
|
constructor(_field, _op, _value) {
|
|
6901
7107
|
super();
|
|
6902
7108
|
this._field = _field;
|
|
6903
7109
|
this._op = _op;
|
|
6904
7110
|
this._value = _value;
|
|
7111
|
+
/** The type of this query constraint */
|
|
6905
7112
|
this.type = 'where';
|
|
6906
7113
|
}
|
|
7114
|
+
static _create(_field, _op, _value) {
|
|
7115
|
+
return new QueryFieldFilterConstraint(_field, _op, _value);
|
|
7116
|
+
}
|
|
6907
7117
|
_apply(query) {
|
|
7118
|
+
const filter = this._parse(query);
|
|
7119
|
+
validateNewFieldFilter(query._query, filter);
|
|
7120
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, filter));
|
|
7121
|
+
}
|
|
7122
|
+
_parse(query) {
|
|
6908
7123
|
const reader = newUserDataReader(query.firestore);
|
|
6909
7124
|
const filter = newQueryFilter(query._query, 'where', reader, query.firestore._databaseId, this._field, this._op, this._value);
|
|
6910
|
-
return
|
|
7125
|
+
return filter;
|
|
6911
7126
|
}
|
|
6912
7127
|
}
|
|
6913
7128
|
/**
|
|
6914
|
-
* Creates a {@link
|
|
6915
|
-
* specified field and that the value should satisfy the
|
|
6916
|
-
* provided.
|
|
7129
|
+
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents
|
|
7130
|
+
* must contain the specified field and that the value should satisfy the
|
|
7131
|
+
* relation constraint provided.
|
|
6917
7132
|
*
|
|
6918
7133
|
* @param fieldPath - The path to compare
|
|
6919
7134
|
* @param opStr - The operation string (e.g "<", "<=", "==", "<",
|
|
6920
7135
|
* "<=", "!=").
|
|
6921
7136
|
* @param value - The value for comparison
|
|
6922
|
-
* @returns The created {@link
|
|
7137
|
+
* @returns The created {@link QueryFieldFilterConstraint}.
|
|
6923
7138
|
*/
|
|
6924
7139
|
function where(fieldPath, opStr, value) {
|
|
6925
7140
|
const op = opStr;
|
|
6926
7141
|
const field = fieldPathFromArgument('where', fieldPath);
|
|
6927
|
-
return
|
|
7142
|
+
return QueryFieldFilterConstraint._create(field, op, value);
|
|
7143
|
+
}
|
|
7144
|
+
/**
|
|
7145
|
+
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents
|
|
7146
|
+
* returned by a Firestore query by performing the logical OR or AND of multiple
|
|
7147
|
+
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s.
|
|
7148
|
+
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or
|
|
7149
|
+
* {@link and} and can then be passed to {@link query} to create a new query
|
|
7150
|
+
* instance that also contains the `QueryCompositeFilterConstraint`.
|
|
7151
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
7152
|
+
*/
|
|
7153
|
+
class QueryCompositeFilterConstraint extends AppliableConstraint {
|
|
7154
|
+
/**
|
|
7155
|
+
* @internal
|
|
7156
|
+
*/
|
|
7157
|
+
constructor(
|
|
7158
|
+
/** The type of this query constraint */
|
|
7159
|
+
type, _queryConstraints) {
|
|
7160
|
+
super();
|
|
7161
|
+
this.type = type;
|
|
7162
|
+
this._queryConstraints = _queryConstraints;
|
|
7163
|
+
}
|
|
7164
|
+
static _create(type, _queryConstraints) {
|
|
7165
|
+
return new QueryCompositeFilterConstraint(type, _queryConstraints);
|
|
7166
|
+
}
|
|
7167
|
+
_parse(query) {
|
|
7168
|
+
const parsedFilters = this._queryConstraints
|
|
7169
|
+
.map(queryConstraint => {
|
|
7170
|
+
return queryConstraint._parse(query);
|
|
7171
|
+
})
|
|
7172
|
+
.filter(parsedFilter => parsedFilter.getFilters().length > 0);
|
|
7173
|
+
if (parsedFilters.length === 1) {
|
|
7174
|
+
return parsedFilters[0];
|
|
7175
|
+
}
|
|
7176
|
+
return CompositeFilter.create(parsedFilters, this._getOperator());
|
|
7177
|
+
}
|
|
7178
|
+
_apply(query) {
|
|
7179
|
+
const parsedFilter = this._parse(query);
|
|
7180
|
+
if (parsedFilter.getFilters().length === 0) {
|
|
7181
|
+
// Return the existing query if not adding any more filters (e.g. an empty
|
|
7182
|
+
// composite filter).
|
|
7183
|
+
return query;
|
|
7184
|
+
}
|
|
7185
|
+
validateNewFilter(query._query, parsedFilter);
|
|
7186
|
+
return new Query(query.firestore, query.converter, queryWithAddedFilter(query._query, parsedFilter));
|
|
7187
|
+
}
|
|
7188
|
+
_getQueryConstraints() {
|
|
7189
|
+
return this._queryConstraints;
|
|
7190
|
+
}
|
|
7191
|
+
_getOperator() {
|
|
7192
|
+
return this.type === 'and' ? "and" /* CompositeOperator.AND */ : "or" /* CompositeOperator.OR */;
|
|
7193
|
+
}
|
|
7194
|
+
}
|
|
7195
|
+
/**
|
|
7196
|
+
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical OR
|
|
7197
|
+
* of all the provided {@link QueryFilterConstraint}s.
|
|
7198
|
+
*
|
|
7199
|
+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
|
|
7200
|
+
* for OR operation. These must be created with calls to {@link where},
|
|
7201
|
+
* {@link or}, or {@link and}.
|
|
7202
|
+
* @returns The created {@link QueryCompositeFilterConstraint}.
|
|
7203
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
7204
|
+
*/
|
|
7205
|
+
function or(...queryConstraints) {
|
|
7206
|
+
// Only support QueryFilterConstraints
|
|
7207
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('or', queryConstraint));
|
|
7208
|
+
return QueryCompositeFilterConstraint._create("or" /* CompositeOperator.OR */, queryConstraints);
|
|
7209
|
+
}
|
|
7210
|
+
/**
|
|
7211
|
+
* Creates a {@link QueryCompositeFilterConstraint} that performs a logical AND
|
|
7212
|
+
* of all the provided {@link QueryFilterConstraint}s.
|
|
7213
|
+
*
|
|
7214
|
+
* @param queryConstraints - Optional. The {@link QueryFilterConstraint}s
|
|
7215
|
+
* for AND operation. These must be created with calls to {@link where},
|
|
7216
|
+
* {@link or}, or {@link and}.
|
|
7217
|
+
* @returns The created {@link QueryCompositeFilterConstraint}.
|
|
7218
|
+
* @internal TODO remove this internal tag with OR Query support in the server
|
|
7219
|
+
*/
|
|
7220
|
+
function and(...queryConstraints) {
|
|
7221
|
+
// Only support QueryFilterConstraints
|
|
7222
|
+
queryConstraints.forEach(queryConstraint => validateQueryFilterConstraint('and', queryConstraint));
|
|
7223
|
+
return QueryCompositeFilterConstraint._create("and" /* CompositeOperator.AND */, queryConstraints);
|
|
6928
7224
|
}
|
|
7225
|
+
/**
|
|
7226
|
+
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a
|
|
7227
|
+
* Firestore query. `QueryOrderByConstraint`s are created by invoking
|
|
7228
|
+
* {@link orderBy} and can then be passed to {@link query} to create a new query
|
|
7229
|
+
* instance that also contains this `QueryOrderByConstraint`.
|
|
7230
|
+
*
|
|
7231
|
+
* Note: Documents that do not contain the orderBy field will not be present in
|
|
7232
|
+
* the query result.
|
|
7233
|
+
*/
|
|
6929
7234
|
class QueryOrderByConstraint extends QueryConstraint {
|
|
7235
|
+
/**
|
|
7236
|
+
* @internal
|
|
7237
|
+
*/
|
|
6930
7238
|
constructor(_field, _direction) {
|
|
6931
7239
|
super();
|
|
6932
7240
|
this._field = _field;
|
|
6933
7241
|
this._direction = _direction;
|
|
7242
|
+
/** The type of this query constraint */
|
|
6934
7243
|
this.type = 'orderBy';
|
|
6935
7244
|
}
|
|
7245
|
+
static _create(_field, _direction) {
|
|
7246
|
+
return new QueryOrderByConstraint(_field, _direction);
|
|
7247
|
+
}
|
|
6936
7248
|
_apply(query) {
|
|
6937
7249
|
const orderBy = newQueryOrderBy(query._query, this._field, this._direction);
|
|
6938
7250
|
return new Query(query.firestore, query.converter, queryWithAddedOrderBy(query._query, orderBy));
|
|
6939
7251
|
}
|
|
6940
7252
|
}
|
|
6941
7253
|
/**
|
|
6942
|
-
* Creates a {@link
|
|
7254
|
+
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the
|
|
6943
7255
|
* specified field, optionally in descending order instead of ascending.
|
|
6944
7256
|
*
|
|
7257
|
+
* Note: Documents that do not contain the specified field will not be present
|
|
7258
|
+
* in the query result.
|
|
7259
|
+
*
|
|
6945
7260
|
* @param fieldPath - The field to sort by.
|
|
6946
7261
|
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If
|
|
6947
7262
|
* not specified, order will be ascending.
|
|
6948
|
-
* @returns The created {@link
|
|
7263
|
+
* @returns The created {@link QueryOrderByConstraint}.
|
|
6949
7264
|
*/
|
|
6950
7265
|
function orderBy(fieldPath, directionStr = 'asc') {
|
|
6951
7266
|
const direction = directionStr;
|
|
6952
7267
|
const path = fieldPathFromArgument('orderBy', fieldPath);
|
|
6953
|
-
return
|
|
7268
|
+
return QueryOrderByConstraint._create(path, direction);
|
|
6954
7269
|
}
|
|
7270
|
+
/**
|
|
7271
|
+
* A `QueryLimitConstraint` is used to limit the number of documents returned by
|
|
7272
|
+
* a Firestore query.
|
|
7273
|
+
* `QueryLimitConstraint`s are created by invoking {@link limit} or
|
|
7274
|
+
* {@link limitToLast} and can then be passed to {@link query} to create a new
|
|
7275
|
+
* query instance that also contains this `QueryLimitConstraint`.
|
|
7276
|
+
*/
|
|
6955
7277
|
class QueryLimitConstraint extends QueryConstraint {
|
|
6956
|
-
|
|
7278
|
+
/**
|
|
7279
|
+
* @internal
|
|
7280
|
+
*/
|
|
7281
|
+
constructor(
|
|
7282
|
+
/** The type of this query constraint */
|
|
7283
|
+
type, _limit, _limitType) {
|
|
6957
7284
|
super();
|
|
6958
7285
|
this.type = type;
|
|
6959
7286
|
this._limit = _limit;
|
|
6960
7287
|
this._limitType = _limitType;
|
|
6961
7288
|
}
|
|
7289
|
+
static _create(type, _limit, _limitType) {
|
|
7290
|
+
return new QueryLimitConstraint(type, _limit, _limitType);
|
|
7291
|
+
}
|
|
6962
7292
|
_apply(query) {
|
|
6963
7293
|
return new Query(query.firestore, query.converter, queryWithLimit(query._query, this._limit, this._limitType));
|
|
6964
7294
|
}
|
|
6965
7295
|
}
|
|
6966
7296
|
/**
|
|
6967
|
-
* Creates a {@link
|
|
7297
|
+
* Creates a {@link QueryLimitConstraint} that only returns the first matching
|
|
7298
|
+
* documents.
|
|
6968
7299
|
*
|
|
6969
7300
|
* @param limit - The maximum number of items to return.
|
|
6970
|
-
* @returns The created {@link
|
|
7301
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
6971
7302
|
*/
|
|
6972
7303
|
function limit(limit) {
|
|
6973
7304
|
validatePositiveNumber('limit', limit);
|
|
6974
|
-
return
|
|
7305
|
+
return QueryLimitConstraint._create('limit', limit, "F" /* LimitType.First */);
|
|
6975
7306
|
}
|
|
6976
7307
|
/**
|
|
6977
|
-
* Creates a {@link
|
|
7308
|
+
* Creates a {@link QueryLimitConstraint} that only returns the last matching
|
|
7309
|
+
* documents.
|
|
6978
7310
|
*
|
|
6979
7311
|
* You must specify at least one `orderBy` clause for `limitToLast` queries,
|
|
6980
7312
|
* otherwise an exception will be thrown during execution.
|
|
6981
7313
|
*
|
|
6982
7314
|
* @param limit - The maximum number of items to return.
|
|
6983
|
-
* @returns The created {@link
|
|
7315
|
+
* @returns The created {@link QueryLimitConstraint}.
|
|
6984
7316
|
*/
|
|
6985
7317
|
function limitToLast(limit) {
|
|
6986
7318
|
validatePositiveNumber('limitToLast', limit);
|
|
6987
|
-
return
|
|
7319
|
+
return QueryLimitConstraint._create('limitToLast', limit, "L" /* LimitType.Last */);
|
|
6988
7320
|
}
|
|
7321
|
+
/**
|
|
7322
|
+
* A `QueryStartAtConstraint` is used to exclude documents from the start of a
|
|
7323
|
+
* result set returned by a Firestore query.
|
|
7324
|
+
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or
|
|
7325
|
+
* {@link (startAfter:1)} and can then be passed to {@link query} to create a
|
|
7326
|
+
* new query instance that also contains this `QueryStartAtConstraint`.
|
|
7327
|
+
*/
|
|
6989
7328
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
6990
|
-
|
|
7329
|
+
/**
|
|
7330
|
+
* @internal
|
|
7331
|
+
*/
|
|
7332
|
+
constructor(
|
|
7333
|
+
/** The type of this query constraint */
|
|
7334
|
+
type, _docOrFields, _inclusive) {
|
|
6991
7335
|
super();
|
|
6992
7336
|
this.type = type;
|
|
6993
7337
|
this._docOrFields = _docOrFields;
|
|
6994
7338
|
this._inclusive = _inclusive;
|
|
6995
7339
|
}
|
|
7340
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
7341
|
+
return new QueryStartAtConstraint(type, _docOrFields, _inclusive);
|
|
7342
|
+
}
|
|
6996
7343
|
_apply(query) {
|
|
6997
7344
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
6998
7345
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
6999
7346
|
}
|
|
7000
7347
|
}
|
|
7001
7348
|
function startAt(...docOrFields) {
|
|
7002
|
-
return
|
|
7349
|
+
return QueryStartAtConstraint._create('startAt', docOrFields,
|
|
7003
7350
|
/*inclusive=*/ true);
|
|
7004
7351
|
}
|
|
7005
7352
|
function startAfter(...docOrFields) {
|
|
7006
|
-
return
|
|
7353
|
+
return QueryStartAtConstraint._create('startAfter', docOrFields,
|
|
7007
7354
|
/*inclusive=*/ false);
|
|
7008
7355
|
}
|
|
7356
|
+
/**
|
|
7357
|
+
* A `QueryEndAtConstraint` is used to exclude documents from the end of a
|
|
7358
|
+
* result set returned by a Firestore query.
|
|
7359
|
+
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or
|
|
7360
|
+
* {@link (endBefore:1)} and can then be passed to {@link query} to create a new
|
|
7361
|
+
* query instance that also contains this `QueryEndAtConstraint`.
|
|
7362
|
+
*/
|
|
7009
7363
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
7010
|
-
|
|
7364
|
+
/**
|
|
7365
|
+
* @internal
|
|
7366
|
+
*/
|
|
7367
|
+
constructor(
|
|
7368
|
+
/** The type of this query constraint */
|
|
7369
|
+
type, _docOrFields, _inclusive) {
|
|
7011
7370
|
super();
|
|
7012
7371
|
this.type = type;
|
|
7013
7372
|
this._docOrFields = _docOrFields;
|
|
7014
7373
|
this._inclusive = _inclusive;
|
|
7015
7374
|
}
|
|
7375
|
+
static _create(type, _docOrFields, _inclusive) {
|
|
7376
|
+
return new QueryEndAtConstraint(type, _docOrFields, _inclusive);
|
|
7377
|
+
}
|
|
7016
7378
|
_apply(query) {
|
|
7017
7379
|
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
7018
7380
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
7019
7381
|
}
|
|
7020
7382
|
}
|
|
7021
7383
|
function endBefore(...docOrFields) {
|
|
7022
|
-
return
|
|
7384
|
+
return QueryEndAtConstraint._create('endBefore', docOrFields,
|
|
7023
7385
|
/*inclusive=*/ false);
|
|
7024
7386
|
}
|
|
7025
7387
|
function endAt(...docOrFields) {
|
|
7026
|
-
return
|
|
7388
|
+
return QueryEndAtConstraint._create('endAt', docOrFields,
|
|
7389
|
+
/*inclusive=*/ true);
|
|
7027
7390
|
}
|
|
7028
7391
|
/** Helper function to create a bound from a document or fields */
|
|
7029
7392
|
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
@@ -7039,10 +7402,10 @@ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive)
|
|
|
7039
7402
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
7040
7403
|
let fieldValue;
|
|
7041
7404
|
if (fieldPath.isKeyField()) {
|
|
7042
|
-
if (op === "array-contains" /* ARRAY_CONTAINS */ || op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
7405
|
+
if (op === "array-contains" /* Operator.ARRAY_CONTAINS */ || op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
7043
7406
|
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid Query. You can't perform '${op}' queries on documentId().`);
|
|
7044
7407
|
}
|
|
7045
|
-
else if (op === "in" /* IN */ || op === "not-in" /* NOT_IN */) {
|
|
7408
|
+
else if (op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */) {
|
|
7046
7409
|
validateDisjunctiveFilterElements(value, op);
|
|
7047
7410
|
const referenceList = [];
|
|
7048
7411
|
for (const arrayValue of value) {
|
|
@@ -7055,16 +7418,15 @@ function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op
|
|
|
7055
7418
|
}
|
|
7056
7419
|
}
|
|
7057
7420
|
else {
|
|
7058
|
-
if (op === "in" /* IN */ ||
|
|
7059
|
-
op === "not-in" /* NOT_IN */ ||
|
|
7060
|
-
op === "array-contains-any" /* ARRAY_CONTAINS_ANY */) {
|
|
7421
|
+
if (op === "in" /* Operator.IN */ ||
|
|
7422
|
+
op === "not-in" /* Operator.NOT_IN */ ||
|
|
7423
|
+
op === "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */) {
|
|
7061
7424
|
validateDisjunctiveFilterElements(value, op);
|
|
7062
7425
|
}
|
|
7063
7426
|
fieldValue = parseQueryValue(dataReader, methodName, value,
|
|
7064
|
-
/* allowArrays= */ op === "in" /* IN */ || op === "not-in" /* NOT_IN */);
|
|
7427
|
+
/* allowArrays= */ op === "in" /* Operator.IN */ || op === "not-in" /* Operator.NOT_IN */);
|
|
7065
7428
|
}
|
|
7066
7429
|
const filter = FieldFilter.create(fieldPath, op, fieldValue);
|
|
7067
|
-
validateNewFilter(query, filter);
|
|
7068
7430
|
return filter;
|
|
7069
7431
|
}
|
|
7070
7432
|
function newQueryOrderBy(query, fieldPath, direction) {
|
|
@@ -7234,62 +7596,84 @@ function validateDisjunctiveFilterElements(value, operator) {
|
|
|
7234
7596
|
*/
|
|
7235
7597
|
function conflictingOps(op) {
|
|
7236
7598
|
switch (op) {
|
|
7237
|
-
case "!=" /* NOT_EQUAL */:
|
|
7238
|
-
return ["!=" /* NOT_EQUAL */, "not-in" /* NOT_IN */];
|
|
7239
|
-
case "array-contains" /* ARRAY_CONTAINS */:
|
|
7599
|
+
case "!=" /* Operator.NOT_EQUAL */:
|
|
7600
|
+
return ["!=" /* Operator.NOT_EQUAL */, "not-in" /* Operator.NOT_IN */];
|
|
7601
|
+
case "array-contains" /* Operator.ARRAY_CONTAINS */:
|
|
7240
7602
|
return [
|
|
7241
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
7242
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
7243
|
-
"not-in" /* NOT_IN */
|
|
7603
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
7604
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
7605
|
+
"not-in" /* Operator.NOT_IN */
|
|
7244
7606
|
];
|
|
7245
|
-
case "in" /* IN */:
|
|
7246
|
-
return ["array-contains-any" /* ARRAY_CONTAINS_ANY */, "in" /* IN */, "not-in" /* NOT_IN */];
|
|
7247
|
-
case "array-contains-any" /* ARRAY_CONTAINS_ANY */:
|
|
7607
|
+
case "in" /* Operator.IN */:
|
|
7608
|
+
return ["array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */, "in" /* Operator.IN */, "not-in" /* Operator.NOT_IN */];
|
|
7609
|
+
case "array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */:
|
|
7248
7610
|
return [
|
|
7249
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
7250
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
7251
|
-
"in" /* IN */,
|
|
7252
|
-
"not-in" /* NOT_IN */
|
|
7611
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
7612
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
7613
|
+
"in" /* Operator.IN */,
|
|
7614
|
+
"not-in" /* Operator.NOT_IN */
|
|
7253
7615
|
];
|
|
7254
|
-
case "not-in" /* NOT_IN */:
|
|
7616
|
+
case "not-in" /* Operator.NOT_IN */:
|
|
7255
7617
|
return [
|
|
7256
|
-
"array-contains" /* ARRAY_CONTAINS */,
|
|
7257
|
-
"array-contains-any" /* ARRAY_CONTAINS_ANY */,
|
|
7258
|
-
"in" /* IN */,
|
|
7259
|
-
"not-in" /* NOT_IN */,
|
|
7260
|
-
"!=" /* NOT_EQUAL */
|
|
7618
|
+
"array-contains" /* Operator.ARRAY_CONTAINS */,
|
|
7619
|
+
"array-contains-any" /* Operator.ARRAY_CONTAINS_ANY */,
|
|
7620
|
+
"in" /* Operator.IN */,
|
|
7621
|
+
"not-in" /* Operator.NOT_IN */,
|
|
7622
|
+
"!=" /* Operator.NOT_EQUAL */
|
|
7261
7623
|
];
|
|
7262
7624
|
default:
|
|
7263
7625
|
return [];
|
|
7264
7626
|
}
|
|
7265
7627
|
}
|
|
7266
|
-
function
|
|
7267
|
-
if (
|
|
7268
|
-
const
|
|
7269
|
-
|
|
7628
|
+
function validateNewFieldFilter(query, fieldFilter) {
|
|
7629
|
+
if (fieldFilter.isInequality()) {
|
|
7630
|
+
const existingInequality = getInequalityFilterField(query);
|
|
7631
|
+
const newInequality = fieldFilter.field;
|
|
7632
|
+
if (existingInequality !== null &&
|
|
7633
|
+
!existingInequality.isEqual(newInequality)) {
|
|
7270
7634
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. All where filters with an inequality' +
|
|
7271
7635
|
' (<, <=, !=, not-in, >, or >=) must be on the same field. But you have' +
|
|
7272
|
-
` inequality filters on '${
|
|
7273
|
-
` and '${
|
|
7636
|
+
` inequality filters on '${existingInequality.toString()}'` +
|
|
7637
|
+
` and '${newInequality.toString()}'`);
|
|
7274
7638
|
}
|
|
7275
7639
|
const firstOrderByField = getFirstOrderByField(query);
|
|
7276
7640
|
if (firstOrderByField !== null) {
|
|
7277
|
-
validateOrderByAndInequalityMatch(query,
|
|
7641
|
+
validateOrderByAndInequalityMatch(query, newInequality, firstOrderByField);
|
|
7278
7642
|
}
|
|
7279
7643
|
}
|
|
7280
|
-
const conflictingOp =
|
|
7644
|
+
const conflictingOp = findOpInsideFilters(query.filters, conflictingOps(fieldFilter.op));
|
|
7281
7645
|
if (conflictingOp !== null) {
|
|
7282
7646
|
// Special case when it's a duplicate op to give a slightly clearer error message.
|
|
7283
|
-
if (conflictingOp ===
|
|
7647
|
+
if (conflictingOp === fieldFilter.op) {
|
|
7284
7648
|
throw new FirestoreError(Code.INVALID_ARGUMENT, 'Invalid query. You cannot use more than one ' +
|
|
7285
|
-
`'${
|
|
7649
|
+
`'${fieldFilter.op.toString()}' filter.`);
|
|
7286
7650
|
}
|
|
7287
7651
|
else {
|
|
7288
|
-
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${
|
|
7652
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Invalid query. You cannot use '${fieldFilter.op.toString()}' filters ` +
|
|
7289
7653
|
`with '${conflictingOp.toString()}' filters.`);
|
|
7290
7654
|
}
|
|
7291
7655
|
}
|
|
7292
7656
|
}
|
|
7657
|
+
function validateNewFilter(query, filter) {
|
|
7658
|
+
let testQuery = query;
|
|
7659
|
+
const subFilters = filter.getFlattenedFilters();
|
|
7660
|
+
for (const subFilter of subFilters) {
|
|
7661
|
+
validateNewFieldFilter(testQuery, subFilter);
|
|
7662
|
+
testQuery = queryWithAddedFilter(testQuery, subFilter);
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
// Checks if any of the provided filter operators are included in the given list of filters and
|
|
7666
|
+
// returns the first one that is, or null if none are.
|
|
7667
|
+
function findOpInsideFilters(filters, operators) {
|
|
7668
|
+
for (const filter of filters) {
|
|
7669
|
+
for (const fieldFilter of filter.getFlattenedFilters()) {
|
|
7670
|
+
if (operators.indexOf(fieldFilter.op) >= 0) {
|
|
7671
|
+
return fieldFilter.op;
|
|
7672
|
+
}
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
return null;
|
|
7676
|
+
}
|
|
7293
7677
|
function validateNewOrderBy(query, orderBy) {
|
|
7294
7678
|
if (getFirstOrderByField(query) === null) {
|
|
7295
7679
|
// This is the first order by. It must match any inequality.
|
|
@@ -7307,6 +7691,24 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
7307
7691
|
`as your first argument to orderBy(), but your first orderBy() ` +
|
|
7308
7692
|
`is on field '${orderBy.toString()}' instead.`);
|
|
7309
7693
|
}
|
|
7694
|
+
}
|
|
7695
|
+
function validateQueryFilterConstraint(functionName, queryConstraint) {
|
|
7696
|
+
if (!(queryConstraint instanceof QueryFieldFilterConstraint) &&
|
|
7697
|
+
!(queryConstraint instanceof QueryCompositeFilterConstraint)) {
|
|
7698
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, `Function ${functionName}() requires AppliableConstraints created with a call to 'where(...)', 'or(...)', or 'and(...)'.`);
|
|
7699
|
+
}
|
|
7700
|
+
}
|
|
7701
|
+
function validateQueryConstraintArray(queryConstraint) {
|
|
7702
|
+
const compositeFilterCount = queryConstraint.filter(filter => filter instanceof QueryCompositeFilterConstraint).length;
|
|
7703
|
+
const fieldFilterCount = queryConstraint.filter(filter => filter instanceof QueryFieldFilterConstraint).length;
|
|
7704
|
+
if (compositeFilterCount > 1 ||
|
|
7705
|
+
(compositeFilterCount > 0 && fieldFilterCount > 0)) {
|
|
7706
|
+
throw new FirestoreError(Code.INVALID_ARGUMENT, 'InvalidQuery. When using composite filters, you cannot use ' +
|
|
7707
|
+
'more than one filter at the top level. Consider nesting the multiple ' +
|
|
7708
|
+
'filters within an `and(...)` statement. For example: ' +
|
|
7709
|
+
'change `query(query, where(...), or(...))` to ' +
|
|
7710
|
+
'`query(query, and(where(...), or(...)))`.');
|
|
7711
|
+
}
|
|
7310
7712
|
}
|
|
7311
7713
|
|
|
7312
7714
|
/**
|
|
@@ -7334,27 +7736,27 @@ function validateOrderByAndInequalityMatch(baseQuery, inequality, orderBy) {
|
|
|
7334
7736
|
class AbstractUserDataWriter {
|
|
7335
7737
|
convertValue(value, serverTimestampBehavior = 'none') {
|
|
7336
7738
|
switch (typeOrder(value)) {
|
|
7337
|
-
case 0 /* NullValue */:
|
|
7739
|
+
case 0 /* TypeOrder.NullValue */:
|
|
7338
7740
|
return null;
|
|
7339
|
-
case 1 /* BooleanValue */:
|
|
7741
|
+
case 1 /* TypeOrder.BooleanValue */:
|
|
7340
7742
|
return value.booleanValue;
|
|
7341
|
-
case 2 /* NumberValue */:
|
|
7743
|
+
case 2 /* TypeOrder.NumberValue */:
|
|
7342
7744
|
return normalizeNumber(value.integerValue || value.doubleValue);
|
|
7343
|
-
case 3 /* TimestampValue */:
|
|
7745
|
+
case 3 /* TypeOrder.TimestampValue */:
|
|
7344
7746
|
return this.convertTimestamp(value.timestampValue);
|
|
7345
|
-
case 4 /* ServerTimestampValue */:
|
|
7747
|
+
case 4 /* TypeOrder.ServerTimestampValue */:
|
|
7346
7748
|
return this.convertServerTimestamp(value, serverTimestampBehavior);
|
|
7347
|
-
case 5 /* StringValue */:
|
|
7749
|
+
case 5 /* TypeOrder.StringValue */:
|
|
7348
7750
|
return value.stringValue;
|
|
7349
|
-
case 6 /* BlobValue */:
|
|
7751
|
+
case 6 /* TypeOrder.BlobValue */:
|
|
7350
7752
|
return this.convertBytes(normalizeByteString(value.bytesValue));
|
|
7351
|
-
case 7 /* RefValue */:
|
|
7753
|
+
case 7 /* TypeOrder.RefValue */:
|
|
7352
7754
|
return this.convertReference(value.referenceValue);
|
|
7353
|
-
case 8 /* GeoPointValue */:
|
|
7755
|
+
case 8 /* TypeOrder.GeoPointValue */:
|
|
7354
7756
|
return this.convertGeoPoint(value.geoPointValue);
|
|
7355
|
-
case 9 /* ArrayValue */:
|
|
7757
|
+
case 9 /* TypeOrder.ArrayValue */:
|
|
7356
7758
|
return this.convertArray(value.arrayValue, serverTimestampBehavior);
|
|
7357
|
-
case 10 /* ObjectValue */:
|
|
7759
|
+
case 10 /* TypeOrder.ObjectValue */:
|
|
7358
7760
|
return this.convertObject(value.mapValue, serverTimestampBehavior);
|
|
7359
7761
|
default:
|
|
7360
7762
|
throw fail();
|
|
@@ -7507,7 +7909,7 @@ function getDocs(query) {
|
|
|
7507
7909
|
const userDataWriter = new LiteUserDataWriter(query.firestore);
|
|
7508
7910
|
return invokeRunQueryRpc(datastore, query._query).then(result => {
|
|
7509
7911
|
const docs = result.map(doc => new QueryDocumentSnapshot(query.firestore, userDataWriter, doc.key, doc, query.converter));
|
|
7510
|
-
if (query._query.limitType === "L" /* Last */) {
|
|
7912
|
+
if (query._query.limitType === "L" /* LimitType.Last */) {
|
|
7511
7913
|
// Limit to last queries reverse the orderBy constraint that was
|
|
7512
7914
|
// specified by the user. As such, we need to reverse the order of the
|
|
7513
7915
|
// results to return the documents in the expected order.
|
|
@@ -8070,7 +8472,7 @@ class TransactionRunner {
|
|
|
8070
8472
|
this.updateFunction = updateFunction;
|
|
8071
8473
|
this.deferred = deferred;
|
|
8072
8474
|
this.attemptsRemaining = options.maxAttempts;
|
|
8073
|
-
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TransactionRetry */);
|
|
8475
|
+
this.backoff = new ExponentialBackoff(this.asyncQueue, "transaction_retry" /* TimerId.TransactionRetry */);
|
|
8074
8476
|
}
|
|
8075
8477
|
/** Runs the transaction and sets the result on deferred. */
|
|
8076
8478
|
run() {
|
|
@@ -8294,7 +8696,7 @@ class AsyncQueueImpl {
|
|
|
8294
8696
|
// List of TimerIds to fast-forward delays for.
|
|
8295
8697
|
this.timerIdsToSkip = [];
|
|
8296
8698
|
// Backoff timer used to schedule retries for retryable operations
|
|
8297
|
-
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* AsyncQueueRetry */);
|
|
8699
|
+
this.backoff = new ExponentialBackoff(this, "async_queue_retry" /* TimerId.AsyncQueueRetry */);
|
|
8298
8700
|
// Visibility handler that triggers an immediate retry of all retryable
|
|
8299
8701
|
// operations. Meant to speed up recovery when we regain file system access
|
|
8300
8702
|
// after page comes into foreground.
|
|
@@ -8464,7 +8866,7 @@ class AsyncQueueImpl {
|
|
|
8464
8866
|
this.delayedOperations.sort((a, b) => a.targetTimeMs - b.targetTimeMs);
|
|
8465
8867
|
for (const op of this.delayedOperations) {
|
|
8466
8868
|
op.skipDelay();
|
|
8467
|
-
if (lastTimerId !== "all" /* All */ && op.timerId === lastTimerId) {
|
|
8869
|
+
if (lastTimerId !== "all" /* TimerId.All */ && op.timerId === lastTimerId) {
|
|
8468
8870
|
break;
|
|
8469
8871
|
}
|
|
8470
8872
|
}
|
|
@@ -8636,5 +9038,5 @@ function runTransaction(firestore, updateFunction, options) {
|
|
|
8636
9038
|
*/
|
|
8637
9039
|
registerFirestore();
|
|
8638
9040
|
|
|
8639
|
-
export { AggregateField, AggregateQuerySnapshot, Bytes, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, Query, QueryConstraint, QueryDocumentSnapshot, QuerySnapshot, Timestamp, Transaction, WriteBatch, addDoc, aggregateQuerySnapshotEqual, arrayRemove, arrayUnion, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, doc, documentId, endAt, endBefore, getCount, getDoc, getDocs, getFirestore, increment, initializeFirestore, limit, limitToLast, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, where, writeBatch };
|
|
9041
|
+
export { AggregateField, AggregateQuerySnapshot, Bytes, CollectionReference, DocumentReference, DocumentSnapshot, FieldPath, FieldValue, Firestore, FirestoreError, GeoPoint, Query, QueryCompositeFilterConstraint, QueryConstraint, QueryDocumentSnapshot, QueryEndAtConstraint, QueryFieldFilterConstraint, QueryLimitConstraint, QueryOrderByConstraint, QuerySnapshot, QueryStartAtConstraint, Timestamp, Transaction, WriteBatch, addDoc, aggregateQuerySnapshotEqual, and, arrayRemove, arrayUnion, collection, collectionGroup, connectFirestoreEmulator, deleteDoc, deleteField, doc, documentId, endAt, endBefore, getCount, getDoc, getDocs, getFirestore, increment, initializeFirestore, limit, limitToLast, or, orderBy, query, queryEqual, refEqual, runTransaction, serverTimestamp, setDoc, setLogLevel, snapshotEqual, startAfter, startAt, terminate, updateDoc, where, writeBatch };
|
|
8640
9042
|
//# sourceMappingURL=index.node.mjs.map
|