Support -0.0 in Firestore (#2751) · firebase/firebase-js-sdk@4f1f303 (original) (raw)

5 files changed

lines changed

Lines changed: 4 additions & 0 deletions

Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
1 1 # Unreleased
2 +- [fixed] Fixed an issue where the number value `-0.0` would lose its sign when
3 + stored in Firestore.
4 +
5 +# 1.10.0
2 6 - [feature] Implemented `Timestamp.valueOf()` so that `Timestamp` objects can be
3 7 compared for relative ordering using the JavaScript arithmetic comparison
4 8 operators (#2632).

Lines changed: 4 additions & 0 deletions

Original file line number Diff line number Diff line change
@@ -438,6 +438,8 @@ export class JsonProtoSerializer {
438 438 return { doubleValue: 'Infinity' } as {};
439 439 } else if (doubleValue === -Infinity) {
440 440 return { doubleValue: '-Infinity' } as {};
441 +} else if (typeUtils.isNegativeZero(doubleValue)) {
442 +return { doubleValue: '-0' } as {};
441 443 }
442 444 }
443 445 return { doubleValue: val.value() };
@@ -487,6 +489,8 @@ export class JsonProtoSerializer {
487 489 return fieldValue.DoubleValue.POSITIVE_INFINITY;
488 490 } else if ((obj.doubleValue as {}) === '-Infinity') {
489 491 return fieldValue.DoubleValue.NEGATIVE_INFINITY;
492 +} else if ((obj.doubleValue as {}) === '-0') {
493 +return new fieldValue.DoubleValue(-0);
490 494 }
491 495 }
492 496

Lines changed: 10 additions & 2 deletions

Original file line number Diff line number Diff line change
@@ -27,6 +27,13 @@ export function isNullOrUndefined(value: unknown): boolean {
27 27 return value === null |
28 28 }
29 29
30 +/** Returns whether the value represents -0. */
31 +export function isNegativeZero(value: number) : boolean {
32 +// Detect if the value is -0.0. Based on polyfill from
33 +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global\_Objects/Object/is
34 +return value === -0 && 1 / value === 1 / -0;
35 +}
36 +
30 37 /**
31 38 * Returns whether a value is an integer and in the safe integer range
32 39 * @param value The value to test for being an integer and in the safe range
@@ -35,7 +42,8 @@ export function isSafeInteger(value: unknown): boolean {
35 42 return (
36 43 typeof value === 'number' &&
37 44 Number.isInteger(value) &&
38 -(value as number) <= Number.MAX_SAFE_INTEGER &&
39 -(value as number) >= Number.MIN_SAFE_INTEGER
45 +!isNegativeZero(value) &&
46 +value <= Number.MAX_SAFE_INTEGER &&
47 +value >= Number.MIN_SAFE_INTEGER
40 48 );
41 49 }

Lines changed: 6 additions & 0 deletions

Original file line number Diff line number Diff line change
@@ -47,6 +47,12 @@ apiDescribe('Firestore', (persistence: boolean) => {
47 47 });
48 48 });
49 49
50 +it('can read and write number fields', () => {
51 +return withTestDb(persistence, db => {
52 +return expectRoundtrip(db, { a: 1, b: NaN, c: Infinity, d: -0.0 });
53 +});
54 +});
55 +
50 56 it('can read and write array fields', () => {
51 57 return withTestDb(persistence, db => {
52 58 return expectRoundtrip(db, { array: [1, 'foo', { deep: true }, null] });

Lines changed: 8 additions & 7 deletions

Original file line number Diff line number Diff line change
@@ -34,20 +34,21 @@ export interface Equatable {
34 34 */
35 35
36 36 function customDeepEqual(left: unknown, right: unknown): boolean {
37 -/**
38 - * START: Custom compare logic
39 - */
40 37 if (typeof left === 'object' && left && 'isEqual' in left) {
41 38 return (left as Equatable<unknown>).isEqual(right);
42 39 }
43 40 if (typeof right === 'object' && right && 'isEqual' in right) {
44 41 return (right as Equatable<unknown>).isEqual(left);
45 42 }
46 -/**
47 - * END: Custom compare logic
48 - */
49 43 if (left === right) {
50 -return true;
44 +if (left === 0.0 && right === 0.0) {
45 +// Firestore treats -0.0 and +0.0 as not equals, even though JavaScript
46 +// treats them as equal by default. Implemented based on MDN's Object.is()
47 +// polyfill.
48 +return 1 / left === 1 / right;
49 +} else {
50 +return true;
51 +}
51 52 }
52 53 if (
53 54 typeof left === 'number' &&