Skip to content

Adding Proto-based equality and comparison #1157

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 4 commits into from
Jan 24, 2020

Conversation

schmidt-sebastian
Copy link
Contributor

@schmidt-sebastian schmidt-sebastian commented Jan 23, 2020

This will be used (and tested) in the follow-up CL that adds the FieldValue tpes.

I am also removing one of the the two "compareInt" functions.

For reference: #1156

This will be used (and tested) in the follow-up CL that adds the FieldValue tpes.
@codecov
Copy link

codecov bot commented Jan 23, 2020

Codecov Report

❗ No coverage uploaded for pull request base (mrschmidt/rewritefieldvalue@baa0a89). Click here to learn what that means.
The diff coverage is 8.66%.

Flag Coverage Δ Complexity Δ
#FirebaseFirestore 61.29% <8.66%> (?) 2279 <6> (?)
#FirebaseFirestore_Ktx 41.17% <ø> (?) 0 <ø> (?)
Impacted Files Coverage Δ Complexity Δ
...gle/firebase/firestore/model/value/FieldValue.java 83.33% <ø> (ø) 4 <0> (?)
...le/firebase/firestore/model/value/ProtoValues.java 0% <0%> (ø) 0 <0> (?)
.../main/java/com/google/firebase/firestore/Blob.java 92.3% <100%> (ø) 10 <1> (?)
.../java/com/google/firebase/firestore/util/Util.java 49.35% <100%> (ø) 18 <4> (?)
.../firebase/firestore/local/SQLiteMutationQueue.java 80.34% <100%> (ø) 43 <1> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update baa0a89...6e4ddde. Read the comment docs.

import java.util.Map;
import java.util.TreeMap;

public class ProtoValueUtil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about naming: how about ProtoValues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed


switch (value.getValueTypeCase()) {
case NULL_VALUE:
return FieldValue.TYPE_ORDER_NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

Putting this class in the util package creates a cyclic reference between packages that isn't going to work in C++. Since we're using these utilities in the service of building models, might as well just put this in the model package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I believe that means that this class can then be package-private. I did not yet make it so since my next PR temporarily adds a new package for the new ObjectValue to avoid breaking all existing code. Added a TODO to change the visibility.

switch (leftType) {
case FieldValue.TYPE_ORDER_ARRAY:
return arrayEquals(left, right);
case FieldValue.TYPE_ORDER_NUMBER:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: consider ordering the switch cases consistently so that it's easier to audit/reason about differences (in this case, that would mean number, array, object).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done (here and everywhere). I am following the type order constants.

}
}

private static boolean objectEquals(Value left, Value right) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: consider ordering these methods in the file in the order you're using them in equals, 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.

Done

}
}

private static int compareMaps(Value left, Value right) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: consider ordering these to match their usage 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.

Done

double thisDouble = left.getDoubleValue();
if (right.getValueTypeCase() == Value.ValueTypeCase.DOUBLE_VALUE) {
return Util.compareDoubles(thisDouble, right.getDoubleValue());
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

You could express this with less code if you handled all the unexpected cases in one place, below:

if (left is double) {
  if (right is double) {
    return ...;
  } else if (right is int) {
    return ...;
  }
} else if (left is int) {
  if (right is double) {
    return ...;
  } else if (right is int) {
    return ...;
  }
}

hardAssert("Unexpected types %s and %s, left, right);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great suggestion. Updated.

// Byte values are equal, continue with comparison
}
return Util.compareIntegers(bytes.size(), other.bytes.size());
return Util.compareByteString(bytes, other.bytes);
Copy link
Contributor

Choose a reason for hiding this comment

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

Other compare methods are plural (e.g. compareIntegers). compareByteStrings?

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

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

LGTM

int minLength =
Math.min(left.getArrayValue().getValuesCount(), right.getArrayValue().getValuesCount());
private static int compareTimestamps(Value left, Value right) {
if (left.getTimestampValue().getSeconds() == right.getTimestampValue().getSeconds()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Elsewhere we compare and then if non-zero do the second one. Consider rephrasing as:

int cmp = Util.compareIntegers(left.getTimestampValue().getSeconds(), right.getTimestampValue().getSeconds());
if (cmp != 0) return cmp;

return Util.compareIntegers(left.getTimestampValue().getNanos(), right.getTimestampValue().getNanos());

This has two benefits:

  • it makes the hierarchy of comparison linear; and
  • it avoids using subtraction for comparison, which is specious in the general case due to the possibility of overflow. I think it's the case that timestamp values are defined in such a way that subtraction can't possibly overflow, but there's no reason not to reuse the comparator we already have, in which case we don't have to think about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code is actually based on the original code. I do think the comparisons in the other functions are easier to follow, so I updated it.

As part of this, I changed the method to take TimestampValues (along with the rest). This avoids a bunch of extra linebreaks since it removes the calls to getTimestampValue() from here.

@wilhuff wilhuff assigned schmidt-sebastian and unassigned wilhuff Jan 24, 2020
@schmidt-sebastian schmidt-sebastian merged commit e7ac8b9 into mrschmidt/rewritefieldvalue Jan 24, 2020
@google-oss-bot
Copy link
Contributor

@schmidt-sebastian: The following test failed, say /retest to rerun them all:

Test name Commit Details Rerun command
smoke-tests 6e4ddde link /test smoke-tests

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/comparisons branch February 7, 2020 00:22
@firebase firebase locked and limited conversation to collaborators Feb 24, 2020
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.

4 participants