Skip to content

Commit

Permalink
DOCSP-35364: Flutter SDK - Nested Collections of Mixed Data (#3201)
Browse files Browse the repository at this point in the history
## Pull Request Info

> **NOTE:** This PR is temporarily using an alpha snapshot - unit tests
will fail. Upgrading to v2.0.0 release, once it's available, may impact
some of the code examples.

Jira ticket: https://jira.mongodb.org/browse/DOCSP-35364

- [Define an Object
Schema](https://preview-mongodbcbullinger.gatsbyjs.io/realm/docsp-35364-dart-collections-mixed/sdk/flutter/realm-database/model-data/define-realm-object-schema/#model-unstructured-data):
Added new "Modeled Unstructured Data" section with example
- [Data
Types](https://preview-mongodbcbullinger.gatsbyjs.io/realm/docsp-35364-dart-collections-mixed/sdk/flutter/realm-database/model-data/data-types/#realmvalue):
Updated ``RealmValue`` mixed data type section with new info and
examples for collections in mixed; added note re. breaking change
(**links to migration page from a separate PR**)
-
[Read](https://preview-mongodbcbullinger.gatsbyjs.io/realm/docsp-35364-dart-collections-mixed/sdk/flutter/crud/read/#query-nested-collections-of-mixed-data):
Add new Query Nested Collections of Mixed Data (**needs query examples
added as part of RQL ticket**)

### Reminder Checklist

Before merging your PR, make sure to check a few things.

- [x] Did you tag pages appropriately?
  - genre
  - meta.keywords
  - meta.description
- [x] Describe your PR's changes in the Release Notes section
- [x] Create a Jira ticket for related docs-app-services work, if any

### Release Notes

- **Flutter** SDK
- Realm Database/Model Data/Define an Object Schema: New "Modeled
Unstructured Data" section added with a tested, Bluehawked code example
to document nested collections of mixed data.
- Realm Database/Model Data/Data Types: ``RealmValue`` section updated
to document the new support for nested collections of mixed data and
additional changes to the data type. Tested, Bluehawked code examples
were updated or added.
- Realm Database/CRUD/Read: New "Query Nested Collections of Mixed Data"
section added with a tested, Bluehawked code example to document new RQL
queries for nested collections.

### Review Guidelines


[REVIEWING.md](https://github.com/mongodb/docs-realm/blob/master/REVIEWING.md)

---------

Co-authored-by: Nikola Irinchev <[email protected]>
  • Loading branch information
cbullinger and nirinchev authored Mar 21, 2024
1 parent ee0c65b commit 4fe38a1
Show file tree
Hide file tree
Showing 22 changed files with 942 additions and 224 deletions.
2 changes: 1 addition & 1 deletion examples/dart/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A simple command-line application using Realm Dart SDK.
publish_to: none

environment:
sdk: "^3.0.2"
sdk: "^3.1.0"

dependencies:
realm_dart: ^2.0.0
Expand Down
209 changes: 165 additions & 44 deletions examples/dart/test/data_types_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:realm_dart/realm.dart';
import 'package:test/expect.dart';
import 'package:test/scaffolding.dart';
import 'dart:typed_data';
import 'dart:developer';

import 'utils.dart';

Expand Down Expand Up @@ -70,10 +71,18 @@ class _RealmValueExample {
@Indexed()
late RealmValue singleAnyValue;
late List<RealmValue> listOfMixedAnyValues;
late Set<RealmValue> setOfMixedAnyValues;
late Map<String, RealmValue> mapOfMixedAnyValues;
}

// :snippet-end:

@RealmModel()
class _RealmValueCollectionExample {
@Indexed()
late RealmValue singleAnyValue;
}

// :snippet-start: datetime-model
@RealmModel()
class _Vehicle {
Expand Down Expand Up @@ -149,6 +158,7 @@ main() {
final object = UuidPrimaryKey(myId);
// :snippet-end:
expect(myId.toString(), isA<String>());
expect(myId, object.id);
});
test('ObjectId', () {
// :snippet-start: objectid-use
Expand All @@ -157,54 +167,165 @@ main() {
// :snippet-end:
expect(object.id.toString(), isA<String>());
});
test("RealmValue - RealmValue.from()", () {
// :snippet-start: realm-value-from
final realm = Realm(Configuration.local([RealmValueExample.schema]));

realm.write(() {
realm.addAll([
RealmValueExample(
singleAnyValue: RealmValue.from(1),
listOfMixedAnyValues: [Uuid.v4(), "abc", 123].map(RealmValue.from)),
RealmValueExample(
singleAnyValue: RealmValue.nullValue(),
listOfMixedAnyValues: ["abc", 123].map(RealmValue.from))
]);
});
// :snippet-end:
group('RealmValue - ', () {
test("RealmValue.from()", () {
// :snippet-start: realm-value-from
final realm = Realm(Configuration.local([RealmValueExample.schema]));

expect(
realm.query<RealmValueExample>("singleAnyValue.@type == 'int'").first,
isNotNull);
expect(
realm.query<RealmValueExample>("singleAnyValue.@type == 'Null'").first,
isNotNull);
cleanUpRealm(realm);
});
test("RealmValue - RealmValue.type and RealmValue.value", () {
final realm = Realm(Configuration.local([RealmValueExample.schema]));
realm.write(() {
realm.addAll([
RealmValueExample(
realm.write(() {
// Use 'RealmValue.from()' to set values
var anyValue = realm.add(RealmValueExample(
// Add a single `RealmValue` value
singleAnyValue: RealmValue.from(1),
listOfMixedAnyValues: [Uuid.v4(), "abc", 123].map(RealmValue.from)),
RealmValueExample(
// 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')
}));

// Use 'RealmValue.nullValue()' to set null values
var anyValueNull = realm.add(RealmValueExample(
singleAnyValue: RealmValue.nullValue(),
listOfMixedAnyValues: ["abc", 123].map(RealmValue.from))
]);
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:
// :snippet-end:
cleanUpRealm(realm);
});
var calledCount = 0;
// :snippet-start: realm-value-type-value
final data = realm.all<RealmValueExample>();
for (var obj in data) {
if (obj.singleAnyValue.type == int) {
print(obj.singleAnyValue.value.toString());
calledCount++; // :remove:
test("RealmValueType and RealmValue.value", () {
final realm = Realm(Configuration.local([RealmValueExample.schema]));
realm.write(() {
realm.addAll([
RealmValueExample(
singleAnyValue: RealmValue.from(1),
listOfMixedAnyValues:
[Uuid.v4(), 'abc', 123].map(RealmValue.from),
mapOfMixedAnyValues: {
'1': RealmValue.from(123),
'2': RealmValue.from('abc')
}),
RealmValueExample(
singleAnyValue: RealmValue.nullValue(),
listOfMixedAnyValues: [null, null].map(RealmValue.from),
mapOfMixedAnyValues: {'null': RealmValue.nullValue()})
]);
});
var approximateAge = 0;
// :snippet-start: realm-value-type
final data = realm.all<RealmValueExample>();
for (var obj in data) {
final anyValue = obj.singleAnyValue;
// Access the RealmValue.type property
switch (anyValue.type) {
// Work with the returned RealmValueType enums
case RealmValueType.int:
approximateAge = DateTime.now().year - anyValue.as<int>();
break;
case RealmValueType.dateTime:
approximateAge =
(DateTime.now().difference(anyValue.as<DateTime>()).inDays /
365)
.floor();
break;
case RealmValueType.string:
final birthday = DateTime.parse(anyValue.as<String>());
approximateAge =
(DateTime.now().difference(birthday).inDays / 365).floor();
break;
// Handle other possible types ...
default:
log('Unhandled type: ${anyValue.type}');
}
}
}
// :snippet-end:
expect(calledCount, 1);
cleanUpRealm(realm);
// :snippet-end:
expect(approximateAge, 2023);
int sum = 0;
String combinedStrings = '';
// :snippet-start: realm-value-value
for (var obj in data) {
for (var mixedValue in obj.listOfMixedAnyValues) {
// Use RealmValue.value to access the value
final value = mixedValue.value;
if (value is int) {
sum = sum + value;
expect(sum, 123); // :remove:
} else if (value is String) {
combinedStrings += value;
expect(combinedStrings, 'abc'); // :remove:
}

// Use RealmValue.as<T> to cast value to a specific type
try {
final intValue = mixedValue.as<int>();
sum = sum + intValue;
expect(sum, 246); // :remove:
} catch (e) {
log('Error casting value to int: $e');
}
}
}
// :snippet-end:
cleanUpRealm(realm);
});
test('Nested collections of mixed data', () {
final realm =
Realm(Configuration.local([RealmValueCollectionExample.schema]));

// :snippet-start: realm-value-nested-collections
realm.write(() {
realm.add(RealmValueCollectionExample(
// Set the RealmValue as a map of mixed data
singleAnyValue: RealmValue.from({
'int': 1,
// You can nest RealmValues in collections
'listOfInt': [2, 3, 4],
'mapOfStrings': {'1': 'first', '2': 'second'},
// You can also nest collections within collections
'mapOfMaps': [
{
'nestedMap_1': {'1': 1, '2': 2},
'nestedMap_2': {'3': 3, '4': 4}
}
],
'listOfMaps': [
{
'nestedList_1': [1, 2, 3],
'nestedList_2': [4, 5, 6]
}
]
})));
// :snippet-end:
final collectionsOfMixed =
realm.all<RealmValueCollectionExample>().first;
final mapValue = collectionsOfMixed.singleAnyValue.asMap();
expect(mapValue.length, 5);
expect(mapValue.containsKey('mapOfStrings'), true);
expect(mapValue['mapOfMaps']?.asList()[0].asMap().length, 2);
});
cleanUpRealm(realm);
});
});
test('DateTime', () {
final config = Configuration.local([Vehicle.schema]);
Expand Down Expand Up @@ -264,7 +385,7 @@ main() {
// Query RealmList with Realm Query Language
final playersWithBodyArmor =
realm.query<Player>("inventory.name == \$0", ['body armor']);
print("LEN " + playersWithBodyArmor.length.toString());
print("LEN ${playersWithBodyArmor.length}");
// :snippet-end:
expect(brave, 'brave');
expect(elvishSword.name, 'elvish sword');
Expand Down Expand Up @@ -441,7 +562,7 @@ main() {
// Add RealmObject to realm database
realm.write(() => realm.add(mapExample));

// Qeury for all MapExample objects
// Query for all MapExample objects
final realmMap = realm.all<MapExample>()[0];

// :remove-start:
Expand Down
52 changes: 52 additions & 0 deletions examples/dart/test/define_realm_model_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ class _Boat {
}
// :snippet-end:

// :snippet-start: unstructured-data-model
// Define class with a `RealmValue` property
@RealmModel()
class _EventLog {
@PrimaryKey()
late ObjectId id;

late String eventType;
late DateTime timestamp;
late String userId;
late RealmValue details;
}
// :snippet-end:

main() {
test('Create a new schema version', () {
// :snippet-start: schema-version
Expand All @@ -46,4 +60,42 @@ main() {
realm.close();
Realm.deleteRealm(config.path);
});

test('Create unstructured data', () {
final config = Configuration.local([EventLog.schema]);
final realm = Realm(config);

// :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',
'items': [
{'id': 1, 'name': 'Laptop', 'price': 1200.00},
{'id': 2, 'name': 'Mouse', 'price': 49.99}
],
'total': 1249.99
})));

final eventLog = realm.all<EventLog>().first;
final items = eventLog.details.asMap();
print('''
Event Type: ${eventLog.eventType}
Timestamp: ${eventLog.timestamp}
User ID: ${eventLog.userId}
Details:
Item:
''');
for (var item in items.entries) {
print('${item.key}: ${item.value}');
}
// :snippet-end:
expect(items['ipAddress']?.value.toString(), '192.168.1.1');
expect(items['total']?.value, 1249.99);
});
realm.close();
Realm.deleteRealm(config.path);
});
}
Loading

0 comments on commit 4fe38a1

Please sign in to comment.