From 12d9358c96d8de816254992b20d263e8ad9aac63 Mon Sep 17 00:00:00 2001 From: zxu Date: Mon, 23 Apr 2018 10:51:52 -0400 Subject: Update `FieldValue` For Porting `Mutation`s to C++ (#1144) * update FieldValue for Mutation implementation * address changes * address changes * address change --- .../src/firebase/firestore/model/CMakeLists.txt | 1 + .../core/src/firebase/firestore/model/document.h | 6 ++ .../src/firebase/firestore/model/field_value.cc | 91 +++++++++++++++++++++- .../src/firebase/firestore/model/field_value.h | 41 +++++++++- .../src/firebase/firestore/model/maybe_document.h | 4 + 5 files changed, 137 insertions(+), 6 deletions(-) (limited to 'Firestore/core/src/firebase/firestore/model') diff --git a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt index 02affdb..cf43d7a 100644 --- a/Firestore/core/src/firebase/firestore/model/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/model/CMakeLists.txt @@ -41,6 +41,7 @@ cc_library( transform_operations.h types.h DEPENDS + absl_optional absl_strings firebase_firestore_util firebase_firestore_types diff --git a/Firestore/core/src/firebase/firestore/model/document.h b/Firestore/core/src/firebase/firestore/model/document.h index 50a7b90..1b7cc13 100644 --- a/Firestore/core/src/firebase/firestore/model/document.h +++ b/Firestore/core/src/firebase/firestore/model/document.h @@ -17,8 +17,10 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_DOCUMENT_H_ +#include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/model/field_value.h" #include "Firestore/core/src/firebase/firestore/model/maybe_document.h" +#include "absl/types/optional.h" namespace firebase { namespace firestore { @@ -42,6 +44,10 @@ class Document : public MaybeDocument { return data_; } + absl::optional field(const FieldPath& path) const { + return data_.Get(path); + } + bool has_local_mutations() const { return has_local_mutations_; } diff --git a/Firestore/core/src/firebase/firestore/model/field_value.cc b/Firestore/core/src/firebase/firestore/model/field_value.cc index b0519f7..9b3acad 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.cc +++ b/Firestore/core/src/firebase/firestore/model/field_value.cc @@ -55,6 +55,19 @@ bool Comparable(Type lhs, Type rhs) { } } +// Makes a copy excluding the specified child, which is expected to be assigned +// different value afterwards. +ObjectValue::Map CopyExcept(const ObjectValue::Map& object_map, + const std::string exclude) { + ObjectValue::Map copy; + for (const auto& kv : object_map) { + if (kv.first != exclude) { + copy[kv.first] = kv.second; + } + } + return copy; +} + } // namespace FieldValue::FieldValue(const FieldValue& value) { @@ -153,6 +166,81 @@ FieldValue& FieldValue::operator=(FieldValue&& value) { } } +FieldValue FieldValue::Set(const FieldPath& field_path, + FieldValue value) const { + FIREBASE_ASSERT_MESSAGE(type() == Type::Object, + "Cannot set field for non-object FieldValue"); + FIREBASE_ASSERT_MESSAGE(!field_path.empty(), + "Cannot set field for empty path on FieldValue"); + // Set the value by recursively calling on child object. + const std::string& child_name = field_path.first_segment(); + const ObjectValue::Map& object_map = object_value_.internal_value; + if (field_path.size() == 1) { + // TODO(zxu): Once immutable type is available, rewrite these. + ObjectValue::Map copy = CopyExcept(object_map, child_name); + copy[child_name] = std::move(value); + return FieldValue::ObjectValueFromMap(std::move(copy)); + } else { + ObjectValue::Map copy = CopyExcept(object_map, child_name); + const auto iter = object_map.find(child_name); + if (iter == copy.end() || iter->second.type() != Type::Object) { + copy[child_name] = FieldValue::ObjectValueFromMap({}).Set( + field_path.PopFirst(), std::move(value)); + } else { + copy[child_name] = object_map.at(child_name) + .Set(field_path.PopFirst(), std::move(value)); + } + return FieldValue::ObjectValueFromMap(std::move(copy)); + } +} + +FieldValue FieldValue::Delete(const FieldPath& field_path) const { + FIREBASE_ASSERT_MESSAGE(type() == Type::Object, + "Cannot delete field for non-object FieldValue"); + FIREBASE_ASSERT_MESSAGE(!field_path.empty(), + "Cannot delete field for empty path on FieldValue"); + // Delete the value by recursively calling on child object. + const std::string& child_name = field_path.first_segment(); + const ObjectValue::Map& object_map = object_value_.internal_value; + if (field_path.size() == 1) { + // TODO(zxu): Once immutable type is available, rewrite these. + ObjectValue::Map copy = CopyExcept(object_map, child_name); + return FieldValue::ObjectValueFromMap(std::move(copy)); + } else { + const auto iter = object_map.find(child_name); + if (iter == object_map.end() || iter->second.type() != Type::Object) { + // If the found value isn't an object, it cannot contain the remaining + // segments of the path. We don't actually change a primitive value to + // an object for a delete. + return *this; + } else { + ObjectValue::Map copy = CopyExcept(object_map, child_name); + copy[child_name] = + object_map.at(child_name).Delete(field_path.PopFirst()); + return FieldValue::ObjectValueFromMap(std::move(copy)); + } + } +} + +absl::optional FieldValue::Get(const FieldPath& field_path) const { + FIREBASE_ASSERT_MESSAGE(type() == Type::Object, + "Cannot get field for non-object FieldValue"); + const FieldValue* current = this; + for (const auto& path : field_path) { + if (current->type() != Type::Object) { + return absl::nullopt; + } + const ObjectValue::Map& object_map = current->object_value_.internal_value; + const auto iter = object_map.find(path); + if (iter == object_map.end()) { + return absl::nullopt; + } else { + current = &iter->second; + } + } + return *current; +} + const FieldValue& FieldValue::NullValue() { static const FieldValue kNullInstance; return kNullInstance; @@ -204,7 +292,6 @@ FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time, result.SwitchTo(Type::ServerTimestamp); result.server_timestamp_value_.local_write_time = local_write_time; result.server_timestamp_value_.previous_value = previous_value; - result.server_timestamp_value_.has_previous_value_ = true; return result; } @@ -212,7 +299,7 @@ FieldValue FieldValue::ServerTimestampValue(const Timestamp& local_write_time) { FieldValue result; result.SwitchTo(Type::ServerTimestamp); result.server_timestamp_value_.local_write_time = local_write_time; - result.server_timestamp_value_.has_previous_value_ = false; + result.server_timestamp_value_.previous_value = absl::nullopt; return result; } diff --git a/Firestore/core/src/firebase/firestore/model/field_value.h b/Firestore/core/src/firebase/firestore/model/field_value.h index 3c5af9c..0f7dfc4 100644 --- a/Firestore/core/src/firebase/firestore/model/field_value.h +++ b/Firestore/core/src/firebase/firestore/model/field_value.h @@ -27,7 +27,9 @@ #include "Firestore/core/include/firebase/firestore/timestamp.h" #include "Firestore/core/src/firebase/firestore/model/database_id.h" #include "Firestore/core/src/firebase/firestore/model/document_key.h" +#include "Firestore/core/src/firebase/firestore/model/field_path.h" #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" +#include "absl/types/optional.h" namespace firebase { namespace firestore { @@ -35,9 +37,7 @@ namespace model { struct ServerTimestamp { Timestamp local_write_time; - Timestamp previous_value; - // TODO(zxu123): adopt absl::optional once abseil is ported. - bool has_previous_value_; + absl::optional previous_value; }; struct ReferenceValue { @@ -118,16 +118,49 @@ class FieldValue { return integer_value_; } + Timestamp timestamp_value() const { + FIREBASE_ASSERT(tag_ == Type::Timestamp); + return timestamp_value_; + } + const std::string& string_value() const { FIREBASE_ASSERT(tag_ == Type::String); return string_value_; } - const ObjectValue object_value() const { + ObjectValue object_value() const { FIREBASE_ASSERT(tag_ == Type::Object); return ObjectValue{object_value_}; } + /** + * Returns a FieldValue with the field at the named path set to value. + * Any absent parent of the field will also be created accordingly. + * + * @param field_path The field path to set. Cannot be empty. + * @param value The value to set. + * @return A new FieldValue with the field set. + */ + FieldValue Set(const FieldPath& field_path, FieldValue value) const; + + /** + * Returns a FieldValue with the field path deleted. If there is no field at + * the specified path, the returned value is an identical copy. + * + * @param field_path The field path to remove. Cannot be empty. + * @return A new FieldValue with the field path removed. + */ + FieldValue Delete(const FieldPath& field_path) const; + + /** + * Returns the value at the given path or absl::nullopt. If the path is empty, + * an identical copy of the FieldValue is returned. + * + * @param field_path the path to search. + * @return The value at the path or absl::nullopt if it doesn't exist. + */ + absl::optional Get(const FieldPath& field_path) const; + /** factory methods. */ static const FieldValue& NullValue(); static const FieldValue& TrueValue(); diff --git a/Firestore/core/src/firebase/firestore/model/maybe_document.h b/Firestore/core/src/firebase/firestore/model/maybe_document.h index 71bd3ef..c2ffb86 100644 --- a/Firestore/core/src/firebase/firestore/model/maybe_document.h +++ b/Firestore/core/src/firebase/firestore/model/maybe_document.h @@ -18,6 +18,7 @@ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_MAYBE_DOCUMENT_H_ #include +#include #include "Firestore/core/src/firebase/firestore/model/document_key.h" #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" @@ -44,6 +45,9 @@ class MaybeDocument { MaybeDocument(DocumentKey key, SnapshotVersion version); + virtual ~MaybeDocument() { + } + /** The runtime type of this document. */ Type type() const { return type_; -- cgit v1.2.3