diff --git a/.gitignore b/.gitignore index 86244e6d9..e7d5d1eb1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,5 @@ Package.resolved examples/*.pro.user docs/html docs/latex -.idea -realm-core/src/realm/parser/generated \ No newline at end of file +.idea/ +realm-core/src/realm/parser/generated/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index b3988e494..000000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -RealmCxx \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 24025e857..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123c2..000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/deployment.xml b/.idea/deployment.xml deleted file mode 100644 index aa03c8f8c..000000000 --- a/.idea/deployment.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 79b3c9483..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index f3cff116d..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/realm-cpp-sdk.iml b/.idea/realm-cpp-sdk.iml deleted file mode 100644 index f08604bb6..000000000 --- a/.idea/realm-cpp-sdk.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 1f05e07a9..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e437afc15..b30336ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -X.Y.Z Release notes (YYYY-MM-DD) +NEXT RELEASE Release notes (YYYY-MM-DD) ============================================================= ### Fixed @@ -8,6 +8,8 @@ X.Y.Z Release notes (YYYY-MM-DD) * Add `realm::default_scheduler::set_default_factory(std::function()>&& factory_fn)` for generating a default scheduler. Set your scheduler factory before instantiating a `realm::db_config`. * Add `realm::default_scheduler::make_default()` which generates a platform default scheduler if `realm::default_scheduler::set_default_factory` is not set. +* Add `managed::to_json(std::ostream&)` which allows managed objects to be printed as json. +* Add `rbool::truepredicate()` and `rbool::falsepredicate()` expressions for type safe queries. ### Compatibility * Fileformat: Generates files with format v24. Reads and automatically upgrade from fileformat v10. diff --git a/include/cpprealm/internal/bridge/obj.hpp b/include/cpprealm/internal/bridge/obj.hpp index 64166b725..c2b256f6c 100644 --- a/include/cpprealm/internal/bridge/obj.hpp +++ b/include/cpprealm/internal/bridge/obj.hpp @@ -226,7 +226,7 @@ namespace realm::internal::bridge { void set_null(const col_key&); obj create_and_set_linked_object(const col_key&); table_view get_backlink_view(table, col_key); - + void to_json(std::ostream& out) const noexcept; private: inline const Obj* get_obj() const; inline Obj* get_obj(); diff --git a/include/cpprealm/internal/bridge/query.hpp b/include/cpprealm/internal/bridge/query.hpp index f19141e9b..569c91ba5 100644 --- a/include/cpprealm/internal/bridge/query.hpp +++ b/include/cpprealm/internal/bridge/query.hpp @@ -232,6 +232,9 @@ namespace realm::internal::bridge { query& links_to(col_key column_key, const internal::bridge::obj& o); query& not_links_to(col_key column_key, const internal::bridge::obj& o); + // Expressions + static query falsepredicate(); + std::string description() const; private: inline Query* get_query(); @@ -243,11 +246,6 @@ namespace realm::internal::bridge { }; - template - using QFn = query& (query::*)(col_key, T); - template - using QFnCS = query& (query::*)(col_key, T, bool); - query operator || (const query& lhs, const query& rhs); } diff --git a/include/cpprealm/macros.hpp b/include/cpprealm/macros.hpp index 059d11c65..dfb8b6034 100644 --- a/include/cpprealm/macros.hpp +++ b/include/cpprealm/macros.hpp @@ -544,7 +544,9 @@ rbool managed>::operator op(const std::optional& rhs) db get_realm() { \ return db(m_realm); \ } \ - bool operator ==(const managed& other) const { \ + bool operator ==(const managed& other) const { \ + if (m_rbool_query != nullptr) \ + throw std::runtime_error("This comparison operator is not valid inside of `where`"); \ auto& a = m_obj; \ auto& b = other.m_obj; \ if (m_realm != other.m_realm) { \ @@ -554,6 +556,8 @@ rbool managed>::operator op(const std::optional& rhs) && a.get_key() == b.get_key(); \ } \ bool operator ==(const managed& other) const { \ + if (m_rbool_query != nullptr) \ + throw std::runtime_error("This comparison operator is not valid inside of `where`"); \ auto& a = m_obj; \ auto& b = other.m_obj; \ if (m_realm != other->m_realm) { \ @@ -571,6 +575,9 @@ rbool managed>::operator op(const std::optional& rhs) bool operator < (const managed& rhs) const { \ return m_obj.get_key() < rhs.m_obj.get_key(); \ } \ + void to_json(std::ostream& out) const noexcept { \ + m_obj.to_json(out); \ + } \ private: \ internal::bridge::obj m_obj; \ internal::bridge::realm m_realm; \ @@ -579,6 +586,8 @@ rbool managed>::operator op(const std::optional& rhs) template friend struct managed; \ template friend struct box; \ template friend struct ::realm::thread_safe_reference; \ + template friend rbool* ::realm::internal::get_rbool(const T&); \ + \ }; \ struct meta_schema_##cls { \ meta_schema_##cls() { \ @@ -590,4 +599,15 @@ rbool managed>::operator op(const std::optional& rhs) }; \ static inline meta_schema_##cls _meta_schema_##cls{}; +namespace realm::internal { + /* + * Helper method for extracting the private `m_rbool_query` + * property on a managed object. + */ + template + rbool* get_rbool(const T& o) { + return o.m_rbool_query; + } +} + #endif //CPPREALM_MACROS_HPP diff --git a/include/cpprealm/rbool.hpp b/include/cpprealm/rbool.hpp index 6b2c92f83..ecfe39c85 100644 --- a/include/cpprealm/rbool.hpp +++ b/include/cpprealm/rbool.hpp @@ -38,14 +38,11 @@ namespace realm { std::optional m_link_chain; internal::bridge::table m_table; - friend rbool operator&&(const rbool &lhs, const rbool &rhs); - template friend struct results; - + friend rbool operator&&(const rbool &lhs, const rbool &rhs); friend rbool operator||(const rbool &lhs, const rbool &rhs); public: - rbool& add_link_chain(const internal::bridge::col_key& col_key) { if (m_link_chain) { m_link_chain->link(col_key); @@ -237,6 +234,29 @@ namespace realm { } return lhs.b && rhs.b; } + + /// Return all objects from a collection. + template + inline rbool truepredicate(const T& o) { + // An empty query returns all results and one way to indicate this + // is to serialise TRUEPREDICATE which is functionally equivalent + rbool* rb = internal::get_rbool(o); + if (rb == nullptr) + throw std::runtime_error("Managed object is not used in a query context"); + auto table = rb->q.get_table(); + return rbool(table); + } + +/// Return no objects from a collection. + template + inline rbool falsepredicate(const T& o) { + rbool* rb = internal::get_rbool(o); + if (rb == nullptr) + throw std::runtime_error("Managed object is not used in a query context"); + auto table = rb->q.get_table(); + auto q = internal::bridge::query(table).and_query(internal::bridge::query(table).falsepredicate()); + return rbool(std::move(q)); + } } #endif //CPPREALM_RBOOL_HPP diff --git a/src/cpprealm/internal/bridge/obj.cpp b/src/cpprealm/internal/bridge/obj.cpp index 4e8d98395..c6c5033be 100644 --- a/src/cpprealm/internal/bridge/obj.cpp +++ b/src/cpprealm/internal/bridge/obj.cpp @@ -406,6 +406,10 @@ namespace realm::internal::bridge { table_view obj::get_backlink_view(table table, col_key col_key) { return get_obj()->get_backlink_view(table, col_key); } + + void obj::to_json(std::ostream& out) const noexcept { + return get_obj()->to_json(out); + } } std::string realm::internal::bridge::table_name_for_object_type(const std::string &v) { diff --git a/src/cpprealm/internal/bridge/query.cpp b/src/cpprealm/internal/bridge/query.cpp index 2252de805..4500300c9 100644 --- a/src/cpprealm/internal/bridge/query.cpp +++ b/src/cpprealm/internal/bridge/query.cpp @@ -595,6 +595,10 @@ namespace realm::internal::bridge { return *this; } + query query::falsepredicate() { + return query(Query(std::make_unique())); + } + std::string query::description() const { #ifdef CPPREALM_HAVE_GENERATED_BRIDGE_TYPES return reinterpret_cast(&m_query)->get_description(); diff --git a/tests/db/object_tests.cpp b/tests/db/object_tests.cpp index aae60ae65..b1b3d1911 100644 --- a/tests/db/object_tests.cpp +++ b/tests/db/object_tests.cpp @@ -1,6 +1,8 @@ #include "../main.hpp" #include "test_objects.hpp" +#include + namespace realm { enum class PrimaryKeyEnum { @@ -1035,6 +1037,22 @@ namespace realm { CHECK(realm.objects().size() == 1); } + SECTION("to_json") { + StringObject str_obj; + str_obj.str_col = "test"; + str_obj._id = 123; + auto realm = db(std::move(config)); + + auto managed_obj = realm.write([&](){ + return realm.add(std::move(str_obj)); + }); + + std::stringstream ss; + managed_obj.to_json(ss); + auto json_str = ss.str(); + CHECK(json_str == "{\"_id\":123,\"str_col\":\"test\"}"); + } + static_assert(!std::is_constructible_v::ref_type>, "Default constructor is private."); static_assert(!std::is_constructible_v>, "Default constructor is private."); static_assert(!std::is_constructible_v>, "Default constructor is private."); diff --git a/tests/db/query_tests.cpp b/tests/db/query_tests.cpp index 32ac55ee3..4abe4b5cc 100644 --- a/tests/db/query_tests.cpp +++ b/tests/db/query_tests.cpp @@ -329,5 +329,84 @@ namespace realm { }); CHECK(res.size() == 3); } + + SECTION("TRUEPREDICATE_FALSEPREDICATE") { + auto realm = db(std::move(config)); + + auto create_obj = [&](int64_t pk) { + auto obj = AllTypesObject(); + obj._id = pk; + obj.str_col = "root obj"; + auto obj_link = AllTypesObjectLink(); + obj_link._id = pk; + obj_link.str_col = "foo"; + auto obj_link2 = StringObject(); + obj_link2._id = pk; + obj_link2.str_col = "bar"; + + obj.opt_obj_col = &obj_link; + obj_link.str_link_col = &obj_link2; + + return realm.write([&]() { + return realm.add(std::move(obj)); + }); + }; + + auto managed_obj = create_obj(0); + auto managed_obj2 = create_obj(1); + auto managed_obj3 = create_obj(2); + + auto res = realm.objects().where([](auto& o) { + return truepredicate(o); + }); + CHECK(res.size() == 3); + + res = realm.objects().where([](auto& o) { + return falsepredicate(o); + }); + CHECK(res.size() == 0); + } + + SECTION("sub results") { + auto realm = db(std::move(config)); + + auto create_obj = [&](int64_t pk) { + auto obj = AllTypesObject(); + obj._id = pk; + obj.str_col = "root obj"; + auto obj_link = AllTypesObjectLink(); + obj_link._id = pk; + obj_link.str_col = "foo"; + auto obj_link2 = StringObject(); + obj_link2._id = pk; + obj_link2.str_col = "bar"; + + obj.opt_obj_col = &obj_link; + obj_link.str_link_col = &obj_link2; + + return realm.write([&]() { + return realm.add(std::move(obj)); + }); + }; + + auto managed_obj = create_obj(0); + auto managed_obj2 = create_obj(1); + auto managed_obj3 = create_obj(2); + + auto res = realm.objects().where([](auto& o) { + return o._id > 0; + }); + CHECK(res.size() == 2); + + res = res.where([](auto& o) { + return o._id > 1; + }); + CHECK(res.size() == 1); + + res = res.where([](auto& o) { + return truepredicate(o); + }); + CHECK(res.size() == 3); + } } -} \ No newline at end of file +}