Skip to content

Commit 330ed4a

Browse files
ObjectValue changes (#8007)
1 parent 30082cd commit 330ed4a

File tree

3 files changed

+207
-120
lines changed

3 files changed

+207
-120
lines changed

Firestore/core/src/model/object_value.cc

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,24 @@
2424
#include "Firestore/core/src/nanopb/fields_array.h"
2525
#include "Firestore/core/src/nanopb/message.h"
2626
#include "Firestore/core/src/nanopb/nanopb_util.h"
27+
#include "Firestore/core/src/util/hashing.h"
28+
#include "absl/types/span.h"
2729

2830
namespace firebase {
2931
namespace firestore {
3032
namespace model {
3133

3234
namespace {
3335

34-
using nanopb::FieldsArray;
36+
using nanopb::CheckedSize;
3537
using nanopb::FreeFieldsArray;
38+
using nanopb::FreeNanopbMessage;
3639
using nanopb::MakeArray;
3740
using nanopb::MakeBytesArray;
3841
using nanopb::MakeString;
3942
using nanopb::MakeStringView;
43+
using nanopb::ReleaseFieldOwnership;
44+
using nanopb::SetRepeatedField;
4045

4146
struct MapEntryKeyCompare {
4247
bool operator()(const google_firestore_v1_MapValue_FieldsEntry& entry,
@@ -49,6 +54,28 @@ struct MapEntryKeyCompare {
4954
}
5055
};
5156

57+
/** Traverses a Value proto and sorts all MapValues by key. */
58+
void SortFields(google_firestore_v1_Value& value) {
59+
if (value.which_value_type == google_firestore_v1_Value_map_value_tag) {
60+
google_firestore_v1_MapValue& map_value = value.map_value;
61+
std::sort(map_value.fields, map_value.fields + map_value.fields_count,
62+
[](const google_firestore_v1_MapValue_FieldsEntry& lhs,
63+
const google_firestore_v1_MapValue_FieldsEntry& rhs) {
64+
return nanopb::MakeStringView(lhs.key) <
65+
nanopb::MakeStringView(rhs.key);
66+
});
67+
68+
for (pb_size_t i = 0; i < map_value.fields_count; ++i) {
69+
SortFields(map_value.fields[i].value);
70+
}
71+
} else if (value.which_value_type ==
72+
google_firestore_v1_Value_array_value_tag) {
73+
for (pb_size_t i = 0; i < value.array_value.values_count; ++i) {
74+
SortFields(value.array_value.values[i]);
75+
}
76+
}
77+
}
78+
5279
/**
5380
* Finds an entry by key in the provided map value. Returns `nullptr` if the
5481
* entry does not exist.
@@ -135,6 +162,7 @@ void ApplyChanges(
135162

136163
target_entry.key = source_entry.key;
137164
target_entry.value = DeepClone(upsert_it->second);
165+
SortFields(target_entry.value);
138166

139167
++upsert_it;
140168
++source_index;
@@ -162,22 +190,57 @@ void ApplyChanges(
162190

163191
free(parent->fields);
164192
parent->fields = target_fields;
165-
parent->fields_count = target_count;
193+
parent->fields_count = CheckedSize(target_count);
166194
}
167195

168196
} // namespace
169197

170-
MutableObjectValue::MutableObjectValue() {
198+
ObjectValue::ObjectValue() {
171199
value_->which_value_type = google_firestore_v1_Value_map_value_tag;
172200
value_->map_value.fields_count = 0;
173201
value_->map_value.fields = nullptr;
174202
}
175203

176-
FieldMask MutableObjectValue::ToFieldMask() const {
204+
ObjectValue::ObjectValue(const google_firestore_v1_Value& value)
205+
: value_(value) {
206+
HARD_ASSERT(value.which_value_type == google_firestore_v1_Value_map_value_tag,
207+
"ObjectValues should be backed by a MapValue");
208+
SortFields(*value_);
209+
}
210+
211+
ObjectValue::ObjectValue(const ObjectValue& other)
212+
: value_(DeepClone(*other.value_)) {
213+
}
214+
215+
ObjectValue ObjectValue::FromMapValue(google_firestore_v1_MapValue map_value) {
216+
google_firestore_v1_Value value{};
217+
value.which_value_type = google_firestore_v1_Value_map_value_tag;
218+
value.map_value = map_value;
219+
return ObjectValue{value};
220+
}
221+
222+
ObjectValue ObjectValue::FromFieldsEntry(
223+
google_firestore_v1_Document_FieldsEntry* fields_entry, pb_size_t count) {
224+
google_firestore_v1_Value value{};
225+
value.which_value_type = google_firestore_v1_Value_map_value_tag;
226+
SetRepeatedField(
227+
&value.map_value.fields, &value.map_value.fields_count,
228+
absl::Span<google_firestore_v1_Document_FieldsEntry>(fields_entry, count),
229+
[](const google_firestore_v1_Document_FieldsEntry& entry) {
230+
google_firestore_v1_MapValue_FieldsEntry result{};
231+
result.key = entry.key;
232+
result.value = entry.value;
233+
return result;
234+
});
235+
ReleaseFieldOwnership(fields_entry, count);
236+
return ObjectValue{value};
237+
}
238+
239+
FieldMask ObjectValue::ToFieldMask() const {
177240
return ExtractFieldMask(value_->map_value);
178241
}
179242

180-
FieldMask MutableObjectValue::ExtractFieldMask(
243+
FieldMask ObjectValue::ExtractFieldMask(
181244
const google_firestore_v1_MapValue& value) const {
182245
std::set<FieldPath> fields;
183246

@@ -206,7 +269,7 @@ FieldMask MutableObjectValue::ExtractFieldMask(
206269
return FieldMask(std::move(fields));
207270
}
208271

209-
absl::optional<google_firestore_v1_Value> MutableObjectValue::Get(
272+
absl::optional<google_firestore_v1_Value> ObjectValue::Get(
210273
const FieldPath& path) const {
211274
if (path.empty()) {
212275
return *value_;
@@ -222,8 +285,12 @@ absl::optional<google_firestore_v1_Value> MutableObjectValue::Get(
222285
return nested_value;
223286
}
224287

225-
void MutableObjectValue::Set(const FieldPath& path,
226-
const google_firestore_v1_Value& value) {
288+
google_firestore_v1_Value ObjectValue::Get() const {
289+
return *value_;
290+
}
291+
292+
void ObjectValue::Set(const FieldPath& path,
293+
const google_firestore_v1_Value& value) {
227294
HARD_ASSERT(!path.empty(), "Cannot set field for empty path on ObjectValue");
228295

229296
google_firestore_v1_MapValue* parent_map = ParentMap(path.PopLast());
@@ -235,8 +302,7 @@ void MutableObjectValue::Set(const FieldPath& path,
235302
ApplyChanges(parent_map, upserts, /*deletes=*/{});
236303
}
237304

238-
void MutableObjectValue::SetAll(const FieldMask& field_mask,
239-
const MutableObjectValue& data) {
305+
void ObjectValue::SetAll(const FieldMask& field_mask, const ObjectValue& data) {
240306
FieldPath parent;
241307

242308
std::map<std::string, google_firestore_v1_Value> upserts;
@@ -264,7 +330,7 @@ void MutableObjectValue::SetAll(const FieldMask& field_mask,
264330
ApplyChanges(parent_map, upserts, deletes);
265331
}
266332

267-
void MutableObjectValue::Delete(const FieldPath& path) {
333+
void ObjectValue::Delete(const FieldPath& path) {
268334
HARD_ASSERT(!path.empty(), "Cannot delete field with empty path");
269335

270336
google_firestore_v1_Value* nested_value = value_.get();
@@ -286,12 +352,19 @@ void MutableObjectValue::Delete(const FieldPath& path) {
286352
}
287353
}
288354

355+
std::string ObjectValue::ToString() const {
356+
return CanonicalId(*value_);
357+
}
358+
359+
size_t ObjectValue::Hash() const {
360+
return util::Hash(CanonicalId(*value_));
361+
}
362+
289363
/**
290364
* Returns the map that contains the leaf element of `path`. If the parent
291365
* entry does not yet exist, or if it is not a map, a new map will be created.
292366
*/
293-
google_firestore_v1_MapValue* MutableObjectValue::ParentMap(
294-
const FieldPath& path) {
367+
google_firestore_v1_MapValue* ObjectValue::ParentMap(const FieldPath& path) {
295368
google_firestore_v1_Value* parent = value_.get();
296369

297370
// Find a or create a parent map entry for `path`.

Firestore/core/src/model/object_value.h

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,33 @@ namespace firestore {
3737
namespace model {
3838

3939
/** A structured object value stored in Firestore. */
40-
// TODO(mutabledocuments): Rename to ObjectValue once other ObjectValue class
41-
// is removed
42-
class MutableObjectValue {
40+
class ObjectValue {
4341
public:
44-
MutableObjectValue();
42+
ObjectValue();
4543

4644
/** Creates a new MutableObjectValue and takes ownership of `value`. */
47-
explicit MutableObjectValue(const google_firestore_v1_Value& value)
48-
: value_(value) {
49-
HARD_ASSERT(
50-
value.which_value_type == google_firestore_v1_Value_map_value_tag,
51-
"ObjectValues should be backed by a MapValue");
52-
}
45+
explicit ObjectValue(const google_firestore_v1_Value& value);
5346

54-
MutableObjectValue(MutableObjectValue&& other) noexcept = default;
55-
MutableObjectValue& operator=(MutableObjectValue&& other) = default;
47+
ObjectValue(ObjectValue&& other) noexcept = default;
48+
ObjectValue& operator=(ObjectValue&& other) noexcept = default;
49+
ObjectValue(const ObjectValue& other);
5650

57-
/** `MutableObjectValue` models unique ownership. */
58-
MutableObjectValue(const MutableObjectValue&) = delete;
59-
MutableObjectValue& operator=(const MutableObjectValue&) = delete;
51+
ObjectValue& operator=(const ObjectValue&) = delete;
52+
53+
/**
54+
* Creates a new ObjectValue that is backed by the given `map_value`.
55+
* ObjectValue takes on ownership of the data.
56+
*/
57+
static ObjectValue FromMapValue(google_firestore_v1_MapValue map_value);
58+
59+
/**
60+
* Creates a new ObjectValue that is backed by the provided document fields.
61+
* ObjectValue takes on ownership of the data and zeroes out the pointers in
62+
* `fields_entry`. This allows the callsite to destruct the Document proto
63+
* without affecting the fields data.
64+
*/
65+
static ObjectValue FromFieldsEntry(
66+
google_firestore_v1_Document_FieldsEntry* fields_entry, pb_size_t count);
6067

6168
/** Recursively extracts the FieldPaths that are set in this ObjectValue. */
6269
FieldMask ToFieldMask() const;
@@ -69,6 +76,11 @@ class MutableObjectValue {
6976
*/
7077
absl::optional<google_firestore_v1_Value> Get(const FieldPath& path) const;
7178

79+
/**
80+
* Returns the ObjectValue in its Protobuf representation.
81+
*/
82+
google_firestore_v1_Value Get() const;
83+
7284
/**
7385
* Sets the field to the provided value.
7486
*
@@ -85,7 +97,7 @@ class MutableObjectValue {
8597
* @param field_mask The field mask that controls which fields to modify.
8698
* @param data A MutableObjectValue that contains the field values.
8799
*/
88-
void SetAll(const FieldMask& field_mask, const MutableObjectValue& data);
100+
void SetAll(const FieldMask& field_mask, const ObjectValue& data);
89101

90102
/**
91103
* Removes the field at the specified path. If there is no field at the
@@ -95,10 +107,13 @@ class MutableObjectValue {
95107
*/
96108
void Delete(const FieldPath& path);
97109

98-
friend bool operator==(const MutableObjectValue& lhs,
99-
const MutableObjectValue& rhs);
110+
std::string ToString() const;
111+
112+
size_t Hash() const;
113+
114+
friend bool operator==(const ObjectValue& lhs, const ObjectValue& rhs);
100115
friend std::ostream& operator<<(std::ostream& out,
101-
const MutableObjectValue& object_value);
116+
const ObjectValue& object_value);
102117

103118
private:
104119
/** Returns the field mask for the provided map value. */
@@ -113,13 +128,12 @@ class MutableObjectValue {
113128
nanopb::Message<google_firestore_v1_Value> value_;
114129
};
115130

116-
inline bool operator==(const MutableObjectValue& lhs,
117-
const MutableObjectValue& rhs) {
131+
inline bool operator==(const ObjectValue& lhs, const ObjectValue& rhs) {
118132
return *lhs.value_ == *rhs.value_;
119133
}
120134

121135
inline std::ostream& operator<<(std::ostream& out,
122-
const MutableObjectValue& object_value) {
136+
const ObjectValue& object_value) {
123137
return out << "ObjectValue(" << *object_value.value_ << ")";
124138
}
125139

0 commit comments

Comments
 (0)