Skip to content

Add FieldValue helpers #7642

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 22 commits into from
Mar 12, 2021
Merged

Conversation

@google-cla google-cla bot added the cla: yes label Mar 2, 2021
@google-oss-bot
Copy link

1 Warning
⚠️ Did you forget to add a changelog entry? (Add #no-changelog to the PR description to silence this warning.)

Generated by 🚫 Danger

@schmidt-sebastian schmidt-sebastian changed the base branch from master to mrschmidt/mutabledocuments March 4, 2021 17:40
@schmidt-sebastian schmidt-sebastian changed the title WIP Add FieldValue helpers Mar 4, 2021
@schmidt-sebastian schmidt-sebastian marked this pull request as ready for review March 4, 2021 17:40
@schmidt-sebastian schmidt-sebastian requested a review from wu-hui March 4, 2021 17:46
@google-oss-bot
Copy link

google-oss-bot commented Mar 4, 2021

Coverage Report

Affected SDKs

  • FirebaseFirestore-iOS-FirebaseFirestore.framework

    SDK overall coverage changed from 88.65% (5f6feba) to 88.64% (164abb8) by -0.00%.

    Filename Base (5f6feba) Head (164abb8) Diff
    grpc_nanopb.cc 100.00% 95.45% -4.55%
    leveldb_key.cc 97.60% 98.08% +0.48%
    ordered_code.cc 93.24% 92.68% -0.56%
    server_timestamp_util.cc ? 84.85% ?
    task.cc 95.21% 96.41% +1.20%
    value_util.cc ? 89.16% ?
    watch_stream.cc 96.15% 92.31% -3.85%

Test Logs

Copy link
Contributor

@wu-hui wu-hui left a comment

Choose a reason for hiding this comment

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

Mostly around how strings are handled.


const uint64_t kNanBits = 0x7fff000000000000ULL;

} // namespace
Copy link
Contributor

Choose a reason for hiding this comment

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

This entire test should be in the unnamed namespace, Costa would give you a link explaining why, I am just forwarding his review comments from my PR..something to do with avoid potential conflict.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking forward to the link :) Fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

If you're interested, here's the recommendation. The idea is to prevent potential name clashes. If you were to accidentally write, say, two different functions the same name and signature in two different test files, they will clash -- the linker would presume they are the same function. That is because by default functions have external linkage and are visible (to the linker) outside of their translation unit (every .cc file corresponds to a translation unit, and the entire program is then built from these TUs). However, functions (and variables, etc.) placed in the unnamed namespace have internal linkage and thus cannot clash (even if they have the same name, the linker will correctly interpret functions with internal linkage as two different functions).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am always interested in learning :) I hope I will remember this time.

// latin small letter e + combining acute accent
Add(equals_group, Wrap("e\u0301b"));
// latin small letter e with acute accent
Add(equals_group, Wrap("\u00e9a"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Other possible case: string with '\0' in the middle of it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Bump

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There should already be done in the line below (added in that review cycle). I added additional ones for comparison + canoncial ID now.

@wu-hui wu-hui assigned schmidt-sebastian and unassigned wu-hui Mar 8, 2021
Copy link
Contributor Author

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

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

Thanks for the review! I assigning it to Costa as well for some final thoughts.


const uint64_t kNanBits = 0x7fff000000000000ULL;

} // namespace
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking forward to the link :) Fixed

@wu-hui wu-hui requested a review from var-const March 9, 2021 21:15
@wu-hui wu-hui removed their assignment Mar 9, 2021
const int32_t kTypeOrderArray = 9;
const int32_t kTypeOrderMap = 10;

class Values {
Copy link
Contributor

Choose a reason for hiding this comment

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

General observation: this class is very Java-style. Since there are no free functions in Java, creating classes that only have static methods makes sense. In C++, it's more natural to just use free functions (note, there's nothing wrong with using free functions when they are appropriate). The private static functions can just be moved into the implementation file (the .cc file), with the additional benefit of a smaller header file.

Similarly, the pattern of naming an utility class that deals with Foos Foos is specific to Java. Consider a file name like value_ordering, field_value_ordering, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made all methods free-standing and renamed the file to "value.h/cc". Should we create a separate namespace the functions?

Copy link
Contributor

@var-const var-const Mar 10, 2021

Choose a reason for hiding this comment

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

Thanks! Looks much better now.

Re. the file name -- do you plan to add more functions (that aren't concerned with comparisons) to this file in the future?

Should we create a separate namespace the functions?

We use namespaces for system layers, so to speak (e.g. model or remote), but not for finer-grained combinations. This is fine to just be in the model namespace.

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 file contains equals, compareTo and canonical ID support. That's all we have on Web and Android, but in general we treated this file as a "proto values utility" class on the other platforms.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: a file named value seems to indicate that it defines a Value class. I don't have a better suggestion than the uninspired value_helpers, but personally, I feel it would convey the intention better (same for the server timestamp one). If you can think of a better name, that's great.

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 to value_util.h to match nanopb_util.h


template <typename... Args>
google_firestore_v1_Value WrapObject(Args... key_value_pairs) {
FieldValue fv = testutil::WrapObject((key_value_pairs)...);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nits:

  • the extra parentheses around key_value_pairs are unnecessary;
  • it's not very important because it's a test file, but ideally you would make sure to forward the arguments to WrapObject to avoid extra copies (though WrapObject's implementation would then proceed to create those copies regardless -- ideally it should be fixed, but that's out of scope for this PR):
  template <typename... Args>
  // Make `Args` a forwarding reference
  google_firestore_v1_Value WrapObject(Args&&... key_value_pairs) {
    FieldValue fv = testutil::WrapObject(std::forward<Args>(key_value_pairs)...);

Feel free to disregard the point re. forwarding.

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 both. Thanks for the explanation.

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.

Thanks for implementing the feedback!

const int32_t kTypeOrderArray = 9;
const int32_t kTypeOrderMap = 10;

class Values {
Copy link
Contributor

@var-const var-const Mar 10, 2021

Choose a reason for hiding this comment

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

Thanks! Looks much better now.

Re. the file name -- do you plan to add more functions (that aren't concerned with comparisons) to this file in the future?

Should we create a separate namespace the functions?

We use namespaces for system layers, so to speak (e.g. model or remote), but not for finer-grained combinations. This is fine to just be in the model namespace.


const uint64_t kNanBits = 0x7fff000000000000ULL;

} // namespace
Copy link
Contributor

Choose a reason for hiding this comment

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

If you're interested, here's the recommendation. The idea is to prevent potential name clashes. If you were to accidentally write, say, two different functions the same name and signature in two different test files, they will clash -- the linker would presume they are the same function. That is because by default functions have external linkage and are visible (to the linker) outside of their translation unit (every .cc file corresponds to a translation unit, and the entire program is then built from these TUs). However, functions (and variables, etc.) placed in the unnamed namespace have internal linkage and thus cannot clash (even if they have the same name, the linker will correctly interpret functions with internal linkage as two different functions).

}

for (size_t i = 0; i < right_map.fields_count; ++i) {
const std::string& key = nanopb::MakeString(right_map.fields[i].key);
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you trying to use flat_hash_map (judging by container_internal::FlatHashMapPolicy)? Can you try adding absl::flat_hash_map to the dependencies?

kTypeOrderArray = 9,
kTypeOrderMap = 10
enum class TypeOrder {
Null = 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: enum members should be named like constants, i.e. prefixed with a k (e.g. kNull).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I should get my inspirations from the style guide, and not from other places in the code :)

}

for (size_t i = 0; i < right_map.fields_count; ++i) {
const std::string& key = nanopb::MakeString(right_map.fields[i].key);
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: as far as I understand, the underscored targets are supposed to be internal, and the public Abseil targets have semicolons in them (e.g. absl::flat_hash_map is public) (I'm basing this on this readme). If you're interested, you can try to add absl::flat_hash_map to the list of dependencies. If that doesn't help, it's probably not worth spending any extra time on.

const int32_t kTypeOrderArray = 9;
const int32_t kTypeOrderMap = 10;

class Values {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: a file named value seems to indicate that it defines a Value class. I don't have a better suggestion than the uninspired value_helpers, but personally, I feel it would convey the intention better (same for the server timestamp one). If you can think of a better name, that's great.

@schmidt-sebastian schmidt-sebastian merged commit 4f537e7 into mrschmidt/mutabledocuments Mar 12, 2021
@schmidt-sebastian schmidt-sebastian deleted the mrschmidt/values branch March 12, 2021 19:42
@firebase firebase locked and limited conversation to collaborators Apr 12, 2021
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