From 68fd4505fbf2523c0116bf71fac6ec5eb0a5ac8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Edelbo?= Date: Wed, 24 Jan 2024 14:15:02 +0100 Subject: [PATCH 1/2] Support querying with @type on nested collections --- CHANGELOG.md | 3 ++- src/realm/query_value.cpp | 16 ++++++++++++++-- src/realm/query_value.hpp | 27 +++++++++++++++------------ test/test_parser.cpp | 12 ++++++++++-- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc77fc0b04e..d48badde176 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,9 +12,10 @@ * Queries on dictionaries in Mixed with @keys did not return correct result ([#7255](https://github.com/realm/realm-core/issues/7255), since 14.0.0-beta.0) * Changes to inner collections will not be reported by notifier on owning collection ([#7270](https://github.com/realm/realm-core/issues/7270), since 14.0.0-beta.0) * @count/@size not supported for mixed properties ([#7280](https://github.com/realm/realm-core/issues/7280), since v10.0.0) +* Query with @type does not support filtering on collections ([#7281](https://github.com/realm/realm-core/issues/7281), since 14.0.0-beta.0) ### Breaking changes -* None. +* If you want to query using @type operation, you must use 'objectlink' to match links to objects. 'object' is reserved for dictionary types. ### Compatibility * Fileformat: Generates files with format v24. Reads and automatically upgrade from fileformat v10. If you want to upgrade from an earlier file format version you will have to use RealmCore v13.x.y or earlier. diff --git a/src/realm/query_value.cpp b/src/realm/query_value.cpp index 52968420d1a..c0f575639dc 100644 --- a/src/realm/query_value.cpp +++ b/src/realm/query_value.cpp @@ -55,13 +55,17 @@ static const std::vector> attribu {"double", TypeOfValue::Double}, {"decimal128", TypeOfValue::Decimal128}, {"decimal", TypeOfValue::Decimal128}, - {"object", TypeOfValue::ObjectLink}, + {"objectlink", TypeOfValue::ObjectLink}, {"link", TypeOfValue::ObjectLink}, {"objectid", TypeOfValue::ObjectId}, {"uuid", TypeOfValue::UUID}, {"guid", TypeOfValue::UUID}, {"numeric", TypeOfValue::Numeric}, - {"bindata", TypeOfValue::Binary}}; + {"bindata", TypeOfValue::Binary}, + {"object", TypeOfValue::Object}, + {"array", TypeOfValue::Array}, + {"collection", TypeOfValue::Collection}, +}; TypeOfValue::Attribute get_single_from(std::string str) { @@ -112,6 +116,14 @@ TypeOfValue::Attribute attribute_from(DataType type) return TypeOfValue::Attribute::ObjectLink; case DataType::Type::UUID: return TypeOfValue::Attribute::UUID; + default: + if (type == type_Dictionary) { + return TypeOfValue::Attribute::Object; + } + if (type == type_List) { + return TypeOfValue::Attribute::Array; + } + break; } throw query_parser::InvalidQueryArgError( util::format("Invalid value '%1' cannot be converted to 'TypeOfValue'", type)); diff --git a/src/realm/query_value.hpp b/src/realm/query_value.hpp index 019f0dda9ae..5a21e08de5d 100644 --- a/src/realm/query_value.hpp +++ b/src/realm/query_value.hpp @@ -29,19 +29,22 @@ namespace realm { class TypeOfValue { public: enum Attribute { - Null = 1, - Int = 2, - Double = 4, - Float = 8, - Bool = 16, - Timestamp = 32, - String = 64, - Binary = 128, - UUID = 256, - ObjectId = 512, - Decimal128 = 1024, - ObjectLink = 2048, + Null = 0x0001, + Int = 0x0002, + Double = 0x0004, + Float = 0x0008, + Bool = 0x0010, + Timestamp = 0x0020, + String = 0x0040, + Binary = 0x0080, + UUID = 0x0100, + ObjectId = 0x0200, + Decimal128 = 0x0400, + ObjectLink = 0x0800, + Object = 0x1000, + Array = 0x2000, Numeric = Int + Double + Float + Decimal128, + Collection = Array + Object }; explicit TypeOfValue(int64_t attributes); explicit TypeOfValue(const std::string& attribute_tags); diff --git a/test/test_parser.cpp b/test/test_parser.cpp index e36eb07a8ee..7d5d7ef3927 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -4812,6 +4812,7 @@ TEST(Parser_TypeOfValue) } std::string bin_data("String2Binary"); table->get_object(15).set(col_any, Mixed()); + table->get_object(17).set_collection(col_any, CollectionType::Dictionary); table->get_object(75).set(col_any, Mixed(75.)); table->get_object(28).set(col_any, Mixed(BinaryData(bin_data))); nb_strings--; @@ -4837,7 +4838,7 @@ TEST(Parser_TypeOfValue) ++it; } } - size_t nb_ints = 71; + size_t nb_ints = 70; verify_query(test_context, table, "mixed.@type == 'string'", nb_strings); verify_query(test_context, table, "mixed.@type == 'double'", 2); verify_query(test_context, table, "mixed.@type == 'float'", 0); @@ -4861,7 +4862,7 @@ TEST(Parser_TypeOfValue) verify_query(test_context, table, "mixed.@type == 'char'", nb_ints); verify_query(test_context, table, "mixed.@type == 'timestamp'", 0); verify_query(test_context, table, "mixed.@type == 'datetimeoffset'", 0); - verify_query(test_context, table, "mixed.@type == 'object'", 0); + verify_query(test_context, table, "mixed.@type == 'object'", 1); verify_query(test_context, table, "mixed.@type == 'binary' || mixed.@type == 'DECIMAL' || mixed.@type == 'Double'", 4); @@ -5284,6 +5285,10 @@ TEST(Parser_NestedMixedDictionaryList) snake->insert("age", 20); } + Obj george = persons->create_object_with_primary_key("George"); + george.set(col_self, george.get_key()); + george.set_collection(col, CollectionType::List); + auto q = persons->column(col).path({"instruments", 0, "strings"}) == 6; CHECK_EQUAL(q.count(), 1); @@ -5300,6 +5305,9 @@ TEST(Parser_NestedMixedDictionaryList) verify_query(test_context, persons, "properties.@keys == 'tickets'", 1); verify_query(test_context, persons, "properties.@size == 3", 1); verify_query(test_context, persons, "properties.instruments.@size == 2", 1); + verify_query(test_context, persons, "properties.@type == 'object'", 2); + verify_query(test_context, persons, "properties.@type == 'array'", 1); + verify_query(test_context, persons, "properties.@type == 'collection'", 3); } TEST(Parser_NestedDictionaryDeep) From 794951aaa12d124f0be22f1ef42249d4eaac7f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20Edelbo?= Date: Thu, 25 Jan 2024 12:47:49 +0100 Subject: [PATCH 2/2] Fix test --- src/realm/query_value.cpp | 2 ++ test/test_parser.cpp | 29 +++++++++++++++++------------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/realm/query_value.cpp b/src/realm/query_value.cpp index c0f575639dc..34b1d65ef9a 100644 --- a/src/realm/query_value.cpp +++ b/src/realm/query_value.cpp @@ -63,7 +63,9 @@ static const std::vector> attribu {"numeric", TypeOfValue::Numeric}, {"bindata", TypeOfValue::Binary}, {"object", TypeOfValue::Object}, + {"dictionary", TypeOfValue::Object}, {"array", TypeOfValue::Array}, + {"list", TypeOfValue::Array}, {"collection", TypeOfValue::Collection}, }; diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 7d5d7ef3927..679b0a23d6c 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -4813,6 +4813,7 @@ TEST(Parser_TypeOfValue) std::string bin_data("String2Binary"); table->get_object(15).set(col_any, Mixed()); table->get_object(17).set_collection(col_any, CollectionType::Dictionary); + table->get_object(19).set(col_any, table->begin()->get_link()); table->get_object(75).set(col_any, Mixed(75.)); table->get_object(28).set(col_any, Mixed(BinaryData(bin_data))); nb_strings--; @@ -4838,7 +4839,8 @@ TEST(Parser_TypeOfValue) ++it; } } - size_t nb_ints = 70; + size_t nb_ints = 69; + size_t nb_numerics = nb_ints + 3; verify_query(test_context, table, "mixed.@type == 'string'", nb_strings); verify_query(test_context, table, "mixed.@type == 'double'", 2); verify_query(test_context, table, "mixed.@type == 'float'", 0); @@ -4863,15 +4865,17 @@ TEST(Parser_TypeOfValue) verify_query(test_context, table, "mixed.@type == 'timestamp'", 0); verify_query(test_context, table, "mixed.@type == 'datetimeoffset'", 0); verify_query(test_context, table, "mixed.@type == 'object'", 1); + verify_query(test_context, table, "mixed.@type == 'objectlink'", 1); verify_query(test_context, table, "mixed.@type == 'binary' || mixed.@type == 'DECIMAL' || mixed.@type == 'Double'", 4); verify_query(test_context, table, "mixed.@type == 'null'", 1); - verify_query(test_context, table, "mixed.@type == 'numeric'", table->size() - nb_strings - 2); - verify_query( - test_context, table, - "mixed.@type == 'numeric' || mixed.@type == 'string' || mixed.@type == 'binary' || mixed.@type == 'null'", - table->size()); + verify_query(test_context, table, "mixed.@type == 'numeric'", nb_numerics); + verify_query(test_context, table, + "mixed.@type == 'numeric' || mixed.@type == 'string' || mixed.@type == 'objectlink' || mixed.@type " + "== 'binary' || mixed.@type == " + "'object' || mixed.@type == 'null'", + table->size()); verify_query(test_context, table, "mixed.@type == mixed.@type", table->size()); verify_query(test_context, origin, "link.mixed.@type == 'numeric' || link.mixed.@type == 'string'", origin->size()); @@ -4879,9 +4883,9 @@ TEST(Parser_TypeOfValue) origin->size()); verify_query(test_context, origin, "ANY links.mixed.@type IN ANY {'numeric', 'string'}", origin->size()); - verify_query(test_context, table, "mixed.@type == int.@type", table->size() - nb_strings - 5); + verify_query(test_context, table, "mixed.@type == int.@type", nb_ints); verify_query(test_context, origin, "link.@type == link.mixed.@type", 0); - verify_query(test_context, origin, "links.@type == links.mixed.@type", 0); + verify_query(test_context, origin, "links.@type == links.mixed.@type", 1); // Object 19 verify_query(test_context, table, "mixed > 50", int_over_50); verify_query(test_context, table, "mixed > 50 && mixed.@type == 'double'", 1); @@ -4935,10 +4939,11 @@ TEST(Parser_TypeOfValue) CHECK_THROW_EX(verify_query(test_context, table, "int.@type == 'int'", 1), query_parser::InvalidQueryError, std::string(e.what()).find("Comparison between two constants is not supported") != std::string::npos); - CHECK_THROW_EX(verify_query(test_context, origin, "link.@type == 'object'", 1), query_parser::InvalidQueryError, - CHECK(std::string(e.what()).find( - "Comparison between two constants is not supported ('\"object\"' and '\"object\"')") != - std::string::npos)); + CHECK_THROW_EX( + verify_query(test_context, origin, "link.@type == 'objectlink'", 1), query_parser::InvalidQueryError, + CHECK(std::string(e.what()).find( + "Comparison between two constants is not supported ('\"objectlink\"' and '\"objectlink\"')") != + std::string::npos)); CHECK_THROW_EX(verify_query(test_context, table, "mixed.@type =[c] 'string'", 1), query_parser::InvalidQueryError, CHECK_EQUAL(std::string(e.what()), "Unsupported comparison operator '=[c]' against type '@type', " "right side must be a string or binary type"));