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