Skip to content

Rework FieldMask to use a (ordered) set of FieldPaths #2136

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 3 commits into from
Jan 2, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 5 additions & 3 deletions Firestore/Example/Tests/Util/FSTHelpers.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

#include <cinttypes>
#include <list>
#include <set>
#include <unordered_map>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -252,18 +253,19 @@ NSComparator FSTTestDocComparator(const absl::string_view fieldPath) {
BOOL merge = !updateMask.empty();

__block FSTObjectValue *objectValue = [FSTObjectValue objectValue];
__block std::vector<FieldPath> fieldMaskPaths;
__block std::set<FieldPath> fieldMaskPaths;
[values enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
const FieldPath path = testutil::Field(util::MakeString(key));
fieldMaskPaths.push_back(path);
fieldMaskPaths.insert(path);
if (![value isEqual:kDeleteSentinel]) {
FSTFieldValue *parsedValue = FSTTestFieldValue(value);
objectValue = [objectValue objectBySettingValue:parsedValue forPath:path];
}
}];

DocumentKey key = testutil::Key(path);
FieldMask mask(merge ? updateMask : fieldMaskPaths);
FieldMask mask(merge ? std::set<FieldPath>(updateMask.begin(), updateMask.end())
: fieldMaskPaths);
return [[FSTPatchMutation alloc] initWithKey:key
fieldMask:mask
value:objectValue
Expand Down
5 changes: 3 additions & 2 deletions Firestore/Source/API/FSTUserDataConverter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import "Firestore/Source/API/FSTUserDataConverter.h"

#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -130,7 +131,7 @@ - (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fie
(FSTObjectValue *)[self parseData:input context:accumulator.RootContext()];

if (fieldMask) {
std::vector<FieldPath> validatedFieldPaths;
std::set<FieldPath> validatedFieldPaths;
for (id fieldPath in fieldMask) {
FieldPath path;

Expand All @@ -150,7 +151,7 @@ - (ParsedSetData)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fie
path.CanonicalString().c_str());
}

validatedFieldPaths.push_back(path);
validatedFieldPaths.insert(path);
}

return std::move(accumulator).MergeData(updateData, FieldMask{std::move(validatedFieldPaths)});
Expand Down
6 changes: 3 additions & 3 deletions Firestore/Source/Remote/FSTSerializerBeta.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#import "Firestore/Source/Remote/FSTSerializerBeta.h"

#include <cinttypes>
#include <set>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -565,10 +566,9 @@ - (GCFSDocumentMask *)encodedFieldMask:(const FieldMask &)fieldMask {
}

- (FieldMask)decodedFieldMask:(GCFSDocumentMask *)fieldMask {
std::vector<FieldPath> fields;
fields.reserve(fieldMask.fieldPathsArray_Count);
std::set<FieldPath> fields;
for (NSString *path in fieldMask.fieldPathsArray) {
fields.push_back(FieldPath::FromServerFormat(util::MakeString(path)));
fields.insert(FieldPath::FromServerFormat(util::MakeString(path)));
}
return FieldMask(std::move(fields));
}
Expand Down
3 changes: 2 additions & 1 deletion Firestore/core/src/firebase/firestore/core/user_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_CORE_USER_DATA_H_

#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -166,7 +167,7 @@ class ParseAccumulator {
// field_mask_ and field_transforms_ are shared across all active context
// objects to accumulate the result. All ChildContext objects append their
// results here.
std::vector<model::FieldPath> field_mask_;
std::set<model::FieldPath> field_mask_;
std::vector<model::FieldTransform> field_transforms_;
};

Expand Down
2 changes: 1 addition & 1 deletion Firestore/core/src/firebase/firestore/core/user_data.mm
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
}

void ParseAccumulator::AddToFieldMask(FieldPath field_path) {
field_mask_.push_back(std::move(field_path));
field_mask_.insert(std::move(field_path));
}

void ParseAccumulator::AddToFieldTransforms(
Expand Down
12 changes: 7 additions & 5 deletions Firestore/core/src/firebase/firestore/model/field_mask.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_FIELD_MASK_H_

#include <initializer_list>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "Firestore/core/src/firebase/firestore/util/hashing.h"
Expand All @@ -41,12 +41,14 @@ namespace model {
*/
class FieldMask {
public:
using const_iterator = std::vector<FieldPath>::const_iterator;
using const_iterator = std::set<FieldPath>::const_iterator;

FieldMask(std::initializer_list<FieldPath> list) : fields_{list} {
}
explicit FieldMask(std::vector<FieldPath> fields)
: fields_{std::move(fields)} {
template <class InputIt>
FieldMask(InputIt first, InputIt last) : fields_{first, last} {
}
explicit FieldMask(std::set<FieldPath> fields) : fields_{std::move(fields)} {
}

const_iterator begin() const {
Expand Down Expand Up @@ -94,7 +96,7 @@ class FieldMask {
friend bool operator==(const FieldMask& lhs, const FieldMask& rhs);

private:
std::vector<FieldPath> fields_;
std::set<FieldPath> fields_;
};

inline bool operator==(const FieldMask& lhs, const FieldMask& rhs) {
Expand Down
23 changes: 13 additions & 10 deletions Firestore/core/test/firebase/firestore/model/field_mask_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

#include "Firestore/core/src/firebase/firestore/model/field_mask.h"

#include <vector>
#include <set>

#include "Firestore/core/src/firebase/firestore/model/field_path.h"
#include "gtest/gtest.h"
Expand All @@ -28,27 +28,30 @@ namespace model {
TEST(FieldMask, ConstructorAndEqual) {
FieldMask mask_a{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")};
std::vector<FieldPath> field_path_vector{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")};
FieldMask mask_b{field_path_vector};
FieldMask mask_c{std::vector<FieldPath>{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")}};
std::set<FieldPath> field_path_set{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")};
FieldMask mask_b{field_path_set};
FieldMask mask_c{std::set<FieldPath>{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")}};
FieldMask mask_d{field_path_set.begin(), field_path_set.end()};

EXPECT_EQ(mask_a, mask_b);
EXPECT_EQ(mask_b, mask_c);
EXPECT_EQ(mask_c, mask_d);
}

TEST(FieldMask, Getter) {
FieldMask mask{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")};
EXPECT_EQ(std::vector<FieldPath>({FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")}),
std::vector<FieldPath>(mask.begin(), mask.end()));
EXPECT_EQ(std::set<FieldPath>({FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")}),
std::set<FieldPath>(mask.begin(), mask.end()));
}

TEST(FieldMask, ToString) {
FieldMask mask{FieldPath::FromServerFormat("foo"),
FieldPath::FromServerFormat("bar")};
EXPECT_EQ("{ foo bar }", mask.ToString());
EXPECT_EQ("{ bar foo }", mask.ToString());
}

} // namespace model
Expand Down
24 changes: 15 additions & 9 deletions Firestore/core/test/firebase/firestore/testutil/testutil.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,40 @@

#include "Firestore/core/test/firebase/firestore/testutil/testutil.h"

#include <set>

namespace firebase {
namespace firestore {
namespace testutil {

std::unique_ptr<model::PatchMutation> PatchMutation(
absl::string_view path,
const model::ObjectValue::Map& values,
// TODO(rsgowman): Investigate changing update_mask to a set.
const std::vector<model::FieldPath>* update_mask) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Very optional: can update_mask also be a set? Perhaps that would allow removing the new constructor of FieldMask taking two iterators?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think so, but I've deferred for now. (Added a TODO here to track this.)

model::FieldValue object_value = model::FieldValue::FromMap({});
std::vector<model::FieldPath> object_mask;
std::set<model::FieldPath> object_mask;

for (const auto& kv : values) {
model::FieldPath field_path = Field(kv.first);
object_mask.push_back(field_path);
object_mask.insert(field_path);
if (kv.second.string_value() != kDeleteSentinel) {
object_value = object_value.Set(field_path, kv.second);
}
}

bool merge = update_mask != nullptr;

// We sort the field_mask_paths to make the order deterministic in tests.
std::sort(object_mask.begin(), object_mask.end());

return absl::make_unique<model::PatchMutation>(
Key(path), std::move(object_value),
model::FieldMask(merge ? *update_mask : object_mask),
merge ? model::Precondition::None() : model::Precondition::Exists(true));
if (merge) {
return absl::make_unique<model::PatchMutation>(
Key(path), std::move(object_value),
model::FieldMask(update_mask->begin(), update_mask->end()),
model::Precondition::None());
} else {
return absl::make_unique<model::PatchMutation>(
Key(path), std::move(object_value), model::FieldMask(object_mask),
model::Precondition::Exists(true));
}
}

} // namespace testutil
Expand Down