Skip to content

Finish FieldValue migration #2766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/firestore/src/api/field_value.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* @license
* Copyright 2017 Google Inc.
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/src/api/user_data_reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ export class UserDataReader {
} else if (typeof value === 'boolean') {
return { booleanValue: value };
} else if (typeof value === 'string') {
return { stringValue: '' + value };
return { stringValue: value };
} else if (value instanceof Date) {
const timestamp = Timestamp.fromDate(value);
return { timestampValue: this.serializer.toTimestamp(timestamp) };
Expand Down
94 changes: 60 additions & 34 deletions packages/firestore/src/api/user_data_writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,29 @@
import * as firestore from '@firebase/firestore-types';

import * as api from '../protos/firestore_proto_api';
import * as log from '../util/log';

import { DocumentReference, Firestore } from './database';
import { Blob } from './blob';
import { GeoPoint } from './geo_point';
import { Timestamp } from './timestamp';
import { DatabaseId } from '../core/database_info';
import { DocumentKey } from '../model/document_key';
import { normalizeByteString, normalizeTimestamp } from '../model/values';
import {
normalizeByteString,
normalizeNumber,
normalizeTimestamp,
typeOrder
} from '../model/values';
import {
getLocalWriteTime,
getPreviousValue,
isServerTimestamp
getPreviousValue
} from '../model/server_timestamps';
import { fail } from '../util/assert';
import { assert, fail } from '../util/assert';
import { forEach } from '../util/obj';
import { TypeOrder } from '../model/field_value';
import { ResourcePath } from '../model/path';
import { isValidResourceName } from '../remote/serializer';

export type ServerTimestampBehavior = 'estimate' | 'previous' | 'none';

Expand All @@ -48,36 +57,34 @@ export class UserDataWriter<T = firestore.DocumentData> {
) {}

convertValue(value: api.Value): unknown {
if ('nullValue' in value) {
return null;
} else if ('booleanValue' in value) {
return value.booleanValue!;
} else if ('integerValue' in value) {
return Number(value.integerValue!);
} else if ('doubleValue' in value) {
return Number(value.doubleValue!);
} else if ('timestampValue' in value) {
return this.convertTimestamp(value.timestampValue!);
} else if ('stringValue' in value) {
return value.stringValue!;
} else if ('bytesValue' in value) {
return new Blob(normalizeByteString(value.bytesValue!));
} else if ('referenceValue' in value) {
return this.convertReference(value.referenceValue!);
} else if ('geoPointValue' in value) {
return new GeoPoint(
value.geoPointValue!.latitude!,
value.geoPointValue!.longitude!
);
} else if ('arrayValue' in value) {
return this.convertArray(value.arrayValue!);
} else if ('mapValue' in value) {
if (isServerTimestamp(value)) {
switch (typeOrder(value)) {
case TypeOrder.NullValue:
return null;
case TypeOrder.BooleanValue:
return value.booleanValue!;
case TypeOrder.NumberValue:
return normalizeNumber(value.integerValue || value.doubleValue);
case TypeOrder.TimestampValue:
return this.convertTimestamp(value.timestampValue!);
case TypeOrder.ServerTimestampValue:
return this.convertServerTimestamp(value);
}
return this.convertObject(value.mapValue!);
} else {
return fail('Invalid value type: ' + JSON.stringify(value));
case TypeOrder.StringValue:
return value.stringValue!;
case TypeOrder.BlobValue:
return new Blob(normalizeByteString(value.bytesValue!));
case TypeOrder.RefValue:
return this.convertReference(value.referenceValue!);
case TypeOrder.GeoPointValue:
return new GeoPoint(
value.geoPointValue!.latitude!,
value.geoPointValue!.longitude!
);
case TypeOrder.ArrayValue:
return this.convertArray(value.arrayValue!);
case TypeOrder.ObjectValue:
return this.convertObject(value.mapValue!);
default:
throw fail('Invalid value type: ' + JSON.stringify(value));
}
}

Expand Down Expand Up @@ -122,7 +129,26 @@ export class UserDataWriter<T = firestore.DocumentData> {
}

private convertReference(name: string): DocumentReference<T> {
const key = DocumentKey.fromName(name);
const resourcePath = ResourcePath.fromString(name);
assert(
isValidResourceName(resourcePath),
'ReferenceValue is not valid ' + name
);
const databaseId = new DatabaseId(resourcePath.get(1), resourcePath.get(3));
const key = new DocumentKey(resourcePath.popFirst(5));

if (!databaseId.isEqual(this.firestore._databaseId)) {
// TODO(b/64130202): Somehow support foreign references.
log.error(
`Document ${key} contains a document ` +
`reference within a different database (` +
`${databaseId.projectId}/${databaseId.database}) which is not ` +
`supported. It will be treated as a reference in the current ` +
`database (${this.firestore._databaseId.projectId}/${this.firestore._databaseId.database}) ` +
`instead.`
);
}

return new DocumentReference(key, this.firestore, this.converter);
}
}
12 changes: 6 additions & 6 deletions packages/firestore/src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import { Document } from '../model/document';
import { DocumentKey } from '../model/document_key';
import {
canonicalId,
compare,
valueCompare,
contains,
equals,
valueEquals,
isArray,
isNanValue,
isNullValue,
Expand Down Expand Up @@ -583,7 +583,7 @@ export class FieldFilter extends Filter {
return (
other !== null &&
typeOrder(this.value) === typeOrder(other) &&
this.matchesComparison(compare(other, this.value))
this.matchesComparison(valueCompare(other, this.value))
);
}

Expand Down Expand Up @@ -631,7 +631,7 @@ export class FieldFilter extends Filter {
return (
this.op.isEqual(other.op) &&
this.field.isEqual(other.field) &&
equals(this.value, other.value)
valueEquals(this.value, other.value)
);
} else {
return false;
Expand Down Expand Up @@ -791,7 +791,7 @@ export class Bound {
docValue !== null,
'Field should exist since document matched the orderBy already.'
);
comparison = compare(component, docValue);
comparison = valueCompare(component, docValue);
}
if (orderByComponent.dir === Direction.DESCENDING) {
comparison = comparison * -1;
Expand All @@ -816,7 +816,7 @@ export class Bound {
for (let i = 0; i < this.position.length; i++) {
const thisPosition = this.position[i];
const otherPosition = other.position[i];
if (!equals(thisPosition, otherPosition)) {
if (!valueEquals(thisPosition, otherPosition)) {
return false;
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/firestore/src/model/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { fail } from '../util/assert';
import { DocumentKey } from './document_key';
import { ObjectValue } from './field_value';
import { FieldPath } from './path';
import { compare } from './values';
import { valueCompare } from './values';

export interface DocumentOptions {
hasLocalMutations?: boolean;
Expand Down Expand Up @@ -112,7 +112,7 @@ export class Document extends MaybeDocument {
const v1 = d1.field(field);
const v2 = d2.field(field);
if (v1 !== null && v2 !== null) {
return compare(v1, v2);
return valueCompare(v1, v2);
} else {
return fail("Trying to compare documents on fields that don't exist");
}
Expand Down
20 changes: 11 additions & 9 deletions packages/firestore/src/model/field_value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { assert } from '../util/assert';
import { FieldMask } from './mutation';
import { FieldPath } from './path';
import { isServerTimestamp } from './server_timestamps';
import { equals, isMapValue, typeOrder } from './values';
import { valueEquals, isMapValue, typeOrder } from './values';
import { forEach } from '../util/obj';
import { SortedSet } from '../util/sorted_set';

Expand All @@ -30,17 +30,19 @@ export interface JsonObject<T> {
}

export const enum TypeOrder {
// This order is defined by the backend.
// This order is based on the backend's ordering, but modified to support
// server timestamps.
NullValue = 0,
BooleanValue = 1,
NumberValue = 2,
TimestampValue = 3,
StringValue = 4,
BlobValue = 5,
RefValue = 6,
GeoPointValue = 7,
ArrayValue = 8,
ObjectValue = 9
ServerTimestampValue = 4,
StringValue = 5,
BlobValue = 6,
RefValue = 7,
GeoPointValue = 8,
ArrayValue = 9,
ObjectValue = 10
}

/**
Expand Down Expand Up @@ -123,7 +125,7 @@ export class ObjectValue {
}

isEqual(other: ObjectValue): boolean {
return equals(this.proto, other.proto);
return valueEquals(this.proto, other.proto);
}

/** Creates a ObjectValueBuilder instance that is based on the current value. */
Expand Down
28 changes: 20 additions & 8 deletions packages/firestore/src/model/transform_operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ import * as api from '../protos/firestore_proto_api';
import { Timestamp } from '../api/timestamp';
import { assert } from '../util/assert';
import { JsonProtoSerializer } from '../remote/serializer';
import { equals, isNumber, normalizeNumber } from './values';
import {
valueEquals,
isArray,
isInteger,
isNumber,
normalizeNumber
} from './values';
import { serverTimestamp } from './server_timestamps';
import { arrayEquals } from '../util/misc';

Expand Down Expand Up @@ -116,7 +122,7 @@ export class ArrayUnionTransformOperation implements TransformOperation {
private apply(previousValue: api.Value | null): api.Value {
const values = coercedFieldValuesArray(previousValue);
for (const toUnion of this.elements) {
if (!values.some(element => equals(element, toUnion))) {
if (!values.some(element => valueEquals(element, toUnion))) {
values.push(toUnion);
}
}
Expand All @@ -130,7 +136,7 @@ export class ArrayUnionTransformOperation implements TransformOperation {
isEqual(other: TransformOperation): boolean {
return (
other instanceof ArrayUnionTransformOperation &&
arrayEquals(this.elements, other.elements, equals)
arrayEquals(this.elements, other.elements, valueEquals)
);
}
}
Expand Down Expand Up @@ -159,7 +165,7 @@ export class ArrayRemoveTransformOperation implements TransformOperation {
private apply(previousValue: api.Value | null): api.Value {
let values = coercedFieldValuesArray(previousValue);
for (const toRemove of this.elements) {
values = values.filter(element => !equals(element, toRemove));
values = values.filter(element => !valueEquals(element, toRemove));
}
return { arrayValue: { values } };
}
Expand All @@ -171,7 +177,7 @@ export class ArrayRemoveTransformOperation implements TransformOperation {
isEqual(other: TransformOperation): boolean {
return (
other instanceof ArrayRemoveTransformOperation &&
arrayEquals(this.elements, other.elements, equals)
arrayEquals(this.elements, other.elements, valueEquals)
);
}
}
Expand Down Expand Up @@ -202,7 +208,11 @@ export class NumericIncrementTransformOperation implements TransformOperation {
// manually cap overflows at 2^63.
const baseValue = this.computeBaseValue(previousValue);
const sum = this.asNumber(baseValue) + this.asNumber(this.operand);
return this.serializer.toNumber(sum);
if (isInteger(baseValue) && isInteger(this.operand)) {
return this.serializer.toInteger(sum);
} else {
return this.serializer.toDouble(sum);
}
}

applyToRemoteDocument(
Expand All @@ -227,7 +237,7 @@ export class NumericIncrementTransformOperation implements TransformOperation {
isEqual(other: TransformOperation): boolean {
return (
other instanceof NumericIncrementTransformOperation &&
equals(this.operand, other.operand)
valueEquals(this.operand, other.operand)
);
}

Expand All @@ -237,5 +247,7 @@ export class NumericIncrementTransformOperation implements TransformOperation {
}

function coercedFieldValuesArray(value: api.Value | null): api.Value[] {
return value?.arrayValue?.values?.slice() || [];
return isArray(value) && value.arrayValue.values
? value.arrayValue.values.slice()
: [];
}
Loading