From 359569db6847af304abe16708152bee11fbd1df1 Mon Sep 17 00:00:00 2001 From: Lee Maguire Date: Tue, 16 Jul 2024 14:24:06 +0100 Subject: [PATCH] Add contains_key to managed maps (#230) --- CHANGELOG.md | 2 ++ include/cpprealm/internal/bridge/query.hpp | 1 + include/cpprealm/managed_dictionary.hpp | 12 ++++++++++++ include/cpprealm/rbool.hpp | 5 +++++ src/cpprealm/internal/bridge/query.cpp | 9 +++++++++ tests/db/map_tests.cpp | 16 ++++++++++++++++ tests/db/query_tests.cpp | 6 ++++++ 7 files changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 706a684e..41dc2be7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ NEXT-RELEASE Release notes (YYYY-MM-DD) * Add ability to use `managed>` in type safe queries when comparing a value for a key. e.g. `realm.object().where([](auto& o) { return o.my_map["foo_key"] == "some value"; })` Supported operators are `==`, `!=`, `>`, `<`, `>=`, `<=` and `contains(const std::string&)`. +* Add `managed>::contains_key` for conveniently checking if a managed map + contains a given key. Use this method in the Type Safe Query API instead of `managed>::find`. ### Compatibility * Fileformat: Generates files with format v24. Reads and automatically upgrade from fileformat v10. diff --git a/include/cpprealm/internal/bridge/query.hpp b/include/cpprealm/internal/bridge/query.hpp index de8b4c7b..3e360cad 100644 --- a/include/cpprealm/internal/bridge/query.hpp +++ b/include/cpprealm/internal/bridge/query.hpp @@ -239,6 +239,7 @@ namespace realm::internal::bridge { query& dictionary_has_value_for_key_greater_than_equals(col_key column_key, const std::string& key, const mixed& value); query& dictionary_has_value_for_key_less_than_equals(col_key column_key, const std::string& key, const mixed& value); query& dictionary_contains_string_for_key(col_key column_key, const std::string& key, const std::string& value); + query& dictionary_contains_key(col_key column_key, const std::string& key); subexpr dictionary_link_subexpr(col_key column_key, col_key link_column_key, const std::string& key); // Expressions diff --git a/include/cpprealm/managed_dictionary.hpp b/include/cpprealm/managed_dictionary.hpp index f272bc67..db13fb53 100644 --- a/include/cpprealm/managed_dictionary.hpp +++ b/include/cpprealm/managed_dictionary.hpp @@ -599,6 +599,9 @@ namespace realm { } iterator find(const std::string& key) { + if (m_rbool_query) { + throw std::runtime_error("`find` is not available in Type Safe Queries, use `contains_key` instead."); + } // Dictionary's `find` searches for the index of the value and not the key. auto d = m_obj->get_dictionary(m_key); auto i = d.find_any_key(key); @@ -627,6 +630,15 @@ namespace realm { m_obj->get_dictionary(m_key).erase(key); } + /// Convenience method to be primarily used in type safe queries. + rbool contains_key(const std::string& key) { + if (m_rbool_query) { + return m_rbool_query->dictionary_has_key(m_key, key); + } else { + return m_obj->get_dictionary(m_key).find_any_key(key) != size_t(-1); + } + } + notification_token observe(std::function&& fn) { auto o = internal::bridge::object(*m_realm, *m_obj); diff --git a/include/cpprealm/rbool.hpp b/include/cpprealm/rbool.hpp index 1a659872..8a146cf3 100644 --- a/include/cpprealm/rbool.hpp +++ b/include/cpprealm/rbool.hpp @@ -236,6 +236,11 @@ namespace realm { return *this; } + rbool& dictionary_has_key(internal::bridge::col_key column_key, const std::string& key) { + q = internal::bridge::query(q.get_table()).dictionary_contains_key(column_key, key); + return *this; + } + ~rbool() { if (is_for_queries) q.~query(); diff --git a/src/cpprealm/internal/bridge/query.cpp b/src/cpprealm/internal/bridge/query.cpp index e0b7a934..1cd537df 100644 --- a/src/cpprealm/internal/bridge/query.cpp +++ b/src/cpprealm/internal/bridge/query.cpp @@ -671,6 +671,15 @@ namespace realm::internal::bridge { return *this; } + query& query::dictionary_contains_key(col_key column_key, const std::string& key) { +#ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES + *reinterpret_cast(&m_query) = reinterpret_cast(&m_query)->get_table()->column(column_key.operator ColKey()).keys().contains(key); +#else + m_query = std::make_shared(m_query->get_table()->column(column_key.operator ColKey()).keys().contains(key)); +#endif + return *this; + } + subexpr query::dictionary_link_subexpr(col_key column_key, col_key link_column_key, const std::string& key) { #ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES auto table = reinterpret_cast(&m_query)->get_table()->column(column_key.operator ColKey()).key(key).get_target_table(); diff --git a/tests/db/map_tests.cpp b/tests/db/map_tests.cpp index fb45f460..119c7c36 100644 --- a/tests/db/map_tests.cpp +++ b/tests/db/map_tests.cpp @@ -207,6 +207,22 @@ TEST_CASE("map", "[map]") { CHECK(as_values == std::map({{"a", std::string("baz")}, {"b", std::string("foo")}})); } + SECTION("contains_key") { + auto obj = AllTypesObject(); + obj.map_str_col = { + {"a", std::string("baz")}, + {"b", std::string("foo")} + }; + + auto realm = db(std::move(config)); + auto managed_obj = realm.write([&realm, &obj] { + return realm.add(std::move(obj)); + }); + + CHECK(managed_obj.map_str_col.contains_key("a")); + CHECK_FALSE(managed_obj.map_str_col.contains_key("c")); + } + SECTION("object lifetime") { managed ptr; { diff --git a/tests/db/query_tests.cpp b/tests/db/query_tests.cpp index b478c7b1..2f1185f3 100644 --- a/tests/db/query_tests.cpp +++ b/tests/db/query_tests.cpp @@ -589,6 +589,12 @@ namespace realm { // Test non existent key CHECK(do_query([](realm::managed& o) -> rbool { return o.map_int_col["NA"] == 1; }) == 0); + + // Check key exists in dictionary + CHECK(do_query([](realm::managed& o) -> rbool { return o.map_int_col.contains_key("one"); }) == 3); + CHECK(do_query([](realm::managed& o) -> rbool { return o.map_int_col.contains_key("three"); }) == 0); + CHECK(do_query([](realm::managed& o) -> rbool { return o.map_int_col.contains_key("one"); }) != 0); + CHECK(do_query([](realm::managed& o) -> rbool { return o.map_int_col.contains_key("three"); }) != 3); } } }