Skip to content

Commit 82d7a9b

Browse files
MarkDuckworthbrettwillisgcf-owl-bot[bot]dlarocque
authored andcommitted
feat(firestore): Added FieldValue.minimum() and FieldValue.maximum() (googleapis#8151)
Co-authored-by: Brett Willis <brettjohnwillis@gmail.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com> Co-authored-by: Daniel La Rocque <dlarocque@google.com>
1 parent d261cd0 commit 82d7a9b

5 files changed

Lines changed: 501 additions & 10 deletions

File tree

handwritten/firestore/dev/src/field-value.ts

Lines changed: 158 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ export class FieldValue implements firestore.FieldValue {
152152

153153
/**
154154
* Returns a special value that can be used with set(), create() or update()
155-
* that tells the server to increment the the field's current value by the
155+
* that tells the server to increment the field's current value by the
156156
* given value.
157157
*
158158
* If either current field value or the operand uses floating point
@@ -186,6 +186,76 @@ export class FieldValue implements firestore.FieldValue {
186186
return new NumericIncrementTransform(n);
187187
}
188188

189+
/**
190+
* Returns a special value that can be used with `set()`, `create()` or `update()`
191+
* that tells the server to set the field to the numeric minimum of the
192+
* field's current and the given value.
193+
*
194+
* If the current field value is not of type 'number', or if the field does
195+
* not yet exist, the transformation will set the field to the given value.
196+
*
197+
* If the existing value and the operand are equivalent, then the field does
198+
* not change. For example, `0`, `0.0`, and `-0.0` are all equivalent. If the
199+
* operand is `NaN` then the result is always `NaN`.
200+
*
201+
* @param {number} n The value to compare to the exiting field value.
202+
* @return {FieldValue} The FieldValue for use in a call to `set()`, `create()` or
203+
* `update()`.
204+
*
205+
* @example
206+
* ```typescript
207+
* let documentRef = firestore.doc('col/doc');
208+
*
209+
* documentRef.update(
210+
* 'counter', Firestore.FieldValue.minimum(1)
211+
* ).then(() => {
212+
* return documentRef.get();
213+
* }).then(doc => {
214+
* // doc.get('counter') is the minimum of either the existing value or 1
215+
* });
216+
* ```
217+
*/
218+
static minimum(n: number): FieldValue {
219+
// eslint-disable-next-line prefer-rest-params
220+
validateMinNumberOfArguments('FieldValue.minimum', arguments, 1);
221+
return new NumericMinimumTransform(n);
222+
}
223+
224+
/**
225+
* Returns a special value that can be used with `set()`, `create()` or `update()`
226+
* that tells the server to set the field to the numeric maximum of the
227+
* field's current and the given value.
228+
*
229+
* If the current field value is not of type 'number', or if the field does
230+
* not yet exist, the transformation will set the field to the given value.
231+
*
232+
* If the existing value and the operand are equivalent, then the field does
233+
* not change. For example, `0`, `0.0`, and `-0.0` are all equivalent. If the
234+
* operand is `NaN` then the result is always `NaN`.
235+
*
236+
* @param {number} n The value to compare to the exiting field value.
237+
* @return {FieldValue} The `FieldValue` for use in a call to `set()`, `create()` or
238+
* `update()`.
239+
*
240+
* @example
241+
* ```typescript
242+
* let documentRef = firestore.doc('col/doc');
243+
*
244+
* documentRef.update(
245+
* 'counter', Firestore.FieldValue.maximum(1)
246+
* ).then(() => {
247+
* return documentRef.get();
248+
* }).then(doc => {
249+
* // doc.get('counter') is the maximum of either the existing value or 1
250+
* });
251+
* ```
252+
*/
253+
static maximum(n: number): FieldValue {
254+
// eslint-disable-next-line prefer-rest-params
255+
validateMinNumberOfArguments('FieldValue.maximum', arguments, 1);
256+
return new NumericMaximumTransform(n);
257+
}
258+
189259
/**
190260
* Returns a special value that can be used with set(), create() or update()
191261
* that tells the server to union the given elements with any array value that
@@ -430,13 +500,12 @@ class ServerTimestampTransform extends FieldTransform {
430500
}
431501

432502
/**
433-
* Increments a field value on the backend.
434-
*
503+
* Base class of numeric field transforms.
435504
* @private
436505
* @internal
437506
*/
438-
class NumericIncrementTransform extends FieldTransform {
439-
constructor(private readonly operand: number) {
507+
abstract class NumericFieldTransform extends FieldTransform {
508+
constructor(protected readonly operand: number) {
440509
super();
441510
}
442511

@@ -460,12 +529,24 @@ class NumericIncrementTransform extends FieldTransform {
460529
return true;
461530
}
462531

463-
get methodName(): string {
464-
return 'FieldValue.increment';
532+
validate(): void {
533+
validateNumber(this.methodName + '()', this.operand);
465534
}
535+
}
466536

467-
validate(): void {
468-
validateNumber('FieldValue.increment()', this.operand);
537+
/**
538+
* Increments a field value on the backend.
539+
*
540+
* @private
541+
* @internal
542+
*/
543+
class NumericIncrementTransform extends NumericFieldTransform {
544+
constructor(operand: number) {
545+
super(operand);
546+
}
547+
548+
get methodName(): string {
549+
return 'FieldValue.increment';
469550
}
470551

471552
toProto(
@@ -480,7 +561,74 @@ class NumericIncrementTransform extends FieldTransform {
480561
return (
481562
this === other ||
482563
(other instanceof NumericIncrementTransform &&
483-
this.operand === other.operand)
564+
(this.operand === other.operand ||
565+
(Number.isNaN(this.operand) && Number.isNaN(other.operand))))
566+
);
567+
}
568+
}
569+
570+
/**
571+
* Sets a field to the minimum of existing or operand.
572+
*
573+
* @private
574+
* @internal
575+
*/
576+
class NumericMinimumTransform extends NumericFieldTransform {
577+
constructor(operand: number) {
578+
super(operand);
579+
}
580+
581+
get methodName(): string {
582+
return 'FieldValue.minimum';
583+
}
584+
585+
toProto(
586+
serializer: Serializer,
587+
fieldPath: FieldPath,
588+
): api.DocumentTransform.IFieldTransform {
589+
const encodedOperand = serializer.encodeValue(this.operand)!;
590+
return {fieldPath: fieldPath.formattedName, minimum: encodedOperand};
591+
}
592+
593+
isEqual(other: firestore.FieldValue): boolean {
594+
return (
595+
this === other ||
596+
(other instanceof NumericMinimumTransform &&
597+
(this.operand === other.operand ||
598+
(Number.isNaN(this.operand) && Number.isNaN(other.operand))))
599+
);
600+
}
601+
}
602+
603+
/**
604+
* Sets a field to the maximum of existing or operand.
605+
*
606+
* @private
607+
* @internal
608+
*/
609+
class NumericMaximumTransform extends NumericFieldTransform {
610+
constructor(operand: number) {
611+
super(operand);
612+
}
613+
614+
get methodName(): string {
615+
return 'FieldValue.maximum';
616+
}
617+
618+
toProto(
619+
serializer: Serializer,
620+
fieldPath: FieldPath,
621+
): api.DocumentTransform.IFieldTransform {
622+
const encodedOperand = serializer.encodeValue(this.operand)!;
623+
return {fieldPath: fieldPath.formattedName, maximum: encodedOperand};
624+
}
625+
626+
isEqual(other: firestore.FieldValue): boolean {
627+
return (
628+
this === other ||
629+
(other instanceof NumericMaximumTransform &&
630+
(this.operand === other.operand ||
631+
(Number.isNaN(this.operand) && Number.isNaN(other.operand))))
484632
);
485633
}
486634
}

0 commit comments

Comments
 (0)