diff --git a/examples/dart/test/data_types_test.dart b/examples/dart/test/data_types_test.dart index 8c0ac994d73..4220fc7036a 100644 --- a/examples/dart/test/data_types_test.dart +++ b/examples/dart/test/data_types_test.dart @@ -13,6 +13,18 @@ part 'data_types_test.realm.dart'; // :remove: // part 'car.realm.dart'; // :uncomment-end: +@RealmModel() +class _Car { + @PrimaryKey() + late ObjectId id; + + String? licensePlate; + bool isElectric = false; + double milesDriven = 0; + late List attributes; + late _Person? owner; +} + // :snippet-start: embedded-object-model // The generated `Address` class is an embedded object. @RealmModel(ObjectType.embeddedObject) @@ -35,17 +47,6 @@ class _Person { } // :snippet-end: -@RealmModel() -class _Car { - @PrimaryKey() - late ObjectId id; - - String? licensePlate; - bool isElectric = false; - double milesDriven = 0; - late List attributes; - late _Person? owner; -} // :snippet-end: // :snippet-start: uuid-model @@ -71,6 +72,7 @@ class _RealmValueExample { @Indexed() late RealmValue singleAnyValue; late List listOfMixedAnyValues; + late Set setOfMixedAnyValues; late Map mapOfMixedAnyValues; } @@ -173,9 +175,18 @@ main() { final realm = Realm(Configuration.local([RealmValueExample.schema])); realm.write(() { + // Use 'RealmValue.from()' to set values var anyValue = realm.add(RealmValueExample( + // Add a single `RealmValue` value singleAnyValue: RealmValue.from(1), + // Add a list of `RealmValue` values listOfMixedAnyValues: [Uuid.v4(), 'abc', 123].map(RealmValue.from), + // Add a set of `RealmValue` values + setOfMixedAnyValues: { + RealmValue.from('abc'), + RealmValue.from('def') + }, + // Add a map of string keys and `RealmValue` values mapOfMixedAnyValues: { '1': RealmValue.from(123), '2': RealmValue.from('abc') @@ -185,16 +196,19 @@ main() { var anyValueNull = realm.add(RealmValueExample( singleAnyValue: RealmValue.nullValue(), listOfMixedAnyValues: [null, null].map(RealmValue.from), + setOfMixedAnyValues: {RealmValue.nullValue()}, mapOfMixedAnyValues: {'null': RealmValue.nullValue()})); // :remove-start: expect(anyValue.singleAnyValue.type, RealmValueType.int); expect(anyValue.listOfMixedAnyValues[1].value.toString(), 'abc'); + expect(anyValue.setOfMixedAnyValues.first.value, 'abc'); expect( anyValue.mapOfMixedAnyValues.containsValue(RealmValue.from('abc')), true); expect(anyValueNull.singleAnyValue.value, null); expect(anyValueNull.listOfMixedAnyValues[0].value, null); + expect(anyValueNull.setOfMixedAnyValues.first.value, null); expect(anyValueNull.mapOfMixedAnyValues.containsValue(null), true); }); // :remove-end: @@ -224,7 +238,7 @@ main() { final data = realm.all(); for (var obj in data) { final anyValue = obj.singleAnyValue; - // Access the RealmValue.type property + // Access the RealmValue.type property switch (anyValue.type) { // Work with the returned RealmValueType enums case RealmValueType.int: diff --git a/examples/dart/test/data_types_test.realm.dart b/examples/dart/test/data_types_test.realm.dart index c74256be61f..1a04fdd9f7a 100644 --- a/examples/dart/test/data_types_test.realm.dart +++ b/examples/dart/test/data_types_test.realm.dart @@ -7,6 +7,131 @@ part of 'data_types_test.dart'; // ************************************************************************** // ignore_for_file: type=lint +class Car extends _Car with RealmEntity, RealmObjectBase, RealmObject { + static var _defaultsSet = false; + + Car( + ObjectId id, { + String? licensePlate, + bool isElectric = false, + double milesDriven = 0, + Iterable attributes = const [], + Person? owner, + }) { + if (!_defaultsSet) { + _defaultsSet = RealmObjectBase.setDefaults({ + 'isElectric': false, + 'milesDriven': 0, + }); + } + RealmObjectBase.set(this, 'id', id); + RealmObjectBase.set(this, 'licensePlate', licensePlate); + RealmObjectBase.set(this, 'isElectric', isElectric); + RealmObjectBase.set(this, 'milesDriven', milesDriven); + RealmObjectBase.set>( + this, 'attributes', RealmList(attributes)); + RealmObjectBase.set(this, 'owner', owner); + } + + Car._(); + + @override + ObjectId get id => RealmObjectBase.get(this, 'id') as ObjectId; + @override + set id(ObjectId value) => RealmObjectBase.set(this, 'id', value); + + @override + String? get licensePlate => + RealmObjectBase.get(this, 'licensePlate') as String?; + @override + set licensePlate(String? value) => + RealmObjectBase.set(this, 'licensePlate', value); + + @override + bool get isElectric => RealmObjectBase.get(this, 'isElectric') as bool; + @override + set isElectric(bool value) => RealmObjectBase.set(this, 'isElectric', value); + + @override + double get milesDriven => + RealmObjectBase.get(this, 'milesDriven') as double; + @override + set milesDriven(double value) => + RealmObjectBase.set(this, 'milesDriven', value); + + @override + RealmList get attributes => + RealmObjectBase.get(this, 'attributes') as RealmList; + @override + set attributes(covariant RealmList value) => + throw RealmUnsupportedSetError(); + + @override + Person? get owner => RealmObjectBase.get(this, 'owner') as Person?; + @override + set owner(covariant Person? value) => + RealmObjectBase.set(this, 'owner', value); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + Car freeze() => RealmObjectBase.freezeObject(this); + + EJsonValue toEJson() { + return { + 'id': id.toEJson(), + 'licensePlate': licensePlate.toEJson(), + 'isElectric': isElectric.toEJson(), + 'milesDriven': milesDriven.toEJson(), + 'attributes': attributes.toEJson(), + 'owner': owner.toEJson(), + }; + } + + static EJsonValue _toEJson(Car value) => value.toEJson(); + static Car _fromEJson(EJsonValue ejson) { + return switch (ejson) { + { + 'id': EJsonValue id, + 'licensePlate': EJsonValue licensePlate, + 'isElectric': EJsonValue isElectric, + 'milesDriven': EJsonValue milesDriven, + 'attributes': EJsonValue attributes, + 'owner': EJsonValue owner, + } => + Car( + fromEJson(id), + licensePlate: fromEJson(licensePlate), + isElectric: fromEJson(isElectric), + milesDriven: fromEJson(milesDriven), + attributes: fromEJson(attributes), + owner: fromEJson(owner), + ), + _ => raiseInvalidEJson(ejson), + }; + } + + static final schema = () { + RealmObjectBase.registerFactory(Car._); + register(_toEJson, _fromEJson); + return SchemaObject(ObjectType.realmObject, Car, 'Car', [ + SchemaProperty('id', RealmPropertyType.objectid, primaryKey: true), + SchemaProperty('licensePlate', RealmPropertyType.string, optional: true), + SchemaProperty('isElectric', RealmPropertyType.bool), + SchemaProperty('milesDriven', RealmPropertyType.double), + SchemaProperty('attributes', RealmPropertyType.string, + collectionType: RealmCollectionType.list), + SchemaProperty('owner', RealmPropertyType.object, + optional: true, linkTarget: 'Person'), + ]); + }(); + + @override + SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; +} + class Address extends _Address with RealmEntity, RealmObjectBase, EmbeddedObject { Address( @@ -170,220 +295,6 @@ class Person extends _Person with RealmEntity, RealmObjectBase, RealmObject { SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; } -class Car extends _Car with RealmEntity, RealmObjectBase, RealmObject { - static var _defaultsSet = false; - - Car( - ObjectId id, { - String? licensePlate, - bool isElectric = false, - double milesDriven = 0, - Iterable attributes = const [], - Person? owner, - }) { - if (!_defaultsSet) { - _defaultsSet = RealmObjectBase.setDefaults({ - 'isElectric': false, - 'milesDriven': 0, - }); - } - RealmObjectBase.set(this, 'id', id); - RealmObjectBase.set(this, 'licensePlate', licensePlate); - RealmObjectBase.set(this, 'isElectric', isElectric); - RealmObjectBase.set(this, 'milesDriven', milesDriven); - RealmObjectBase.set>( - this, 'attributes', RealmList(attributes)); - RealmObjectBase.set(this, 'owner', owner); - } - - Car._(); - - @override - ObjectId get id => RealmObjectBase.get(this, 'id') as ObjectId; - @override - set id(ObjectId value) => RealmObjectBase.set(this, 'id', value); - - @override - String? get licensePlate => - RealmObjectBase.get(this, 'licensePlate') as String?; - @override - set licensePlate(String? value) => - RealmObjectBase.set(this, 'licensePlate', value); - - @override - bool get isElectric => RealmObjectBase.get(this, 'isElectric') as bool; - @override - set isElectric(bool value) => RealmObjectBase.set(this, 'isElectric', value); - - @override - double get milesDriven => - RealmObjectBase.get(this, 'milesDriven') as double; - @override - set milesDriven(double value) => - RealmObjectBase.set(this, 'milesDriven', value); - - @override - RealmList get attributes => - RealmObjectBase.get(this, 'attributes') as RealmList; - @override - set attributes(covariant RealmList value) => - throw RealmUnsupportedSetError(); - - @override - Person? get owner => RealmObjectBase.get(this, 'owner') as Person?; - @override - set owner(covariant Person? value) => - RealmObjectBase.set(this, 'owner', value); - - @override - Stream> get changes => - RealmObjectBase.getChanges(this); - - @override - Car freeze() => RealmObjectBase.freezeObject(this); - - EJsonValue toEJson() { - return { - 'id': id.toEJson(), - 'licensePlate': licensePlate.toEJson(), - 'isElectric': isElectric.toEJson(), - 'milesDriven': milesDriven.toEJson(), - 'attributes': attributes.toEJson(), - 'owner': owner.toEJson(), - }; - } - - static EJsonValue _toEJson(Car value) => value.toEJson(); - static Car _fromEJson(EJsonValue ejson) { - return switch (ejson) { - { - 'id': EJsonValue id, - 'licensePlate': EJsonValue licensePlate, - 'isElectric': EJsonValue isElectric, - 'milesDriven': EJsonValue milesDriven, - 'attributes': EJsonValue attributes, - 'owner': EJsonValue owner, - } => - Car( - fromEJson(id), - licensePlate: fromEJson(licensePlate), - isElectric: fromEJson(isElectric), - milesDriven: fromEJson(milesDriven), - attributes: fromEJson(attributes), - owner: fromEJson(owner), - ), - _ => raiseInvalidEJson(ejson), - }; - } - - static final schema = () { - RealmObjectBase.registerFactory(Car._); - register(_toEJson, _fromEJson); - return SchemaObject(ObjectType.realmObject, Car, 'Car', [ - SchemaProperty('id', RealmPropertyType.objectid, primaryKey: true), - SchemaProperty('licensePlate', RealmPropertyType.string, optional: true), - SchemaProperty('isElectric', RealmPropertyType.bool), - SchemaProperty('milesDriven', RealmPropertyType.double), - SchemaProperty('attributes', RealmPropertyType.string, - collectionType: RealmCollectionType.list), - SchemaProperty('owner', RealmPropertyType.object, - optional: true, linkTarget: 'Person'), - ]); - }(); - - @override - SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; -} - -class Car extends _Car with RealmEntity, RealmObjectBase, RealmObject { - static var _defaultsSet = false; - - Car( - ObjectId id, { - String? licensePlate, - bool isElectric = false, - double milesDriven = 0, - Person? owner, - Iterable attributes = const [], - }) { - if (!_defaultsSet) { - _defaultsSet = RealmObjectBase.setDefaults({ - 'isElectric': false, - 'milesDriven': 0, - }); - } - RealmObjectBase.set(this, 'id', id); - RealmObjectBase.set(this, 'licensePlate', licensePlate); - RealmObjectBase.set(this, 'isElectric', isElectric); - RealmObjectBase.set(this, 'milesDriven', milesDriven); - RealmObjectBase.set(this, 'owner', owner); - RealmObjectBase.set>( - this, 'attributes', RealmList(attributes)); - } - - Car._(); - - @override - ObjectId get id => RealmObjectBase.get(this, 'id') as ObjectId; - @override - set id(ObjectId value) => RealmObjectBase.set(this, 'id', value); - - @override - String? get licensePlate => - RealmObjectBase.get(this, 'licensePlate') as String?; - @override - set licensePlate(String? value) => - RealmObjectBase.set(this, 'licensePlate', value); - - @override - bool get isElectric => RealmObjectBase.get(this, 'isElectric') as bool; - @override - set isElectric(bool value) => RealmObjectBase.set(this, 'isElectric', value); - - @override - double get milesDriven => - RealmObjectBase.get(this, 'milesDriven') as double; - @override - set milesDriven(double value) => - RealmObjectBase.set(this, 'milesDriven', value); - - @override - RealmList get attributes => - RealmObjectBase.get(this, 'attributes') as RealmList; - @override - set attributes(covariant RealmList value) => - throw RealmUnsupportedSetError(); - - @override - Person? get owner => RealmObjectBase.get(this, 'owner') as Person?; - @override - set owner(covariant Person? value) => - RealmObjectBase.set(this, 'owner', value); - - @override - Stream> get changes => - RealmObjectBase.getChanges(this); - - @override - Car freeze() => RealmObjectBase.freezeObject(this); - - static SchemaObject get schema => _schema ??= _initSchema(); - static SchemaObject? _schema; - static SchemaObject _initSchema() { - RealmObjectBase.registerFactory(Car._); - return const SchemaObject(ObjectType.realmObject, Car, 'Car', [ - SchemaProperty('id', RealmPropertyType.objectid, primaryKey: true), - SchemaProperty('licensePlate', RealmPropertyType.string, optional: true), - SchemaProperty('isElectric', RealmPropertyType.bool), - SchemaProperty('milesDriven', RealmPropertyType.double), - SchemaProperty('attributes', RealmPropertyType.string, - collectionType: RealmCollectionType.list), - SchemaProperty('owner', RealmPropertyType.object, - optional: true, linkTarget: 'Person'), - ]); - } -} - class UuidPrimaryKey extends _UuidPrimaryKey with RealmEntity, RealmObjectBase, RealmObject { UuidPrimaryKey( @@ -498,11 +409,14 @@ class RealmValueExample extends _RealmValueExample RealmValueExample({ RealmValue singleAnyValue = const RealmValue.nullValue(), Iterable listOfMixedAnyValues = const [], + Set setOfMixedAnyValues = const {}, Map mapOfMixedAnyValues = const {}, }) { RealmObjectBase.set(this, 'singleAnyValue', singleAnyValue); RealmObjectBase.set>(this, 'listOfMixedAnyValues', RealmList(listOfMixedAnyValues)); + RealmObjectBase.set>( + this, 'setOfMixedAnyValues', RealmSet(setOfMixedAnyValues)); RealmObjectBase.set>( this, 'mapOfMixedAnyValues', RealmMap(mapOfMixedAnyValues)); } @@ -524,6 +438,14 @@ class RealmValueExample extends _RealmValueExample set listOfMixedAnyValues(covariant RealmList value) => throw RealmUnsupportedSetError(); + @override + RealmSet get setOfMixedAnyValues => + RealmObjectBase.get(this, 'setOfMixedAnyValues') + as RealmSet; + @override + set setOfMixedAnyValues(covariant RealmSet value) => + throw RealmUnsupportedSetError(); + @override RealmMap get mapOfMixedAnyValues => RealmObjectBase.get(this, 'mapOfMixedAnyValues') @@ -544,6 +466,8 @@ class RealmValueExample extends _RealmValueExample return { 'singleAnyValue': singleAnyValue.toEJson(), 'listOfMixedAnyValues': listOfMixedAnyValues.toEJson(), + 'setOfMixedAnyValues': setOfMixedAnyValues.toEJson(), + 'mapOfMixedAnyValues': mapOfMixedAnyValues.toEJson(), }; } @@ -553,10 +477,14 @@ class RealmValueExample extends _RealmValueExample { 'singleAnyValue': EJsonValue singleAnyValue, 'listOfMixedAnyValues': EJsonValue listOfMixedAnyValues, + 'setOfMixedAnyValues': EJsonValue setOfMixedAnyValues, + 'mapOfMixedAnyValues': EJsonValue mapOfMixedAnyValues, } => RealmValueExample( singleAnyValue: fromEJson(singleAnyValue), listOfMixedAnyValues: fromEJson(listOfMixedAnyValues), + setOfMixedAnyValues: fromEJson(setOfMixedAnyValues), + mapOfMixedAnyValues: fromEJson(mapOfMixedAnyValues), ), _ => raiseInvalidEJson(ejson), }; @@ -571,6 +499,8 @@ class RealmValueExample extends _RealmValueExample optional: true, indexType: RealmIndexType.regular), SchemaProperty('listOfMixedAnyValues', RealmPropertyType.mixed, optional: true, collectionType: RealmCollectionType.list), + SchemaProperty('setOfMixedAnyValues', RealmPropertyType.mixed, + optional: true, collectionType: RealmCollectionType.set), SchemaProperty('mapOfMixedAnyValues', RealmPropertyType.mixed, optional: true, collectionType: RealmCollectionType.map), ]); @@ -580,6 +510,65 @@ class RealmValueExample extends _RealmValueExample SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; } +class RealmValueCollectionExample extends _RealmValueCollectionExample + with RealmEntity, RealmObjectBase, RealmObject { + RealmValueCollectionExample({ + RealmValue singleAnyValue = const RealmValue.nullValue(), + }) { + RealmObjectBase.set(this, 'singleAnyValue', singleAnyValue); + } + + RealmValueCollectionExample._(); + + @override + RealmValue get singleAnyValue => + RealmObjectBase.get(this, 'singleAnyValue') as RealmValue; + @override + set singleAnyValue(RealmValue value) => + RealmObjectBase.set(this, 'singleAnyValue', value); + + @override + Stream> get changes => + RealmObjectBase.getChanges(this); + + @override + RealmValueCollectionExample freeze() => + RealmObjectBase.freezeObject(this); + + EJsonValue toEJson() { + return { + 'singleAnyValue': singleAnyValue.toEJson(), + }; + } + + static EJsonValue _toEJson(RealmValueCollectionExample value) => + value.toEJson(); + static RealmValueCollectionExample _fromEJson(EJsonValue ejson) { + return switch (ejson) { + { + 'singleAnyValue': EJsonValue singleAnyValue, + } => + RealmValueCollectionExample( + singleAnyValue: fromEJson(singleAnyValue), + ), + _ => raiseInvalidEJson(ejson), + }; + } + + static final schema = () { + RealmObjectBase.registerFactory(RealmValueCollectionExample._); + register(_toEJson, _fromEJson); + return SchemaObject(ObjectType.realmObject, RealmValueCollectionExample, + 'RealmValueCollectionExample', [ + SchemaProperty('singleAnyValue', RealmPropertyType.mixed, + optional: true, indexType: RealmIndexType.regular), + ]); + }(); + + @override + SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; +} + class Vehicle extends _Vehicle with RealmEntity, RealmObjectBase, RealmObject { Vehicle( ObjectId id, diff --git a/examples/dart/test/define_realm_model_test.dart b/examples/dart/test/define_realm_model_test.dart index 2eed0184fd7..62a0a088efe 100644 --- a/examples/dart/test/define_realm_model_test.dart +++ b/examples/dart/test/define_realm_model_test.dart @@ -1,7 +1,6 @@ import 'package:realm_dart/realm.dart'; import 'package:test/expect.dart'; import 'package:test/scaffolding.dart'; -import 'dart:convert'; part 'define_realm_model_test.realm.dart'; @@ -38,6 +37,7 @@ class _Boat { // :snippet-end: // :snippet-start: unstructured-data-model +// Define class with a `RealmValue` property @RealmModel() class _EventLog { @PrimaryKey() @@ -67,6 +67,8 @@ main() { // :snippet-start: create-unstructured-data-example realm.write(() { + // Add `eventLog` property data as a map of mixed data, which + // also includes nested lists of mixed data realm.add(EventLog(ObjectId(), 'purchase', DateTime.now(), 'user123', details: RealmValue.from({ 'ipAddress': '192.168.1.1', diff --git a/examples/dart/test/define_realm_model_test.realm.dart b/examples/dart/test/define_realm_model_test.realm.dart index 8804580218d..63e4780c717 100644 --- a/examples/dart/test/define_realm_model_test.realm.dart +++ b/examples/dart/test/define_realm_model_test.realm.dart @@ -262,14 +262,13 @@ class EventLog extends _EventLog String eventType, DateTime timestamp, String userId, { - Map details = const {}, + RealmValue details = const RealmValue.nullValue(), }) { RealmObjectBase.set(this, 'id', id); RealmObjectBase.set(this, 'eventType', eventType); RealmObjectBase.set(this, 'timestamp', timestamp); RealmObjectBase.set(this, 'userId', userId); - RealmObjectBase.set>( - this, 'details', RealmMap(details)); + RealmObjectBase.set(this, 'details', details); } EventLog._(); @@ -298,11 +297,10 @@ class EventLog extends _EventLog set userId(String value) => RealmObjectBase.set(this, 'userId', value); @override - RealmMap get details => - RealmObjectBase.get(this, 'details') as RealmMap; + RealmValue get details => + RealmObjectBase.get(this, 'details') as RealmValue; @override - set details(covariant RealmMap value) => - throw RealmUnsupportedSetError(); + set details(RealmValue value) => RealmObjectBase.set(this, 'details', value); @override Stream> get changes => @@ -311,17 +309,49 @@ class EventLog extends _EventLog @override EventLog freeze() => RealmObjectBase.freezeObject(this); - static SchemaObject get schema => _schema ??= _initSchema(); - static SchemaObject? _schema; - static SchemaObject _initSchema() { + EJsonValue toEJson() { + return { + 'id': id.toEJson(), + 'eventType': eventType.toEJson(), + 'timestamp': timestamp.toEJson(), + 'userId': userId.toEJson(), + 'details': details.toEJson(), + }; + } + + static EJsonValue _toEJson(EventLog value) => value.toEJson(); + static EventLog _fromEJson(EJsonValue ejson) { + return switch (ejson) { + { + 'id': EJsonValue id, + 'eventType': EJsonValue eventType, + 'timestamp': EJsonValue timestamp, + 'userId': EJsonValue userId, + 'details': EJsonValue details, + } => + EventLog( + fromEJson(id), + fromEJson(eventType), + fromEJson(timestamp), + fromEJson(userId), + details: fromEJson(details), + ), + _ => raiseInvalidEJson(ejson), + }; + } + + static final schema = () { RealmObjectBase.registerFactory(EventLog._); - return const SchemaObject(ObjectType.realmObject, EventLog, 'EventLog', [ + register(_toEJson, _fromEJson); + return SchemaObject(ObjectType.realmObject, EventLog, 'EventLog', [ SchemaProperty('id', RealmPropertyType.objectid, primaryKey: true), SchemaProperty('eventType', RealmPropertyType.string), SchemaProperty('timestamp', RealmPropertyType.timestamp), SchemaProperty('userId', RealmPropertyType.string), - SchemaProperty('details', RealmPropertyType.mixed, - optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('details', RealmPropertyType.mixed, optional: true), ]); - } + }(); + + @override + SchemaObject get objectSchema => RealmObjectBase.getSchema(this) ?? schema; } diff --git a/examples/dart/test/read_write_data_test.dart b/examples/dart/test/read_write_data_test.dart index 4eba0584bf0..aff7e94c65c 100644 --- a/examples/dart/test/read_write_data_test.dart +++ b/examples/dart/test/read_write_data_test.dart @@ -205,7 +205,7 @@ void main() { ]); final teams = realm.all(); - // Use bracket notation to query values at the specified path + // Use bracket notation to query collection values at the specified path final teamsWithHighPriorityEvents = // Check any element at that path with [*] teams.query("eventLog[*].priority == 'high'"); @@ -216,13 +216,21 @@ void main() { teams.query("eventLog[*].type[FIRST] == 'maintenance'"); print(teamsWithMaintenanceEvents.length); // prints `1` + final teamsWithMultipleEvents = + // Check for collection at that path with matching elements + // Note that the order must match unless you use ANY or ALL + teams.query("eventLog[*].type[*] == {'maintenance', 'work_order'}"); + print( + teamsWithMultipleEvents.length); // prints `0` because order matters + final teamsWithEventsAsLists = - // Check the collection type with @type + // Check the collection type with @type teams.query("eventLog[*].type.@type == 'list'"); print(teamsWithEventsAsLists.length); // prints `2` // :remove-start: expect(teamsWithHighPriorityEvents.length, 2); expect(teamsWithMaintenanceEvents.length, 1); + expect(teamsWithMultipleEvents.length, 0); expect(teamsWithEventsAsLists.length, 2); // :remove-end: }); @@ -253,152 +261,156 @@ void main() { cleanUpRealm(realm); }); }); - test('Return from write block', () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); + group('Write, update, and delete data', () { + test('Return from write block', () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); - // :snippet-start: return-from-write - final yoda = realm.write(() { - return realm.add(Person(ObjectId(), 'Yoda')); + // :snippet-start: return-from-write + final yoda = realm.write(() { + return realm.add(Person(ObjectId(), 'Yoda')); + }); + // :snippet-end: + expect(yoda.name, 'Yoda'); + cleanUpRealm(realm); }); - // :snippet-end: - expect(yoda.name, 'Yoda'); - cleanUpRealm(realm); - }); - test("Create an Object", () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - // :snippet-start: create-object - realm.write(() { - realm.add(Person(ObjectId(), 'Lando')); + test("Create an Object", () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + // :snippet-start: create-object + realm.write(() { + realm.add(Person(ObjectId(), 'Lando')); + }); + // :snippet-end: + expect(realm.all().first.name, 'Lando'); + cleanUpRealm(realm); }); - // :snippet-end: - expect(realm.all().first.name, 'Lando'); - cleanUpRealm(realm); - }); - test('Create Multiple Objects', () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - // :snippet-start: create-multiple-objects - realm.write(() { - realm.addAll([ - Person(ObjectId(), 'Figrin D\'an'), - Person(ObjectId(), 'Greedo'), - Person(ObjectId(), 'Toro') - ]); + test('Create Multiple Objects', () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + // :snippet-start: create-multiple-objects + realm.write(() { + realm.addAll([ + Person(ObjectId(), 'Figrin D\'an'), + Person(ObjectId(), 'Greedo'), + Person(ObjectId(), 'Toro') + ]); + }); + // :snippet-end: + expect(realm.all().length, 3); + cleanUpRealm(realm); }); - // :snippet-end: - expect(realm.all().length, 3); - cleanUpRealm(realm); - }); - test('Update Object Properties', () { - final config = Configuration.local([Person.schema, Team.schema]); - final realm = Realm(config); - final spaceshipTeam = Team(ObjectId(), 'Millennium Falcon Crew', - crew: [Person(ObjectId(), 'Han'), Person(ObjectId(), 'Chewbacca')]); - realm.write(() => realm.add(spaceshipTeam)); - // :snippet-start: update-object - realm.write(() { - spaceshipTeam.name = 'Galactic Republic Scout Team'; - spaceshipTeam.crew - .addAll([Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Leia')]); + test('Update Object Properties', () { + final config = Configuration.local([Person.schema, Team.schema]); + final realm = Realm(config); + final spaceshipTeam = Team(ObjectId(), 'Millennium Falcon Crew', + crew: [Person(ObjectId(), 'Han'), Person(ObjectId(), 'Chewbacca')]); + realm.write(() => realm.add(spaceshipTeam)); + // :snippet-start: update-object + realm.write(() { + spaceshipTeam.name = 'Galactic Republic Scout Team'; + spaceshipTeam.crew + .addAll([Person(ObjectId(), 'Luke'), Person(ObjectId(), 'Leia')]); + }); + // :snippet-end: + expect(spaceshipTeam.name, 'Galactic Republic Scout Team'); + expect(spaceshipTeam.crew.length, 4); + cleanUpRealm(realm); }); - // :snippet-end: - expect(spaceshipTeam.name, 'Galactic Republic Scout Team'); - expect(spaceshipTeam.crew.length, 4); - cleanUpRealm(realm); - }); - test('Upsert data', () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - // :snippet-start: upsert - final id = ObjectId(); - // Add Anakin Skywalker to the realm with primary key `id` - final anakin = Person( - id, - "Anakin Skywalker", - ); - realm.write(() { - realm.add(anakin); - }); + test('Upsert data', () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + // :snippet-start: upsert + final id = ObjectId(); + // Add Anakin Skywalker to the realm with primary key `id` + final anakin = Person( + id, + "Anakin Skywalker", + ); + realm.write(() { + realm.add(anakin); + }); - // Overwrite the 'Anakin' Person object - // with a new 'Darth Vader' object - final darthVader = Person(id, 'Darth Vader'); - realm.write(() { - realm.add(darthVader, update: true); + // Overwrite the 'Anakin' Person object + // with a new 'Darth Vader' object + final darthVader = Person(id, 'Darth Vader'); + realm.write(() { + realm.add(darthVader, update: true); + }); + // :snippet-end: + final darthAnakin = realm.find(id); + expect(darthAnakin!.name, 'Darth Vader'); + cleanUpRealm(realm); }); - // :snippet-end: - final darthAnakin = realm.find(id); - expect(darthAnakin!.name, 'Darth Vader'); - cleanUpRealm(realm); - }); - test("Delete a single object", () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - final obiWan = - realm.write((() => realm.add(Person(ObjectId(), 'Obi-Wan')))); - expect(realm.all().length, 1); - // :snippet-start: delete-one-object - realm.write(() { - realm.delete(obiWan); + test("Delete a single object", () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + final obiWan = + realm.write((() => realm.add(Person(ObjectId(), 'Obi-Wan')))); + expect(realm.all().length, 1); + // :snippet-start: delete-one-object + realm.write(() { + realm.delete(obiWan); + }); + // :snippet-end: + expect(realm.all().length, 0); + cleanUpRealm(realm); }); - // :snippet-end: - expect(realm.all().length, 0); - cleanUpRealm(realm); - }); - test("Delete multiple objects", () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - final obiWan = - realm.write((() => realm.add(Person(ObjectId(), 'Obi-Wan')))); - final quiGon = - realm.write((() => realm.add(Person(ObjectId(), 'Qui-Gon')))); - expect(realm.all().length, 2); - // :snippet-start: delete-multiple-objects - realm.write(() { - realm.deleteMany([obiWan, quiGon]); + test("Delete multiple objects", () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + final obiWan = + realm.write((() => realm.add(Person(ObjectId(), 'Obi-Wan')))); + final quiGon = + realm.write((() => realm.add(Person(ObjectId(), 'Qui-Gon')))); + expect(realm.all().length, 2); + // :snippet-start: delete-multiple-objects + realm.write(() { + realm.deleteMany([obiWan, quiGon]); + }); + // :snippet-end: + expect(realm.all().length, 0); + cleanUpRealm(realm); }); - // :snippet-end: - expect(realm.all().length, 0); - cleanUpRealm(realm); - }); - test("Delete all objects of a type", () { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - realm.write( - () => realm.addAll( - [Person(ObjectId(), 'Boba Fett'), Person(ObjectId(), 'Jango Fett')]), - ); - expect(realm.all().length, 2); - // :snippet-start: delete-all-objects-of-type - realm.write(() { - realm.deleteAll(); + test("Delete all objects of a type", () { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + realm.write( + () => realm.addAll([ + Person(ObjectId(), 'Boba Fett'), + Person(ObjectId(), 'Jango Fett') + ]), + ); + expect(realm.all().length, 2); + // :snippet-start: delete-all-objects-of-type + realm.write(() { + realm.deleteAll(); + }); + // :snippet-end: + expect(realm.all().length, 0); + cleanUpRealm(realm); }); - // :snippet-end: - expect(realm.all().length, 0); - cleanUpRealm(realm); - }); - test('Write async', () async { - final config = Configuration.local([Person.schema]); - final realm = Realm(config); - // :snippet-start: write-async - // Add Leia to the realm using `writeAsync` - Person leia = Person(ObjectId(), "Leia"); - realm.writeAsync(() { - realm.add(leia); + test('Write async', () async { + final config = Configuration.local([Person.schema]); + final realm = Realm(config); + // :snippet-start: write-async + // Add Leia to the realm using `writeAsync` + Person leia = Person(ObjectId(), "Leia"); + realm.writeAsync(() { + realm.add(leia); + }); + // :snippet-end: + final leiaAgain = realm.query("name == \$0", ['Leia']); + expect(leiaAgain.length, 0); + expect(realm.isInTransaction, true); + // let transaction resolve + await Future.delayed(Duration(milliseconds: 500)); + expect(realm.isInTransaction, false); + expect(realm.query("name == \$0", ['Leia']).length, 1); + cleanUpRealm(realm); }); - // :snippet-end: - final leiaAgain = realm.query("name == \$0", ['Leia']); - expect(leiaAgain.length, 0); - expect(realm.isInTransaction, true); - // let transaction resolve - await Future.delayed(Duration(milliseconds: 500)); - expect(realm.isInTransaction, false); - expect(realm.query("name == \$0", ['Leia']).length, 1); - cleanUpRealm(realm); }); } diff --git a/examples/dart/test/read_write_data_test.realm.dart b/examples/dart/test/read_write_data_test.realm.dart index 6e69d83a6e9..b1a04434c55 100644 --- a/examples/dart/test/read_write_data_test.realm.dart +++ b/examples/dart/test/read_write_data_test.realm.dart @@ -90,14 +90,13 @@ class Team extends _Team with RealmEntity, RealmObjectBase, RealmObject { ObjectId id, String name, { Iterable crew = const [], - Map log = const {}, + RealmValue eventLog = const RealmValue.nullValue(), }) { RealmObjectBase.set(this, 'id', id); RealmObjectBase.set(this, 'name', name); RealmObjectBase.set>( this, 'crew', RealmList(crew)); - RealmObjectBase.set>( - this, 'log', RealmMap(log)); + RealmObjectBase.set(this, 'eventLog', eventLog); } Team._(); @@ -120,11 +119,11 @@ class Team extends _Team with RealmEntity, RealmObjectBase, RealmObject { throw RealmUnsupportedSetError(); @override - RealmMap get log => - RealmObjectBase.get(this, 'log') as RealmMap; + RealmValue get eventLog => + RealmObjectBase.get(this, 'eventLog') as RealmValue; @override - set log(covariant RealmMap value) => - throw RealmUnsupportedSetError(); + set eventLog(RealmValue value) => + RealmObjectBase.set(this, 'eventLog', value); @override Stream> get changes => @@ -138,6 +137,7 @@ class Team extends _Team with RealmEntity, RealmObjectBase, RealmObject { 'id': id.toEJson(), 'name': name.toEJson(), 'crew': crew.toEJson(), + 'eventLog': eventLog.toEJson(), }; } @@ -148,11 +148,13 @@ class Team extends _Team with RealmEntity, RealmObjectBase, RealmObject { 'id': EJsonValue id, 'name': EJsonValue name, 'crew': EJsonValue crew, + 'eventLog': EJsonValue eventLog, } => Team( fromEJson(id), fromEJson(name), crew: fromEJson(crew), + eventLog: fromEJson(eventLog), ), _ => raiseInvalidEJson(ejson), }; @@ -166,8 +168,7 @@ class Team extends _Team with RealmEntity, RealmObjectBase, RealmObject { SchemaProperty('name', RealmPropertyType.string), SchemaProperty('crew', RealmPropertyType.object, linkTarget: 'Person', collectionType: RealmCollectionType.list), - SchemaProperty('log', RealmPropertyType.mixed, - optional: true, collectionType: RealmCollectionType.map), + SchemaProperty('eventLog', RealmPropertyType.mixed, optional: true), ]); }(); diff --git a/source/examples/generated/flutter/data_types_test.snippet.data-types-example-model.dart b/source/examples/generated/flutter/data_types_test.snippet.data-types-example-model.dart index 754aedec470..2066b900a47 100644 --- a/source/examples/generated/flutter/data_types_test.snippet.data-types-example-model.dart +++ b/source/examples/generated/flutter/data_types_test.snippet.data-types-example-model.dart @@ -1,6 +1,18 @@ part 'car.realm.dart'; +@RealmModel() +class _Car { + @PrimaryKey() + late ObjectId id; + + String? licensePlate; + bool isElectric = false; + double milesDriven = 0; + late List attributes; + late _Person? owner; +} + // The generated `Address` class is an embedded object. @RealmModel(ObjectType.embeddedObject) class _Address { @@ -21,14 +33,3 @@ class _Person { late _Address? address; // Must be nullable } -@RealmModel() -class _Car { - @PrimaryKey() - late ObjectId id; - - String? licensePlate; - bool isElectric = false; - double milesDriven = 0; - late List attributes; - late _Person? owner; -} diff --git a/source/examples/generated/flutter/data_types_test.snippet.realm-value-from.dart b/source/examples/generated/flutter/data_types_test.snippet.realm-value-from.dart index 0424674f089..c7fc5b8a9a4 100644 --- a/source/examples/generated/flutter/data_types_test.snippet.realm-value-from.dart +++ b/source/examples/generated/flutter/data_types_test.snippet.realm-value-from.dart @@ -1,9 +1,18 @@ final realm = Realm(Configuration.local([RealmValueExample.schema])); realm.write(() { + // Use 'RealmValue.from()' to set values var anyValue = realm.add(RealmValueExample( + // Add a single `RealmValue` value singleAnyValue: RealmValue.from(1), + // Add a list of `RealmValue` values listOfMixedAnyValues: [Uuid.v4(), 'abc', 123].map(RealmValue.from), + // Add a set of `RealmValue` values + setOfMixedAnyValues: { + RealmValue.from('abc'), + RealmValue.from('def') + }, + // Add a map of string keys and `RealmValue` values mapOfMixedAnyValues: { '1': RealmValue.from(123), '2': RealmValue.from('abc') @@ -13,5 +22,6 @@ realm.write(() { var anyValueNull = realm.add(RealmValueExample( singleAnyValue: RealmValue.nullValue(), listOfMixedAnyValues: [null, null].map(RealmValue.from), + setOfMixedAnyValues: {RealmValue.nullValue()}, mapOfMixedAnyValues: {'null': RealmValue.nullValue()})); diff --git a/source/examples/generated/flutter/data_types_test.snippet.realm-value-model.dart b/source/examples/generated/flutter/data_types_test.snippet.realm-value-model.dart index bf96c0b8b78..f148b752e51 100644 --- a/source/examples/generated/flutter/data_types_test.snippet.realm-value-model.dart +++ b/source/examples/generated/flutter/data_types_test.snippet.realm-value-model.dart @@ -3,6 +3,7 @@ class _RealmValueExample { @Indexed() late RealmValue singleAnyValue; late List listOfMixedAnyValues; + late Set setOfMixedAnyValues; late Map mapOfMixedAnyValues; } diff --git a/source/examples/generated/flutter/data_types_test.snippet.realm-value-type.dart b/source/examples/generated/flutter/data_types_test.snippet.realm-value-type.dart index 90c402a68e1..f90cf086912 100644 --- a/source/examples/generated/flutter/data_types_test.snippet.realm-value-type.dart +++ b/source/examples/generated/flutter/data_types_test.snippet.realm-value-type.dart @@ -1,7 +1,7 @@ final data = realm.all(); for (var obj in data) { final anyValue = obj.singleAnyValue; - // Access the RealmValue.type property + // Access the RealmValue.type property switch (anyValue.type) { // Work with the returned RealmValueType enums case RealmValueType.int: diff --git a/source/examples/generated/flutter/define_realm_model_test.snippet.create-unstructured-data-example.dart b/source/examples/generated/flutter/define_realm_model_test.snippet.create-unstructured-data-example.dart index 0544a332b42..ea71da6b8ed 100644 --- a/source/examples/generated/flutter/define_realm_model_test.snippet.create-unstructured-data-example.dart +++ b/source/examples/generated/flutter/define_realm_model_test.snippet.create-unstructured-data-example.dart @@ -1,4 +1,6 @@ realm.write(() { + // Add `eventLog` property data as a map of mixed data, which + // also includes nested lists of mixed data realm.add(EventLog(ObjectId(), 'purchase', DateTime.now(), 'user123', details: RealmValue.from({ 'ipAddress': '192.168.1.1', diff --git a/source/examples/generated/flutter/define_realm_model_test.snippet.unstructured-data-model.dart b/source/examples/generated/flutter/define_realm_model_test.snippet.unstructured-data-model.dart index c553d0f258e..b0de34b4c69 100644 --- a/source/examples/generated/flutter/define_realm_model_test.snippet.unstructured-data-model.dart +++ b/source/examples/generated/flutter/define_realm_model_test.snippet.unstructured-data-model.dart @@ -1,3 +1,4 @@ +// Define class with a `RealmValue` property @RealmModel() class _EventLog { @PrimaryKey() diff --git a/source/examples/generated/flutter/read_write_data_test.snippet.filter-nested-collections.dart b/source/examples/generated/flutter/read_write_data_test.snippet.filter-nested-collections.dart index e559d1d468f..375cecbeed9 100644 --- a/source/examples/generated/flutter/read_write_data_test.snippet.filter-nested-collections.dart +++ b/source/examples/generated/flutter/read_write_data_test.snippet.filter-nested-collections.dart @@ -28,7 +28,7 @@ realm.write(() { ]); final teams = realm.all(); - // Use bracket notation to query values at the specified path + // Use bracket notation to query collection values at the specified path final teamsWithHighPriorityEvents = // Check any element at that path with [*] teams.query("eventLog[*].priority == 'high'"); @@ -39,8 +39,15 @@ realm.write(() { teams.query("eventLog[*].type[FIRST] == 'maintenance'"); print(teamsWithMaintenanceEvents.length); // prints `1` + final teamsWithMultipleEvents = + // Check for collection at that path with matching elements + // Note that the order must match unless you use ANY or ALL + teams.query("eventLog[*].type[*] == {'maintenance', 'work_order'}"); + print( + teamsWithMultipleEvents.length); // prints `0` because order matters + final teamsWithEventsAsLists = - // Check the collection type with @type + // Check the collection type with @type teams.query("eventLog[*].type.@type == 'list'"); print(teamsWithEventsAsLists.length); // prints `2` }); diff --git a/source/sdk/flutter/crud/read.txt b/source/sdk/flutter/crud/read.txt index 42ffa4e40c7..400f6d25694 100644 --- a/source/sdk/flutter/crud/read.txt +++ b/source/sdk/flutter/crud/read.txt @@ -216,24 +216,25 @@ properties can contain collections (a list or map) of mixed data. These collections can be nested within collections and can contain other collections of mixed data. -You can query these using the same RQL operators and syntax as you would for a -normal list or dictionary. Refer to the :ref:`rql` documentation for more -information. +You can query these using the same syntax as you would for a +normal list or dictionary collection. Refer to the :ref:`rql` documentation for more +information on supported operators and list comparisons. For nested collections, you can also use: -- Bracket notation, which provides additional collection query support: +- Bracket notation, which provides the following collection query operators: - - ``[FIRST]`` and [``LAST``] - match the first or last elements within the collection - - ``[]`` - match the element at the specific index - - ``[*]`` - match any elements within the collection - - ``[SIZE]`` - match the collection length + - ``[FIRST]`` and [``LAST``]: match the first or last elements within the collection. + - ``[]``: match the element at the specific index. + - ``[*]``: match any element within the collection (this operator assumes a + collection type at that path). + - ``[SIZE]``: match the collection length. -- The ``@type`` operator, which has the following possible values: +- The ``@type`` operator, which supports the following values: - - ``array`` and ``list`` - match a list collection - - ``dictionary`` and ``object`` - match a map collection - - ``collection`` - matches a list or a map collection + - ``array`` and ``list``: match a list collection. + - ``dictionary`` and ``object``: match a map collection. + - ``collection``: match a list or a map collection. .. literalinclude:: /examples/generated/flutter/read_write_data_test.snippet.filter-nested-collections.dart :language: dart diff --git a/source/sdk/flutter/realm-database/model-data/data-types.txt b/source/sdk/flutter/realm-database/model-data/data-types.txt index 8a1a7c4db94..635b91dac86 100644 --- a/source/sdk/flutter/realm-database/model-data/data-types.txt +++ b/source/sdk/flutter/realm-database/model-data/data-types.txt @@ -365,13 +365,12 @@ data type is a mixed data type that can represent any other valid data type except embedded objects. In Flutter SDK v2.0.0 and later, ``RealmValue`` can represent a ``List`` or ``Map``. -``RealmValue`` is indexable, but cannot be a primary key. You can also create -collections of type ``RealmValue``. - Define a RealmValue Property ```````````````````````````` -To define a property as ``RealmValue``, set its type in your object model. +To define a ``RealmValue`` property, set its type in your object model. +``RealmValue`` is indexable, but cannot be a primary key. You can also define +properties as collections (lists, sets, or maps) of type ``RealmValue``. .. literalinclude:: /examples/generated/flutter/data_types_test.snippet.realm-value-model.dart :language: dart @@ -390,8 +389,6 @@ To add a ``RealmValue`` to a Realm object, call ``RealmValue.from()`` on the dat .. literalinclude:: /examples/generated/flutter/data_types_test.snippet.realm-value-from.dart :language: dart - - .. _flutter-access-realm-value-type: Access RealmValue Data Type @@ -414,7 +411,10 @@ To access the data stored in a ``RealmValue``, you can use: You can check the type of data currently stored in a ``RealmValue`` property by accessing the ``type`` property. Starting with Flutter SDK v2.0.0, this returns a ``RealmValueType`` enum. In earlier SDK versions, the SDK returned a -``RealmValue.value.runtimeType``. +``RealmValue.value.runtimeType``. + +The following example uses ``RealmValueType`` to run calculations based on the +data type. .. literalinclude:: /examples/generated/flutter/data_types_test.snippet.realm-value-type.dart :language: dart diff --git a/source/sdk/flutter/realm-database/model-data/define-realm-object-schema.txt b/source/sdk/flutter/realm-database/model-data/define-realm-object-schema.txt index 6560dad7e3e..6934cdf7a9f 100644 --- a/source/sdk/flutter/realm-database/model-data/define-realm-object-schema.txt +++ b/source/sdk/flutter/realm-database/model-data/define-realm-object-schema.txt @@ -269,10 +269,11 @@ data when modeling a variable event log object: .. literalinclude:: /examples/generated/flutter/define_realm_model_test.snippet.unstructured-data-model.dart :language: dart :emphasize-lines: 9 - :caption: EventLog data model + :caption: Data model .. io-code-block:: :copyable: true + :caption: Create unstructured data .. input:: /examples/generated/flutter/define_realm_model_test.snippet.create-unstructured-data-example.dart :language: dart