Skip to content

Protobuf-backed FieldValues #2682

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

Conversation

schmidt-sebastian
Copy link
Contributor

Copy link
Contributor

@var-const var-const left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the long delay.

}

private convertValue(value: api.Value): FieldType {
if ('nullValue' in value) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: this seems to involve string comparison. Is there any way to use some sort of an enum instead? (based on the assumption that it might have some, even if minor, effect on performance when converting many values)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are key lookups in a ProtobufJS-generated JavaScript object. Unfortunately, we don't control how these are generated. FWIW, back in the days (with an older version of ProtobufJs), we were able to use an enum-based approach.

}

private convertReference(value: string): DocumentKey {
// TODO(mrschmidt): Move `value()` and `convertValue()` to DocumentSnapshot,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this comment be moved somewhere else? (it refers to value and convertValue, not convertReference)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will allow us to fix convertReference. I think this is not the best comment, but it will get removed before we merge.

if (other instanceof PrimitiveValue) {
return equals(this.proto, other.proto);
}
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify -- why aren't two maps with the same keys and values ever considered equal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A map would pass the instanceof PrimitiveValue check and trigger this code: https://github.com/firebase/firebase-js-sdk/blob/mrschmidt/rewritefieldvalue/packages/firestore/src/model/proto_values.ts#L162 (which enforces key/value based equality).


/**
* An ObjectValue represents a MapValue in the Firestore Proto and offers the
* ability to add an remove fields (via the ObjectValueBuilder).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/an/and/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

/**
* An ObjectValue represents a MapValue in the Firestore Proto and offers the
* ability to add an remove fields (via the ObjectValueBuilder).
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: consider extending the comment to compare and contrast ObjectValue and PrimitiveValue that is a map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion, but PrimitiveValue is actually just a stepping stone to allow us to gradually remove field values. In the final PR, PrimitiveValue will disappear.

const timestamp = Timestamp.fromDate(input);
return {
timestampValue: {
seconds: String(timestamp.seconds),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: why is String necessary here, but not for integerValue above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

integerValue is defined as string | number, which this should be too. Fixed.

expectedTypeOrder: TypeOrder
): boolean {
return value !== undefined && typeOrder(value) === expectedTypeOrder;
): value is api.Value {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: how does value is api.Value work as a return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It tells the transpiler that it can assume that value is api.Value if the function returns true. This allows the following:

let valueOrUnknown : api.Value | null | undefined = ...;

if (isType(valueOrUnknown, ...)) {
   // valueOrUnknown is api.Value
}

This is all based on the honor system and we could easily fool the transpiler by providing the wrong guarantees.


/**
* An Overlay, which contains an update to apply. Can either be Value proto, a
* ma of Overlay values (to represent additional nesting at the given key) or
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: s/ma/map/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


/**
* Removes the field at the specified path. If there is no field at the
* specified path nothing is changed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultranit: I think there should be a comma after path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

* Removes the field at the specified path. If there is no field at the
* specified path nothing is changed.
*
* @param path The field path to remove
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultranit: missing full stop at the end of the sentence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

@schmidt-sebastian schmidt-sebastian merged commit c32a9a6 into mrschmidt/rewritefieldvalue Mar 4, 2020
@firebase firebase locked and limited conversation to collaborators Apr 4, 2020
@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/rewritefieldvalue-4 branch April 6, 2020 19:26
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants