diff --git a/include/_mgp.hpp b/include/_mgp.hpp index 152bb71f9..a98303905 100644 --- a/include/_mgp.hpp +++ b/include/_mgp.hpp @@ -335,6 +335,12 @@ inline void map_insert(mgp_map *map, const char *key, mgp_value *value) { MgInvokeVoid(mgp_map_insert, map, key, value); } +inline void map_update(mgp_map *map, const char *key, mgp_value *value) { + MgInvokeVoid(mgp_map_update, map, key, value); +} + +inline void map_erase(mgp_map *map, const char *key) { MgInvokeVoid(mgp_map_erase, map, key); } + inline size_t map_size(mgp_map *map) { return MgInvoke(mgp_map_size, map); } inline mgp_value *map_at(mgp_map *map, const char *key) { return MgInvoke(mgp_map_at, map, key); } diff --git a/include/mg_procedure.h b/include/mg_procedure.h index f92b0cf16..19306655f 100644 --- a/include/mg_procedure.h +++ b/include/mg_procedure.h @@ -462,6 +462,18 @@ void mgp_map_destroy(struct mgp_map *map); /// Return mgp_error::MGP_ERROR_KEY_ALREADY_EXISTS if a previous mapping already exists. enum mgp_error mgp_map_insert(struct mgp_map *map, const char *key, struct mgp_value *value); +/// Insert a mapping from a NULL terminated character string to a value. +/// If a mapping with the same key already exists, it is replaced. +/// In case of update, both the string and the value are copied into the map. +/// Therefore, the map does not take ownership of the original key nor value, so +/// you still need to free their memory explicitly. +/// Return mgp_error::MGP_ERROR_UNABLE_TO_ALLOCATE is returned if unable to allocate for insertion. +enum mgp_error mgp_map_update(struct mgp_map *map, const char *key, struct mgp_value *value); + +// Erase a mapping by key. +// If the key doesn't exist in the map nothing happens +enum mgp_error mgp_map_erase(struct mgp_map *map, const char *key); + /// Get the number of items stored in mgp_map. /// Current implementation always returns without errors. enum mgp_error mgp_map_size(struct mgp_map *map, size_t *result); diff --git a/include/mgp.hpp b/include/mgp.hpp index 144133e4b..7b5c9c65f 100644 --- a/include/mgp.hpp +++ b/include/mgp.hpp @@ -464,6 +464,7 @@ class Map { public: /// @brief Creates a Map from the copy of the given @ref mgp_map. explicit Map(mgp_map *ptr); + /// @brief Creates a Map from the copy of the given @ref mgp_map. explicit Map(const mgp_map *const_ptr); @@ -472,6 +473,7 @@ class Map { /// @brief Creates a Map from the given vector. explicit Map(const std::map &items); + /// @brief Creates a Map from the given vector. explicit Map(std::map &&items); @@ -488,11 +490,13 @@ class Map { /// @brief Returns the size of the map. size_t Size() const; + /// @brief Returns whether the map is empty. bool Empty() const; /// @brief Returns the value at the given `key`. Value const operator[](std::string_view key) const; + /// @brief Returns the value at the given `key`. Value const At(std::string_view key) const; @@ -533,16 +537,30 @@ class Map { /// @brief Inserts the given `key`-`value` pair into the map. The `value` is copied. void Insert(std::string_view key, const Value &value); + /// @brief Inserts the given `key`-`value` pair into the map. /// @note Takes the ownership of `value` by moving it. The behavior of accessing `value` after performing this /// operation is undefined. void Insert(std::string_view key, Value &&value); - // void Erase(std::string_view key); // not implemented (requires mgp_map_erase in the MGP API) + /// @brief Updates the `key`-`value` pair in the map. If the key doesn't exist, the value gets inserted. The `value` + /// is copied. + void Update(std::string_view key, const Value &value); + + /// @brief Updates the `key`-`value` pair in the map. If the key doesn't exist, the value gets inserted. The `value` + /// is copied. + /// @note Takes the ownership of `value` by moving it. The behavior of accessing `value` after performing this + /// operation is undefined. + void Update(std::string_view key, Value &&value); + + /// @brief Erases the element associated with the key from the map, if it doesn't exist does nothing. + void Erase(std::string_view key); + // void Clear(); // not implemented (requires mgp_map_clear in the MGP API) /// @exception std::runtime_error Map contains value of unknown type. bool operator==(const Map &other) const; + /// @exception std::runtime_error Map contains value of unknown type. bool operator!=(const Map &other) const; @@ -2390,9 +2408,20 @@ inline void Map::Insert(std::string_view key, const Value &value) { mgp::map_ins inline void Map::Insert(std::string_view key, Value &&value) { mgp::map_insert(ptr_, key.data(), value.ptr_); + value.~Value(); value.ptr_ = nullptr; } +inline void Map::Update(std::string_view key, const Value &value) { mgp::map_update(ptr_, key.data(), value.ptr_); } + +inline void Map::Update(std::string_view key, Value &&value) { + mgp::map_update(ptr_, key.data(), value.ptr_); + value.~Value(); + value.ptr_ = nullptr; +} + +inline void Map::Erase(std::string_view key) { mgp::map_erase(ptr_, key.data()); } + inline bool Map::operator==(const Map &other) const { return util::MapsEqual(ptr_, other.ptr_); } inline bool Map::operator!=(const Map &other) const { return !(*this == other); } @@ -3438,7 +3467,6 @@ inline std::ostream &operator<<(std::ostream &os, const mgp::Type &type) { } } - /* #endregion */ /* #region Record */ diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp index 9309b2b4b..6bc6a3f67 100644 --- a/src/query/procedure/mg_procedure_impl.cpp +++ b/src/query/procedure/mg_procedure_impl.cpp @@ -1043,6 +1043,25 @@ mgp_error mgp_map_insert(mgp_map *map, const char *key, mgp_value *value) { }); } +mgp_error mgp_map_update(mgp_map *map, const char *key, mgp_value *value) { + return WrapExceptions([&] { + auto emplace_result = map->items.emplace(key, *value); + if (!emplace_result.second) { + map->items.erase(emplace_result.first); + map->items.emplace(key, *value); + } + }); +} + +mgp_error mgp_map_erase(mgp_map *map, const char *key) { + return WrapExceptions([&] { + auto iterator = map->items.find(key); + if (iterator != map->items.end()) { + map->items.erase(iterator); + } + }); +} + mgp_error mgp_map_size(mgp_map *map, size_t *result) { static_assert(noexcept(map->items.size())); *result = map->items.size(); diff --git a/tests/unit/cpp_api.cpp b/tests/unit/cpp_api.cpp index 3e47ff782..68c61a436 100644 --- a/tests/unit/cpp_api.cpp +++ b/tests/unit/cpp_api.cpp @@ -531,3 +531,34 @@ TYPED_TEST(CppApiTestFixture, TestTypeOperatorStream) { ASSERT_EQ(int_test, "int"); ASSERT_EQ(list_test, "list"); } + +TYPED_TEST(CppApiTestFixture, TestMapUpdate) { + mgp::Map map{}; + mgp::Value double_1{1.0}; + mgp::Value double_2{2.0}; + + map.Update("1", double_1); + ASSERT_EQ(map.At("1"), double_1); + + map.Update("1", double_2); + ASSERT_EQ(map.At("1"), double_2); +} + +TYPED_TEST(CppApiTestFixture, TestMapErase) { + mgp::Map map{}; + mgp::Value double_1{1.0}; + mgp::Value double_2{2.0}; + + map.Insert("1", double_1); + map.Insert("2", double_2); + ASSERT_EQ(map.Size(), 2); + + map.Erase("1"); + ASSERT_EQ(map.Size(), 1); + + map.Erase("1"); + ASSERT_EQ(map.Size(), 1); + + map.Erase("2"); + ASSERT_EQ(map.Size(), 0); +}