field-value.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. "use strict";
  2. /*!
  3. * Copyright 2018 Google Inc. All Rights Reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. Object.defineProperty(exports, "__esModule", { value: true });
  18. exports.DeleteTransform = exports.FieldTransform = exports.FieldValue = exports.VectorValue = void 0;
  19. const deepEqual = require("fast-deep-equal");
  20. const serializer_1 = require("./serializer");
  21. const util_1 = require("./util");
  22. const validate_1 = require("./validate");
  23. /**
  24. * Represent a vector type in Firestore documents.
  25. * Create an instance with {@link FieldValue.vector}.
  26. *
  27. * @class VectorValue
  28. */
  29. class VectorValue {
  30. /**
  31. * @private
  32. * @internal
  33. */
  34. constructor(values) {
  35. // Making a copy of the parameter.
  36. this._values = (values || []).map(n => n);
  37. }
  38. /**
  39. * Returns a copy of the raw number array form of the vector.
  40. */
  41. toArray() {
  42. return this._values.map(n => n);
  43. }
  44. /**
  45. * @private
  46. * @internal
  47. */
  48. _toProto(serializer) {
  49. return serializer.encodeVector(this._values);
  50. }
  51. /**
  52. * @private
  53. * @internal
  54. */
  55. static _fromProto(valueArray) {
  56. var _a, _b;
  57. const values = (_b = (_a = valueArray.arrayValue) === null || _a === void 0 ? void 0 : _a.values) === null || _b === void 0 ? void 0 : _b.map(v => {
  58. return v.doubleValue;
  59. });
  60. return new VectorValue(values);
  61. }
  62. /**
  63. * Returns `true` if the two VectorValue has the same raw number arrays, returns `false` otherwise.
  64. */
  65. isEqual(other) {
  66. return (0, util_1.isPrimitiveArrayEqual)(this._values, other._values);
  67. }
  68. }
  69. exports.VectorValue = VectorValue;
  70. /**
  71. * Sentinel values that can be used when writing documents with set(), create()
  72. * or update().
  73. *
  74. * @class FieldValue
  75. */
  76. class FieldValue {
  77. /** @private */
  78. constructor() { }
  79. /**
  80. * Creates a new `VectorValue` constructed with a copy of the given array of numbers.
  81. *
  82. * @param values - Create a `VectorValue` instance with a copy of this array of numbers.
  83. *
  84. * @returns A new `VectorValue` constructed with a copy of the given array of numbers.
  85. */
  86. static vector(values) {
  87. return new VectorValue(values);
  88. }
  89. /**
  90. * Returns a sentinel for use with update() or set() with {merge:true} to mark
  91. * a field for deletion.
  92. *
  93. * @returns {FieldValue} The sentinel value to use in your objects.
  94. *
  95. * @example
  96. * ```
  97. * let documentRef = firestore.doc('col/doc');
  98. * let data = { a: 'b', c: 'd' };
  99. *
  100. * documentRef.set(data).then(() => {
  101. * return documentRef.update({a: Firestore.FieldValue.delete()});
  102. * }).then(() => {
  103. * // Document now only contains { c: 'd' }
  104. * });
  105. * ```
  106. */
  107. static delete() {
  108. return DeleteTransform.DELETE_SENTINEL;
  109. }
  110. /**
  111. * Returns a sentinel used with set(), create() or update() to include a
  112. * server-generated timestamp in the written data.
  113. *
  114. * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
  115. * create() or update().
  116. *
  117. * @example
  118. * ```
  119. * let documentRef = firestore.doc('col/doc');
  120. *
  121. * documentRef.set({
  122. * time: Firestore.FieldValue.serverTimestamp()
  123. * }).then(() => {
  124. * return documentRef.get();
  125. * }).then(doc => {
  126. * console.log(`Server time set to ${doc.get('time')}`);
  127. * });
  128. * ```
  129. */
  130. static serverTimestamp() {
  131. return ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL;
  132. }
  133. /**
  134. * Returns a special value that can be used with set(), create() or update()
  135. * that tells the server to increment the the field's current value by the
  136. * given value.
  137. *
  138. * If either current field value or the operand uses floating point
  139. * precision, both values will be interpreted as floating point numbers and
  140. * all arithmetic will follow IEEE 754 semantics. Otherwise, integer
  141. * precision is kept and the result is capped between -2^63 and 2^63-1.
  142. *
  143. * If the current field value is not of type 'number', or if the field does
  144. * not yet exist, the transformation will set the field to the given value.
  145. *
  146. * @param {number} n The value to increment by.
  147. * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
  148. * create() or update().
  149. *
  150. * @example
  151. * ```
  152. * let documentRef = firestore.doc('col/doc');
  153. *
  154. * documentRef.update(
  155. * 'counter', Firestore.FieldValue.increment(1)
  156. * ).then(() => {
  157. * return documentRef.get();
  158. * }).then(doc => {
  159. * // doc.get('counter') was incremented
  160. * });
  161. * ```
  162. */
  163. static increment(n) {
  164. // eslint-disable-next-line prefer-rest-params
  165. (0, validate_1.validateMinNumberOfArguments)('FieldValue.increment', arguments, 1);
  166. return new NumericIncrementTransform(n);
  167. }
  168. /**
  169. * Returns a special value that can be used with set(), create() or update()
  170. * that tells the server to union the given elements with any array value that
  171. * already exists on the server. Each specified element that doesn't already
  172. * exist in the array will be added to the end. If the field being modified is
  173. * not already an array it will be overwritten with an array containing
  174. * exactly the specified elements.
  175. *
  176. * @param {...*} elements The elements to union into the array.
  177. * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
  178. * create() or update().
  179. *
  180. * @example
  181. * ```
  182. * let documentRef = firestore.doc('col/doc');
  183. *
  184. * documentRef.update(
  185. * 'array', Firestore.FieldValue.arrayUnion('foo')
  186. * ).then(() => {
  187. * return documentRef.get();
  188. * }).then(doc => {
  189. * // doc.get('array') contains field 'foo'
  190. * });
  191. * ```
  192. */
  193. static arrayUnion(...elements) {
  194. (0, validate_1.validateMinNumberOfArguments)('FieldValue.arrayUnion', elements, 1);
  195. return new ArrayUnionTransform(elements);
  196. }
  197. /**
  198. * Returns a special value that can be used with set(), create() or update()
  199. * that tells the server to remove the given elements from any array value
  200. * that already exists on the server. All instances of each element specified
  201. * will be removed from the array. If the field being modified is not already
  202. * an array it will be overwritten with an empty array.
  203. *
  204. * @param {...*} elements The elements to remove from the array.
  205. * @return {FieldValue} The FieldValue sentinel for use in a call to set(),
  206. * create() or update().
  207. *
  208. * @example
  209. * ```
  210. * let documentRef = firestore.doc('col/doc');
  211. *
  212. * documentRef.update(
  213. * 'array', Firestore.FieldValue.arrayRemove('foo')
  214. * ).then(() => {
  215. * return documentRef.get();
  216. * }).then(doc => {
  217. * // doc.get('array') no longer contains field 'foo'
  218. * });
  219. * ```
  220. */
  221. static arrayRemove(...elements) {
  222. (0, validate_1.validateMinNumberOfArguments)('FieldValue.arrayRemove', elements, 1);
  223. return new ArrayRemoveTransform(elements);
  224. }
  225. /**
  226. * Returns true if this `FieldValue` is equal to the provided value.
  227. *
  228. * @param {*} other The value to compare against.
  229. * @return {boolean} true if this `FieldValue` is equal to the provided value.
  230. *
  231. * @example
  232. * ```
  233. * let fieldValues = [
  234. * Firestore.FieldValue.increment(-1.0),
  235. * Firestore.FieldValue.increment(-1),
  236. * Firestore.FieldValue.increment(-0.0),
  237. * Firestore.FieldValue.increment(-0),
  238. * Firestore.FieldValue.increment(0),
  239. * Firestore.FieldValue.increment(0.0),
  240. * Firestore.FieldValue.increment(1),
  241. * Firestore.FieldValue.increment(1.0)
  242. * ];
  243. *
  244. * let equal = 0;
  245. * for (let i = 0; i < fieldValues.length; ++i) {
  246. * for (let j = i + 1; j < fieldValues.length; ++j) {
  247. * if (fieldValues[i].isEqual(fieldValues[j])) {
  248. * ++equal;
  249. * }
  250. * }
  251. * }
  252. * console.log(`Found ${equal} equalities.`);
  253. * ```
  254. */
  255. isEqual(other) {
  256. return this === other;
  257. }
  258. }
  259. exports.FieldValue = FieldValue;
  260. /**
  261. * An internal interface shared by all field transforms.
  262. *
  263. * A 'FieldTransform` subclass should implement '.includeInDocumentMask',
  264. * '.includeInDocumentTransform' and 'toProto' (if '.includeInDocumentTransform'
  265. * is 'true').
  266. *
  267. * @private
  268. * @internal
  269. * @abstract
  270. */
  271. class FieldTransform extends FieldValue {
  272. }
  273. exports.FieldTransform = FieldTransform;
  274. /**
  275. * A transform that deletes a field from a Firestore document.
  276. *
  277. * @private
  278. * @internal
  279. */
  280. class DeleteTransform extends FieldTransform {
  281. constructor() {
  282. super();
  283. }
  284. /**
  285. * Deletes are included in document masks.
  286. * @private
  287. * @internal
  288. */
  289. get includeInDocumentMask() {
  290. return true;
  291. }
  292. /**
  293. * Deletes are are omitted from document transforms.
  294. * @private
  295. * @internal
  296. */
  297. get includeInDocumentTransform() {
  298. return false;
  299. }
  300. get methodName() {
  301. return 'FieldValue.delete';
  302. }
  303. validate() { }
  304. toProto() {
  305. throw new Error('FieldValue.delete() should not be included in a FieldTransform');
  306. }
  307. }
  308. exports.DeleteTransform = DeleteTransform;
  309. /**
  310. * Sentinel value for a field delete.
  311. * @private
  312. * @internal
  313. */
  314. DeleteTransform.DELETE_SENTINEL = new DeleteTransform();
  315. /**
  316. * A transform that sets a field to the Firestore server time.
  317. *
  318. * @private
  319. * @internal
  320. */
  321. class ServerTimestampTransform extends FieldTransform {
  322. constructor() {
  323. super();
  324. }
  325. /**
  326. * Server timestamps are omitted from document masks.
  327. *
  328. * @private
  329. * @internal
  330. */
  331. get includeInDocumentMask() {
  332. return false;
  333. }
  334. /**
  335. * Server timestamps are included in document transforms.
  336. *
  337. * @private
  338. * @internal
  339. */
  340. get includeInDocumentTransform() {
  341. return true;
  342. }
  343. get methodName() {
  344. return 'FieldValue.serverTimestamp';
  345. }
  346. validate() { }
  347. toProto(serializer, fieldPath) {
  348. return {
  349. fieldPath: fieldPath.formattedName,
  350. setToServerValue: 'REQUEST_TIME',
  351. };
  352. }
  353. }
  354. /**
  355. * Sentinel value for a server timestamp.
  356. *
  357. * @private
  358. * @internal
  359. */
  360. ServerTimestampTransform.SERVER_TIMESTAMP_SENTINEL = new ServerTimestampTransform();
  361. /**
  362. * Increments a field value on the backend.
  363. *
  364. * @private
  365. * @internal
  366. */
  367. class NumericIncrementTransform extends FieldTransform {
  368. constructor(operand) {
  369. super();
  370. this.operand = operand;
  371. }
  372. /**
  373. * Numeric transforms are omitted from document masks.
  374. *
  375. * @private
  376. * @internal
  377. */
  378. get includeInDocumentMask() {
  379. return false;
  380. }
  381. /**
  382. * Numeric transforms are included in document transforms.
  383. *
  384. * @private
  385. * @internal
  386. */
  387. get includeInDocumentTransform() {
  388. return true;
  389. }
  390. get methodName() {
  391. return 'FieldValue.increment';
  392. }
  393. validate() {
  394. (0, validate_1.validateNumber)('FieldValue.increment()', this.operand);
  395. }
  396. toProto(serializer, fieldPath) {
  397. const encodedOperand = serializer.encodeValue(this.operand);
  398. return { fieldPath: fieldPath.formattedName, increment: encodedOperand };
  399. }
  400. isEqual(other) {
  401. return (this === other ||
  402. (other instanceof NumericIncrementTransform &&
  403. this.operand === other.operand));
  404. }
  405. }
  406. /**
  407. * Transforms an array value via a union operation.
  408. *
  409. * @private
  410. * @internal
  411. */
  412. class ArrayUnionTransform extends FieldTransform {
  413. constructor(elements) {
  414. super();
  415. this.elements = elements;
  416. }
  417. /**
  418. * Array transforms are omitted from document masks.
  419. * @private
  420. * @internal
  421. */
  422. get includeInDocumentMask() {
  423. return false;
  424. }
  425. /**
  426. * Array transforms are included in document transforms.
  427. * @private
  428. * @internal
  429. */
  430. get includeInDocumentTransform() {
  431. return true;
  432. }
  433. get methodName() {
  434. return 'FieldValue.arrayUnion';
  435. }
  436. validate(allowUndefined) {
  437. for (let i = 0; i < this.elements.length; ++i) {
  438. validateArrayElement(i, this.elements[i], allowUndefined);
  439. }
  440. }
  441. toProto(serializer, fieldPath) {
  442. const encodedElements = serializer.encodeValue(this.elements).arrayValue;
  443. return {
  444. fieldPath: fieldPath.formattedName,
  445. appendMissingElements: encodedElements,
  446. };
  447. }
  448. isEqual(other) {
  449. return (this === other ||
  450. (other instanceof ArrayUnionTransform &&
  451. deepEqual(this.elements, other.elements)));
  452. }
  453. }
  454. /**
  455. * Transforms an array value via a remove operation.
  456. *
  457. * @private
  458. * @internal
  459. */
  460. class ArrayRemoveTransform extends FieldTransform {
  461. constructor(elements) {
  462. super();
  463. this.elements = elements;
  464. }
  465. /**
  466. * Array transforms are omitted from document masks.
  467. * @private
  468. * @internal
  469. */
  470. get includeInDocumentMask() {
  471. return false;
  472. }
  473. /**
  474. * Array transforms are included in document transforms.
  475. * @private
  476. * @internal
  477. */
  478. get includeInDocumentTransform() {
  479. return true;
  480. }
  481. get methodName() {
  482. return 'FieldValue.arrayRemove';
  483. }
  484. validate(allowUndefined) {
  485. for (let i = 0; i < this.elements.length; ++i) {
  486. validateArrayElement(i, this.elements[i], allowUndefined);
  487. }
  488. }
  489. toProto(serializer, fieldPath) {
  490. const encodedElements = serializer.encodeValue(this.elements).arrayValue;
  491. return {
  492. fieldPath: fieldPath.formattedName,
  493. removeAllFromArray: encodedElements,
  494. };
  495. }
  496. isEqual(other) {
  497. return (this === other ||
  498. (other instanceof ArrayRemoveTransform &&
  499. deepEqual(this.elements, other.elements)));
  500. }
  501. }
  502. /**
  503. * Validates that `value` can be used as an element inside of an array. Certain
  504. * field values (such as ServerTimestamps) are rejected. Nested arrays are also
  505. * rejected.
  506. *
  507. * @private
  508. * @internal
  509. * @param arg The argument name or argument index (for varargs methods).
  510. * @param value The value to validate.
  511. * @param allowUndefined Whether to allow nested properties that are `undefined`.
  512. */
  513. function validateArrayElement(arg, value, allowUndefined) {
  514. if (Array.isArray(value)) {
  515. throw new Error(`${(0, validate_1.invalidArgumentMessage)(arg, 'array element')} Nested arrays are not supported.`);
  516. }
  517. (0, serializer_1.validateUserInput)(arg, value, 'array element',
  518. /*path=*/ { allowDeletes: 'none', allowTransforms: false, allowUndefined },
  519. /*path=*/ undefined,
  520. /*level=*/ 0,
  521. /*inArray=*/ true);
  522. }
  523. //# sourceMappingURL=field-value.js.map