Flutter SDK は、Dart 言語のデータ型、 BSON 型の限定的なサブセット、および UUID をサポートしています。
App Services スキーマで特定のデータ型が にどのようにマッピングされるかについては、BSON types ドキュメントの「 データモデル マッピングAtlas App Services 」 を参照してください。
Dart types
Realm は次の Dart タイプをサポートしています。
intdoubleboolStringDateTime
日時
Realm Flutter SDK でDateTimeを使用する場合、他の Dart 型と同様に、モデル内でそれを宣言できます。
() class _Vehicle { () late ObjectId id; late String nickname; late DateTime dateLastServiced; }
ただし、Realm が UTC のDateTimeを保存していることに注意することが重要です。 DateTimeを使用する場合は、保存する前に UTC で作成するか、 .toUtc()で変換する必要があります。 アプリケーションでそれが必要な場合は、Realm から読み取るときにローカルまたは希望のタイムゾーンに変換することができます。
// Create a Realm object with date in UTC, or convert with .toUtc() before storing final subaruOutback = realm.write<Vehicle>(() { return realm.add( Vehicle(ObjectId(), 'Subie', DateTime.utc(2022, 9, 18, 12, 30, 0))); }); final fordFusion = Vehicle(ObjectId(), 'Fuse', DateTime(2022, 9, 18, 8, 30, 0).toUtc()); realm.write(() { realm.add(fordFusion); }); // When you query the object, the `DateTime` returned is UTC final queriedSubaruOutback = realm.all<Vehicle>().query('nickname == "Subie"')[0]; // If your app needs it, convert it to Local() or the desired time zone final localizedSubieDateLastServiced = queriedSubaruOutback.dateLastServiced.toLocal();
Realm オブジェクトの参照
また、別の Realm から 1 つ以上の Realm オブジェクトを参照することもできます。 詳細については、関係プロパティのドキュメント を参照してください。
コレクション
Realm コレクションには、 Realm でサポートされているデータ型の 0 個以上のインスタンスが含まれます。 Realm コレクションでは、コレクション内のすべてのオブジェクトは同じタイプです。
Realm のクエリ言語を使用して、任意のコレクションをフィルタリングおよびソートできます。 コレクションはライブ オブジェクトであるため、Realm インスタンスの現在の状態を常に反映します。 コレクションの内容は、コレクションまたはその Realm に新しい要素が追加または削除されると更新されます。
変更通知をサブスクライブして、コレクション内の変更をリッスンすることもできます。
Realm には次のタイプのコレクションがあります。
RealmList
Realmオブジェクトには、サポートされている任意のデータ型のリストを含めることができます。Realm はRealmListデータ型を使用してデータを保存します。
RealmListのアイテムとしてRealmObjectsを含めると多対多の関係が表されます。
データベースからオブジェクトを削除すると、そのオブジェクトが存在していたすべての Realmlist からそのオブジェクトが削除されます。 したがって、 RealmObject型のRealmListに null 値が含まれることはありません。 また、 RealmListには同じRealmObjectへの複数の参照を含めることができます。
プリミティブ型のRealmListには null 値を含めることができます。 リストで null 値を許可しない場合は、リスト宣言で null 以外の型を使用します(たとえば、List<int> ではなくList<int?> を使用します)。
RealmListは可変であり、書込みトランザクション内でRealmListに要素を追加および削除できます。
スキーマへの RealmList の追加
Realm オブジェクト スキーマにRealmListを追加するには、Realm オブジェクト モデルでサポートされている任意の Realm データ型(他のコレクションを除く)にプロパティをタイプList<T> T定義します。
() class _Player { () late ObjectId id; late String username; // `inventory` property of type RealmList<Item> // where Items are other RealmObjects late List<_Item> inventory; // `traits` property of type RealmList<String> // where traits are Dart Strings. late List<String> traits; } () class _Item { () late ObjectId id; late String name; late String description; }
RealmList との連携
バージョン 2.0.0 での変更logical.getList() によるプロパティ名で RealmList を取得する
次の例は、RealmList の基本的な使用方法の一部を示しています。利用可能なすべてのメソッドの詳細については、RealmList の参照ドキュメントを参照してください。
final artemis = realm.write(() => realm.add(Player(ObjectId(), 'Art3mis', inventory: [ Item(ObjectId(), 'elvish sword', 'sword forged by elves'), Item(ObjectId(), 'body armor', 'protects player from damage'), ], traits: [ 'brave', 'kind' ]))); // Get a RealmList by property name with dynamic.getList() final inventory = artemis.dynamic.getList('inventory'); // Use RealmList methods to filter results RealmList<String> traits = artemis.traits; final brave = traits.firstWhere((element) => element == 'brave'); final elvishSword = artemis.inventory.where((item) => item.name == 'elvish sword').first; // Query RealmList with Realm Query Language final playersWithBodyArmor = realm.query<Player>("inventory.name == \$0", ['body armor']); print("LEN ${playersWithBodyArmor.length}");
RealmSet
Realmオブジェクトには、 別のコレクションを除くすべてのサポートされているデータ型のセットを含めることができます。Realm はRealmSetデータ型を使用してデータを保存します。RealmSetコレクションでは、すべての値は一意です。RealmSetは、Realm 固有のプロパティとメソッドを追加してネイティブのDart Setデータ型を拡張します。
RealmSetのアイテムとしてRealmObjectsを含めると多対多の関係が表されます。
RealmSetは可変であり、書込みトランザクション内でRealmSetに要素を追加および削除できます。
スキーマへの RealmSet の追加
Realm オブジェクト スキーマにRealmSetを追加するには、プロパティをタイプSet<T>として定義します。ここでは、 Tは Realm オブジェクト モデルでサポートされている Realm データ型(他のコレクションを除く)にできます。
スキーマで RealmSet を定義する場合、
プリミティブ型のセットは、null 可能または null 以外として定義できます。 たとえば、Realm スキーマでは、
Set<int>とSet<int?>の両方が有効です。RealmObject型とRealmValue型のセットは、null 以外の値のみを含めることができます。 たとえば、Set<RealmValue>は有効ですが、Set<RealmValue?>は有効ではありません。スキーマでセットを定義する場合、デフォルト値を定義することはできません。 たとえば、
Set mySet = {0,1,2}は無効です。
() class _RealmSetExample { late Set<String> primitiveSet; late Set<int?> nullablePrimitiveSet; late Set<_SomeRealmModel> realmObjectSet; } () class _SomeRealmModel { late ObjectId id; }
RealmSet との連携
バージョン 2.0.0 での変更logical.getSet() によるプロパティ名で RealmSet を取得します
次の例は、RealmSet の基本的な使用方法の一部を示しています。利用可能なすべてのメソッドの詳細については、RealmSet に関する参照ドキュメントを参照してください。
final realm = Realm( Configuration.local([RealmSetExample.schema, SomeRealmModel.schema])); // Pass native Dart Sets to the object to create RealmSets final setExample = RealmSetExample( primitiveSet: {'apple', 'pear'}, nullablePrimitiveSet: {null, 2, 3}, realmObjectSet: {SomeRealmModel(ObjectId())}); // Add RealmObject to database realm.write(() => realm.add(setExample)); // Once you add the sets, they are of type RealmSet RealmSet primitiveSet = setExample.primitiveSet; // Modify RealmSets of RealmObjects in write transactions realm.write(() { // Add element to a RealmSet with RealmSet.add() setExample.realmObjectSet.add(SomeRealmModel(ObjectId())); // Remove element from a RealmSet with RealmSet.remove() setExample.primitiveSet.remove('pear'); }); // Check if a RealmSet contains an element with RealmSet.contains() if (setExample.primitiveSet.contains('apple')) { print('Set contains an apple'); } // Get RealmSet by property name with dynamic.getSet() final getSetResult = setExample.dynamic.getSet('primitiveSet'); // Check number of elements in a RealmSet with RealmSet.length print( 'Set now has ${getSetResult.length} elements'); // Prints 'Set now has 1 elements' // Query RealmSets using Realm Query Language final results = realm.query<RealmSetExample>('\$0 IN nullablePrimitiveSet', [null]);
RealmMap
バージョン 1.7.0 の新機能。
RealmMap は、<String, T> のキーと値のペアを含むコレクションであり、T は SDK でサポートされている任意のデータ型です。パーセント エンコーディングを使用しない限り、マップキーに が含まれていないこと、. または$ で始まっていることはありません。
RealmMapは可変であり、書込みトランザクション内でRealmMapに要素を追加および削除できます。 変更リスナーを使用して、RealmMap エントリの変更をリッスンできます。
スキーマへの RealmMap の追加
Realm オブジェクト スキーマにRealmMapを追加するには、Realm オブジェクト モデルでサポートされている任意の Realm データ型(他のコレクションを除く)にプロパティをタイプRealmMap<String, T> T定義します。
() class _MapExample { late Map<String, int> map; late Map<String, int?> nullableMap; }
RealmMap との連携
バージョン 2.0.0 での変更logical.getMap() によるプロパティ名で RealmMap を取得します
次の例は、RealmMap の基本的な使用方法の一部を示しています。利用可能なすべてのメソッドの詳細については、RealmMap に関する参照 ドキュメントを参照してください。
final realm = Realm(Configuration.local([MapExample.schema])); // Pass native Dart Maps to the object to create RealmMaps final mapExample = MapExample( map: { 'first': 1, 'second': 2, 'third': 3, }, nullableMap: { 'first': null, 'second': 2, 'third': null, }, ); // Add RealmObject to the database realm.write(() => realm.add(mapExample)); // Once you add maps, they are of type RealmMap RealmMap map = mapExample.map; // Modify RealmMaps in write transactions realm.write(() { // Update value by key with .update() or [value] = newValue mapExample.nullableMap['second'] = null; mapExample.map.update('first', (value) => 5); mapExample.nullableMap.update('fourth', (v) => 4, ifAbsent: () => null); // Add a new map entry with .addEntries() const newMap = {'fourth': 4}; mapExample.map.addEntries(newMap.entries); }); // Check a RealmMap with .containsKey() or .containsValue() if (mapExample.map.containsKey('first')) { print('Map contains key "first"'); } else if (mapExample.map.containsValue(null)) { print('Map contains null value'); } else { print('These aren\'t the maps you\'re looking for'); } // Get a RealmMap by property name with dynamic.getMap() final getPrimitiveMap = mapExample.dynamic.getMap('map'); // Check the number of elements in a RealmMap with RealmMap.length print( 'Map contains ${getPrimitiveMap.length} elements'); // Prints 'Map contains 4 elements' // Query RealmMaps using Realm Query Language final results = realm.query<MapExample>('map.first == \$0', [5]);
RealmResults
RealmResultsコレクションは、 クエリ操作の遅延評価された結果を表します。RealmList と違い、結果は不変です。 結果コレクションの要素を追加または削除することはできません。これは、結果コレクションの内容がデータベースに対するクエリによって決定されるためです。
Realm.all() と Realm.query() RealmResults を返すRealm のクエリの詳細については、読み取り操作を参照してください。
RealmResults<Player> players = realm.all<Player>(); RealmResults<Player> bravePlayers = realm.query<Player>('ANY traits == \$0', ['brave']);
結果は遅延評価される
Realm は、結果コレクションの要素にアクセスするなど、そのクエリの結果を実際に要求した場合にのみクエリを実行します。 この遅延評価により、大規模なデータセットや複雑なクエリを処理するための洗練された高性能なコードを書込むことができます。
コレクションはライブ
ライブ オブジェクトと同様に、Realm コレクションは通常ライブです。
ライブ結果のコレクションには、関連付けられているクエリの現在の結果が常に反映されます。
RealmObjectsのライブリストは、Realm インスタンスの関係の現在の状態を常に反映します。
ただし、コレクションがライブではない場合が 2 つあります。
コレクションは未管理です: Realm にまだ追加されていない、または Realm からコピーされた Realm オブジェクトの
RealmListプロパティ。コレクションは固定されています。
ライブ コレクションの コレクションの変更をリッスンすると組み合わせると、クリーンでリアクティブなコードが可能になります。 たとえば、ビューにクエリの結果が表示されるとします。 ビュー クラスで結果コレクションへの参照を保持すると、結果コレクションを更新したり最新であることを検証したりすることなく、必要に応じて結果コレクションを読み取ることができます。
重要
インデックスは変更される可能性があります
結果は自動的に更新されるため、 コレクション内のオブジェクトの位置インデックスやコレクション内のオブジェクトの数を保存しないでください。 保存されたインデックスやカウント値は、使用する時間によって期限切れになる可能性があります。
サポートされている追加のデータ型
ObjectId
ObjectId は、オブジェクトの識別子として使用できる MongoDB 固有の 12 バイトの一意の値です。 ObjectId はインデックス作成可能であり、プライマリキーとして使用できます。
プロパティを ObjectId として定義するには、オブジェクトモデルでそのタイプをObjectIdとして設定します。
() class _ObjectIdPrimaryKey { () late ObjectId id; }
オブジェクトの一意の識別子プロパティを設定するには、 ObjectId()を呼び出します。 または、 ObjectId()に string を渡して、一意の識別子プロパティを特定の値に設定します。
final id = ObjectId(); final object = ObjectIdPrimaryKey(id);
UUID
UUID(汎用一意識別子)は 16 バイトの一意の値です。オブジェクトの識別子として UUID を使用できます。UUID はインデックス作成可能で、プライマリキーとして使用できます。
注意
ObjectId の代わりに UUID を使用する
一般的に、一意の識別子として機能するフィールドにはUUIDを使用できます。 MongoDB に保存されていないデータを移行する場合、オブジェクトの一意の識別子がすでにUUIDタイプになっている可能性があるため、 UUIDを使用すると特に便利です。 あるいは、MongoDB にすでに存在するデータのコレクションには、 ObjectIdを使用すると便利な場合があります。
プロパティを UUID として定義するには、オブジェクトモデルでそのタイプをUuidとして設定します。
() class _UuidPrimaryKey { () late Uuid id; }
オブジェクトの一意の識別子プロパティをランダム値に設定するには、いずれかのUuidメソッドを呼び出して UUID を作成しますUuid.v4()など)。
final myId = Uuid.v4(); final object = UuidPrimaryKey(myId);
Decimal128
Dartにはネイティブ 10 進数タイプはありません。IEEE-754 128の128 ビット実装である Decimal を使用できます。10 進数型を定義する場合は、Decimal128 BSON型を使用します。
Decimal128を使用する場合は、Dart compareTo()メソッドが Dart double型をシミュレートする合計順序付けを実装していることに注意してください。 つまり、 compareTo()を使用する場合、次の条件が当てはまります。
すべての
NaN値は等しく、数値より大きいと見なされます。-Decimal128.zeroはDecimal128.zero(および整数 0)より小さいが、ゼロ以外の負の値より大きいです。負の無限大は他のすべての値よりも小さく、正の無限大はすべての NaN 以外の値より大きいです。
他のすべての値は、数値を使用して比較されます。
RealmValue
重要
Flutter SDK v 2.0.0 RealmValue の変更
Flutter SDK バージョン2.0.0 は、 RealmValueのListまたはMapタイプを許可するようにRealmValueをアップデートし、非構造化データをモデル化する際の柔軟性を高めます。 詳細については、「非構造化データのモデル化」を参照してください。
このアップデートには次の重大な変更も含まれており、v 2.0.0以降にアップグレードするとアプリに影響する可能性があります。
RealmValue.typeは、RealmValueTypeではなくTypeの列挙型になりました。RealmValue.uint8Listの名前がRealmValue.binaryに変更されます。
既存のアプリを以前のバージョンから v 2.0.0以降にアップグレードする方法の詳細については、「 Flutter SDK v 2.0.0へのアップグレード 」を参照してください。
RealmValueデータ型は、埋め込みオブジェクトを除く他の有効なデータ型を表す混合データ型です。Flutter SDK v2.0.0以降では、RealmValue は List<RealmValue> または Map<String, RealmValue> を表すことができます。
RealmValue プロパティの定義
RealmValueプロパティを定義するには、オブジェクトモデルでそのタイプを設定します。 RealmValueはインデックス作成可能ですが、プライマリキーにすることはできません。 You can also define properties as collections (lists, sets, or maps) of type RealmValue.
() class _RealmValueExample { () late RealmValue singleAnyValue; late List<RealmValue> listOfMixedAnyValues; late Set<RealmValue> setOfMixedAnyValues; late Map<String, RealmValue> mapOfMixedAnyValues; }
注意
RealmValue は null 値ではないが null 値を含めることができます
Realm オブジェクト スキーマを定義する場合、null 可能なRealmValueを作成することはできません。 ただし、 RealmValueプロパティに null 値を含める場合は、特別なRealmValue.nullValue()プロパティを使用できます。
RealmValue の作成
Realm オブジェクトにRealmValueを追加するには、データに対してRealmValue.from()を呼び出すか、null 値を設定するにはRealmValue.nullValue()を呼び出します。
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') })); // Use 'RealmValue.nullValue()' to set null values var anyValueNull = realm.add(RealmValueExample( singleAnyValue: RealmValue.nullValue(), listOfMixedAnyValues: [null, null].map(RealmValue.from), setOfMixedAnyValues: {RealmValue.nullValue()}, mapOfMixedAnyValues: {'null': RealmValue.nullValue()}));
RealmValue データ型にアクセス
バージョン2.0.0での変更: RealmValueType列挙型はRealmValue.typeに置き換わります。 RealmValue.binaryはRealmValue.uint8List に置き換えられます。
RealmValueに保存されているデータにアクセスするには、次のコマンドを使用します。
RealmValue.valueはObject?を返します。RealmValue.as<T>は、 データを取得し、それを目的の型にキャストします。
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; } else if (value is String) { combinedStrings += value; } // Use RealmValue.as<T> to cast value to a specific type try { final intValue = mixedValue.as<int>(); sum = sum + intValue; } catch (e) { log('Error casting value to int: $e'); } } }
typeプロパティにアクセスすると、 RealmValueプロパティに現在保存されているデータ型を確認できます。 Flutter SDK v 2.0.0以降、これはRealmValueType列挙型を返します。 以前の SDK バージョンでは、SDK はRealmValue.value.runtimeTypeを返しました。
次の例では、 RealmValueTypeを使用して、 データ型に基づいて計算を実行します。
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}'); } }
混合としてのコレクション
バージョン2.0.0での変更: RealmValueプロパティには、混合データのリストまたはマップを含めることができます。
バージョン2.0.0以降では、混合データ型は混合要素のコレクション(リストまたはマップであるが、セットではない)を保持できます。 混合コレクションを使用して、非構造化データまたは変数データをモデル化できます。 詳細については、「非構造化データのモデル化 」を参照してください。
混合コレクションは最大100レベルまでネストできます。
通常のコレクションと同様に、混合コレクション プロパティをクエリし、 変更のリスナーを登録できます。
混合コレクション要素を個別に検索して更新できます
セットまたは埋め込みオブジェクトを混合コレクションに保存することはできません。
混合コレクションを使用するには、データモデルで 混合型プロパティを定義します 。 次に、 RealmValue.from()を使用してリストまたはマップ コレクションを作成します。
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] } ] })));
Uint8List
Uint8List は、dart:typed_data のバイナリデータ型です。このデータ型は、オブジェクトモデルとプロパティ値で使用できます。
プロパティをUint8Listとして定義するには、まずdart:typed_dataをインポートする必要があります。 次に、オブジェクトモデルでオブジェクトのタイプをUint8Listとして設定します。
() class _BinaryExample { late String name; late Uint8List requiredBinaryProperty; late Uint8List? nullableBinaryProperty; }
Realm オブジェクトにUint8Listを追加するには、データに対してUint8List.fromList()を呼び出します。
final realm = Realm(Configuration.local([BinaryExample.schema])); realm.write(() { realm.addAll([ BinaryExample("Example binary object", Uint8List.fromList([1, 2])) ]); });
埋め込みオブジェクト
Realm は、埋め込まれた各オブジェクトを親オブジェクト内のネストされたデータとして扱います。 埋め込みオブジェクトは、親オブジェクトのライフサイクルを継承します。 独立した Realm オブジェクトとして存在することはできません。 埋め込みオブジェクトには、次のプロパティがあります。
埋め込みオブジェクトは、親オブジェクトが削除されるか、親がその埋め込みを参照しなくなると削除されます。
埋め込みオブジェクトを別の親オブジェクトに再割り当てすることはできません。
複数の親オブジェクトから埋め込みオブジェクトにリンクすることはできません。
埋め込みオブジェクトをクエリするには、その親オブジェクトを通じてアクセスする必要があります。
ObjectType.embeddedObject を @RealmModel() アノテーションに渡すことで埋め込みオブジェクトを宣言します。埋め込みオブジェクトは、親オブジェクトの RealmModel で定義するときに null 可能である必要があります。また、埋め込みオブジェクトのスキーマをRealm の 構成 に含める必要があります。
次の例は、Realm スキーマで埋め込みオブジェクトをモデル化する方法を示しています。 _Addressモデルは_Personモデルに埋め込まれています。
// The generated `Address` class is an embedded object. (ObjectType.embeddedObject) class _Address { late String street; late String city; late String state; late String country; } () class _Person { () late ObjectId id; late String name; // Embedded object in parent object schema late _Address? address; // Must be nullable }
親プロパティを使用して、埋め込みオブジェクトの親にアクセスできます。
次の例は、埋め込みオブジェクトを操作する際における固有の考慮事項を示しています。 この例では、上記スキーマの_Address RealmModelから生成されたAddress埋め込みオブジェクトを使用します。
// Both parent and embedded objects in schema final realm = Realm(Configuration.local([Person.schema, Address.schema])); // Create an embedded object. final joesHome = Address("500 Dean Street", "Brooklyn", "NY", "USA"); final joePrimaryKey = ObjectId(); final joe = Person(joePrimaryKey, "Joe", address: joesHome); realm.write(() => realm.add(joe)); // Update an embedded object property. realm.write(() { joe.address?.street = "800 Park Place"; }); // Query a collection of embedded objects. // You must access the embedded object through the parent RealmObject type. final peopleWithNewYorkHomes = realm.query<Person>("address.state = 'NY'"); // Overwrite an embedded object. // Also deletes original embedded object from realm. final joesNewHome = Address("12 Maple Way", "Toronto", "ON", "Canada"); realm.write(() { joe.address = joesNewHome; }); // You can access the parent object from an embedded object. final thePersonObject = joesNewHome.parent; // Delete embedded object from parent object. realm.write(() => realm.delete(joe.address!)); // Add address back for the following example. final anotherNewHome = Address("202 Coconut Court", "Miami", "FL", "USA"); realm.write(() { joe.address = anotherNewHome; }); // Deleting the parent object also deletes the embedded object. realm.write(() => realm.delete(joe));
例
次のモデルには、サポートされているデータ型の一部が含まれています。
part 'car.realm.dart'; // The generated `Address` class is an embedded object. (ObjectType.embeddedObject) class _Address { late String street; late String city; late String state; late String country; } () class _Person { () late ObjectId id; late String name; // Embedded object in parent object schema late _Address? address; // Must be nullable } () class _Car { () late ObjectId id; String? licensePlate; bool isElectric = false; double milesDriven = 0; late List<String> attributes; late _Person? owner; }