From 21a82796670d1c2d92022b5d697f4a97a738b28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 4 Feb 2022 12:09:14 +0100 Subject: [PATCH 1/4] Add RealmList.query method --- lib/src/list.dart | 32 +++++++++++++++++++++++++------- lib/src/native/realm_core.dart | 23 ++++++++++++++++++++++- lib/src/realm_class.dart | 2 +- lib/src/results.dart | 4 ++-- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/lib/src/list.dart b/lib/src/list.dart index f36682116..dd164c03c 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -24,14 +24,15 @@ import 'native/realm_core.dart'; import 'realm_object.dart'; import 'realm_class.dart'; +import 'results.dart'; -/// Instances of this class are live collections and will update as new elements are either +/// Instances of this class are live collections and will update as new elements are either /// added to or deleted from the Realm that match the underlying query. /// ///{@category Realm} class RealmList extends collection.ListBase { - late final RealmListHandle _handle; - late final Realm _realm; + final RealmListHandle _handle; + final Realm _realm; RealmList._(this._handle, this._realm); @@ -72,9 +73,9 @@ class RealmList extends collection.ListBase { /// Clears the collection in memory and the references /// to the objects in this collection in Realm. - - /// Removes all elements from this list. - /// + + /// Removes all elements from this list. + /// /// The length of the list becomes zero. /// If the elements are managed [RealmObject]s, they all remain in the Realm. @override @@ -83,10 +84,27 @@ class RealmList extends collection.ListBase { } } +// The query operations on lists only work for list of objects (core restriction), +// so we add it as an extension method to allow the compiler to prevent misuse. +extension RealmListOfObject on RealmList { + /// Filter list. + /// + /// @param query The query + /// @param args Possible parameters for substition in query + /// + /// @return The live result + /// + /// Only works for lists of objects. + RealmResults query(String query, [List args = const []]) { + final handle = realmCore.queryList(this, query, args); + return RealmResultsInternal.create(handle, realm); + } +} + /// @nodoc extension RealmListInternal on RealmList { RealmListHandle get handle => _handle; - Realm? get realm => _realm; + Realm get realm => _realm; static RealmList create(RealmListHandle handle, Realm realm) => RealmList._(handle, realm); diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 7ef21f097..edaa10690 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -364,6 +364,27 @@ class _RealmCore { }); } + RealmResultsHandle queryList(RealmList target, String query, List args) { + return using((arena) { + final length = args.length; + final argsPointer = arena(length); + for (var i = 0; i < length; ++i) { + _intoRealmValue(args[i], argsPointer.elementAt(i), arena); + } + final queryHandle = RealmQueryHandle._(_realmLib.invokeGetPointer( + () => _realmLib.realm_query_parse_for_list( + target.handle._pointer, + query.toUtf8Ptr(arena), + length, + argsPointer, + ), + )); + final resultsPointer = _realmLib.invokeGetPointer(() => _realmLib.realm_query_find_all(queryHandle._pointer)); + return RealmResultsHandle._(resultsPointer); + }); + } + + RealmObjectHandle getObjectAt(RealmResults results, int index) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_get_object(results.handle._pointer, index)); return RealmObjectHandle._(pointer); @@ -404,7 +425,7 @@ class _RealmCore { return using((Arena arena) { final realm_value = arena(); _realmLib.invokeGetBool(() => _realmLib.realm_list_get(list.handle._pointer, index, realm_value)); - return realm_value.toDartValue(list.realm!); + return realm_value.toDartValue(list.realm); }); } diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index b8dc71eaf..30b4db47f 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -32,7 +32,7 @@ export 'package:realm_common/realm_common.dart' show Ignored, Indexed, MapTo, PrimaryKey, RealmError, RealmModel, RealmUnsupportedSetError, RealmCollectionType, RealmPropertyType; export "configuration.dart" show Configuration, RealmSchema, SchemaObject; -export 'list.dart' show RealmList; +export 'list.dart' show RealmList, RealmListOfObject; export 'realm_object.dart' show RealmException, RealmObject; export 'realm_property.dart'; export 'results.dart' show RealmResults; diff --git a/lib/src/results.dart b/lib/src/results.dart index d20c79cff..fd356bf7f 100644 --- a/lib/src/results.dart +++ b/lib/src/results.dart @@ -26,8 +26,8 @@ import 'realm_class.dart'; /// /// {@category Realm} class RealmResults extends collection.IterableBase { - late final RealmResultsHandle _handle; - late final Realm _realm; + final RealmResultsHandle _handle; + final Realm _realm; RealmResults._(this._handle, this._realm); From ce1f6c2717851050799cdd3106e4ad5c197a9b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Fri, 4 Feb 2022 12:18:46 +0100 Subject: [PATCH 2/4] Add test of RealmList.query --- test/realm_test.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/realm_test.dart b/test/realm_test.dart index d4179c1e8..4f607f315 100644 --- a/test/realm_test.dart +++ b/test/realm_test.dart @@ -769,6 +769,24 @@ Future main([List? args]) async { realm.close(); }); + test('Query list', () { + final config = Configuration([Team.schema, Person.schema]); + final realm = Realm(config); + + final person = Person('Kasper'); + final team = Team('Realm-dart', players: [ + Person('Lubo'), + person, + Person('Desi'), + ]); + + realm.write(() => realm.add(team)); + + final result = (team.players as RealmList).query(r'name BEGINSWITH $0', ['K']); + + expect(result, [person]); + }); + test('Sort result', () { var config = Configuration([Person.schema]); var realm = Realm(config); From 1ab46167f70035d582d70917cebdd6cde5138f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Tue, 8 Feb 2022 10:39:03 +0100 Subject: [PATCH 3/4] Update CHANGELOG, and add TODO to remember to get rid of cast when possible --- CHANGELOG.md | 3 +++ test/realm_test.dart | 2 ++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 622bd89f1..6cbfd7653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ vNext ### Changes * Primary key annotation no longer requires field to be final. +### Enhancements +* Allow query on lists + 0.2.0+alpha Release notes (2022-01-31) ============================================================== diff --git a/test/realm_test.dart b/test/realm_test.dart index 4f607f315..ac94bbcb9 100644 --- a/test/realm_test.dart +++ b/test/realm_test.dart @@ -782,6 +782,8 @@ Future main([List? args]) async { realm.write(() => realm.add(team)); + // TODO: Get rid of cast, once type signature of team.players is a RealmList + // as opposed to the List we have today. final result = (team.players as RealmList).query(r'name BEGINSWITH $0', ['K']); expect(result, [person]); From b3ecef98b6009cb2338ec2de99504734425f8074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kasper=20Overg=C3=A5rd=20Nielsen?= Date: Tue, 8 Feb 2022 15:37:11 +0100 Subject: [PATCH 4/4] Apply suggestions for comments from code review Co-authored-by: blagoev --- CHANGELOG.md | 2 +- lib/src/list.dart | 11 ++++++++--- lib/src/native/realm_core.dart | 8 +++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cbfd7653..8f86c20cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ vNext * Primary key annotation no longer requires field to be final. ### Enhancements -* Allow query on lists +* Support query on lists of realm objects 0.2.0+alpha Release notes (2022-01-31) diff --git a/lib/src/list.dart b/lib/src/list.dart index dd164c03c..0c344b2fd 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -87,13 +87,18 @@ class RealmList extends collection.ListBase { // The query operations on lists only work for list of objects (core restriction), // so we add it as an extension method to allow the compiler to prevent misuse. extension RealmListOfObject on RealmList { - /// Filter list. + /// Filters the list and returns a new [RealmResults] according to the provided query. /// - /// @param query The query - /// @param args Possible parameters for substition in query + /// Only works for lists of Realm objects. + /// + /// @param query The query used to filter the list + /// @param args Optional parameters for substitution in the query /// /// @return The live result /// + /// The Realm Dart and Realm Flutter SDKs supports querying based on a language inspired by [NSPredicate](https://academy.realm.io/posts/nspredicate-cheatsheet/) + /// and [Predicate Programming Guide.](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Predicates/AdditionalChapters/Introduction.html#//apple_ref/doc/uid/TP40001789) + /// /// Only works for lists of objects. RealmResults query(String query, [List args = const []]) { final handle = realmCore.queryList(this, query, args); diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index edaa10690..9ed8a4e6a 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -297,7 +297,6 @@ class _RealmCore { }); } - // For debugging // ignore: unused_element int get _threadId => _realmLib.get_thread_id(); @@ -364,7 +363,7 @@ class _RealmCore { }); } - RealmResultsHandle queryList(RealmList target, String query, List args) { + RealmResultsHandle queryList(RealmList target, String query, List args) { return using((arena) { final length = args.length; final argsPointer = arena(length); @@ -384,7 +383,6 @@ class _RealmCore { }); } - RealmObjectHandle getObjectAt(RealmResults results, int index) { final pointer = _realmLib.invokeGetPointer(() => _realmLib.realm_results_get_object(results.handle._pointer, index)); return RealmObjectHandle._(pointer); @@ -477,8 +475,8 @@ abstract class Handle { Handle(this._pointer, int size) { if (_realmLib.realm_attach_finalizer(this, _pointer.cast(), size) == false) { - throw Exception("Error creating $runtimeType"); - } + throw Exception("Error creating $runtimeType"); + } } @override