Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

同期された Realm へのデータの書き込み - Flutter SDK

項目一覧

  • 同期するデータの決定
  • App Services の構成
  • クライアント データモデルと構成
  • 同期された Realm への書き込み
  • 成功した書込み
  • 書込みの考慮
  • クエリ サブスクリプションに一致しない書込み (write)
  • 権限に一致しない書込み

Flexible Sync を使用して同期された Realm にデータを書き込む場合、ローカル Realm への書き込みと同じ API を使用できます。 ただし、アプリケーションを開発する際に注意すべき動作の違いがいくつかあります。 Realm へのデータの読み取りと書込みの詳細については、「 データの読み取りと書込み 」を参照してください。

同期された Realm に書き込む場合、書き込み操作は次の両方と一致する必要があります。

  • 同期サブスクライブ クエリ。
    • 書込み (write) 操作がサブスクライブ内のクエリと一致しない場合、書込み (write) は非致命的な置換書込みエラー(ErrorCompentingWrite)で元に戻ります。

    • 書込みエラーの修正と回避方法の詳細については、 の書込み保証、セクションを参照してください。

  • App Services App での権限
    • 権限式に一致しないデータを書込み (write) しようとすると、書込み (write) は致命的でない権限拒否エラーで元に戻ります。 クライアントでは、これはエラーとして表示されます(ErrorCompentingWrite)。 サーバーでは、 ロールの書込みフィルターによって書込み (write) が拒否されたかどうかの詳細を確認できます。

    • アプリの権限の設定の詳細については、App Services ドキュメントの「ロールベースの権限」および「 Device Sync 権限ガイド 」を参照してください。

権限拒否エラー、書込み (write) エラー、およびその他の Device Sync エラーのタイプの詳細については、App Services ドキュメントの「同期エラー 」を参照してください。

このページの例では、次の Device Sync 構成を持つ Atlas App Services アプリと、次の Realm SDK データモデルとサブスクリプションを持つクライアント アプリを使用します。

Device Sync は、次のクエリ可能なフィールドで構成されています。

  • _id (常に含まれる)

  • miles

  • ownerId

App Services App には、ユーザーが自分のデータのみを読み書きできるように構成された権限があります。

{
"name": "owner-read-write",
"apply_when": {},
"document_filters": {
"read": { "ownerId": "%%user.id" },
"write": { "ownerId": "%%user.id" }
},
"read": true,
"write": true
}

このページの例では、次のスキーマを使用します。

@RealmModel()
class _Car {
@MapTo("_id")
@PrimaryKey()
late ObjectId id;
// This is the queryable field
late String ownerId;
late String make;
late String? model;
late int? miles;
}

そのスキーマを使用して、例では 同期された Realm を構成し、このサブスクリプション クエリに一致するオブジェクトを同期します。

final app = App(AppConfiguration(APP_ID));
final user = await app.logIn(Credentials.anonymous());
final config = Configuration.flexibleSync(user, [Car.schema]);
final realm = Realm(config);
// Add subscriptions
realm.subscriptions.update((mutableSubscriptions) {
// Get Cars from Atlas that match the Realm Query Language query.
// Uses the queryable field `miles`.
// Query matches cars with less than 100 miles or `null` miles.
final newCarQuery = realm.query<Car>("miles < 100 OR miles == \$0", [null]);
mutableSubscriptions.add(newCarQuery, name: "new-car-subscription");
});
await realm.subscriptions.waitForSynchronization();

Flexible Sync レルムへの書き込みは、大きく次の 2 つのカテゴリのいずれかに分類されます。

  • 正常な書込み: 書込まれたオブジェクトは、クエリ サブスクリプションとユーザーの権限の両方に一致します。 オブジェクトは Realm に正常に書込み (write) され、App Services バックエンドや他のデバイスに正常に同期されます。

  • 書込みの修正 : 書込まれたオブジェクトがサブスクリプション クエリと一致しない場合、またはユーザーが書込みを実行するための十分な権限を持っていない場合、Realm は不正な書込みを元に戻します。

書き込みがApp Services の権限とクライアント内のFlexible Sync サブスクライブ クエリの両方に一致する場合、Realm Flutter SDK は同期された Realm にオブジェクトを正常に書込むことができます。 このオブジェクトは、デバイスがネットワークに接続している場合に App Services バックエンドと同期します。

// Per the Device Sync permissions, users can only read and write data
// where the `Car.ownerId` property matches their own user ID.
final userId = user.id;
realm.write(() {
// WRITE SUCCEEDS
// `newCar` is successfully written to the realm and synced to Atlas
// because it's data matches the subscription query (miles < 100)
// and it's `ownerId` field matches the user ID.
final newCar = Car(ObjectId(), userId, 'Toyota', miles: 2);
realm.add(newCar);
});

最初は成功したように見えた書込みが、実際には不正な書込みである場合もあります。 このような場合、オブジェクトはデータベースに書込み (write) を行いますが、データベースがバックエンドに同期すると、Realm は修復書込み (write) と呼ばれる致命的でないエラー操作で書込みを元に戻します。 次の場合、書込み保証 (write) が発生する可能性があります。

  • 書き込みが クエリ サブスクリプションと一致しません: 書き込まれたオブジェクトはユーザーの権限と一致しますが、クエリ サブスクリプションには一致しません。

  • 書込み (write)は権限と一致しません: 書込まれたオブジェクトはクエリ サブスクリプションと一致しますが、ユーザーの権限と一致しません。

詳しくは、クエリ サブスクリプションの範囲外のデータ、またはユーザーの権限と一致しないデータを書き込むと、次の状況が発生します。

  1. クライアント Realm には「不正」書込みの概念が存在しないため、書込みは最初は Realm が App Services バックエンドで変更セットを解決するまで成功します。

  2. 同期時に、サーバーはルールと権限を適用します。 サーバーは、ユーザーには書込み (write) を実行する権限がないと判断します。

  3. サーバーは、「書き込み補正」と呼ばれる元に戻す操作をクライアントに送信します。

  4. クライアントの Realm は不正な書込み (write) 操作を元に戻します。

特定のオブジェクトへの不正な書込み (write) と、それに対応する書込み (write) の間にあるクライアント側の書込み (write) は、いずれもなくなります。

実際には、オブジェクトが Realm に書き込まれ、サーバーがクライアントに補完書込み (write) を送信した後に消えます。

権限拒否エラー、書込み (write) エラー、およびその他の Device Sync エラーのタイプの詳細については、App Services ドキュメントの「同期エラー 」を参照してください。

App Services ログには、 の書き込みエラーが発生する理由に関する詳細情報が記載されています。

サブスクリプション クエリに一致する場合にのみ、 Flexible Sync レルムにオブジェクトを書き込むことができます。 サブスクリプション クエリに一致しない書込み (write) を実行すると、Realm は最初に オブジェクトを書込みますが、その後は補完的な書込み (write) を実行します。 これは、サブスクライブ クエリに一致しない不正な書込み (write) を元に戻す、致命的でない操作です。

クエリ サブスクリプションに一致しないオブジェクトを書き込みをクリックする場合は、オブジェクトがクエリ サブスクリプションに一致する別のレルムを開きます。 あるいは、権限やサブスクリプション クエリを強制しないローカル Realmに オブジェクトを書込むこともできます。

上記の同期された Realm の構成では、このオブジェクトを書込み (write) しようとすると、クエリ サブスクリプションは一致しません。

final carId = ObjectId();
final ownerId = app.currentUser!.id;
realm.write(() {
// WRITE REVERTED BY QUERY SUBSCRIPTION COMPENSATING WRITE
// `oldCar` is initially written to the realm, then later removed
// in a compensating write when the server processes the write.
// This is because the `miles` property of `oldCar` doesn't match
// the subscription query, which is only for cars with less than 100 miles.
final oldCar = Car(carId, ownerId, 'Honda', miles: 90000);
realm.add(oldCar);
});
// Let changes sync to and from server
await realm.syncSession.waitForUpload();
await realm.syncSession.waitForDownload();
final noCar = realm.find<Car>(carId);
// The Car is no longer in the realm because of
// the compensating write from the server.
expect(noCar, isNull);

このシナリオでのクライアント側ログのエラー メッセージは次のとおりです。

[INFO] Realm: Connection[1]: Session[1]: Received: ERROR "Client attempted
a write that is outside of permissions or query filters; it has been reverted"
(error_code=231, try_again=true, error_action=Warning)
[INFO] Realm: Connection[1]: Session[1]: Reporting compensating write
for client version 21 in server version 2877: Client attempted a write that
is outside of permissions or query filters; it has been reverted
[ERROR] Realm: SyncSessionError message: Client attempted a write that
is outside of permissions or query filters; it has been reverted
Logs: https://services.cloud.mongodb.com/groups/5f60207f14dfb25d23101102/apps/639340a757271cb5e3a0f0cf/logs?co_id=6424433efb0c6bbcc330347c
category: SyncErrorCategory.session code: SyncSessionErrorCode.compensatingWrite isFatal: false

このシナリオでの App Services ログのエラー メッセージは次のとおりです。

Error:
Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231)
Details:
{
"Car": {
"6424433fd4d9f52ee93ad590": "write to \"6424433fd4d9f52ee93ad590\" in table \"Car\" not allowed; object is outside of the current query view"
}
}

オブジェクトがユーザーのサーバー側書込み権限と一致しない場合は、クライアントに書込み (write) しようとすると、補間書込み (write) エラーがtriggerされることもあります。

クライアントでは、このタイプの書込みはクエリ サブスクリプションに一致しない書込みと同じように動作します。 実際には、これは書き込みが成功しているように見えても、Realm が App Services バックエンドと同期し、書き込みを実行すると、オブジェクトは「表示されなくなります」。

上記で詳細に説明した Device Sync 構成の権限を考慮すると、 ownerIdプロパティがログイン ユーザーのuser.idと一致しないオブジェクトを書込み (write) しようとすると、正規表現ではありません。

final carId = 'someOtherId';
realm.write(() {
// WRITE REVERTED BY PERMISSION ERROR
// `otherUsersCar` is initially written to the realm, then removed upon synchronization
// because it's `ownerId` property doesn't match the user ID of the user
// making the request.
final otherUsersCar = Car(ObjectId(), carId, 'Ford');
realm.add(otherUsersCar);
});
// sync changes
await realm.syncSession.waitForUpload();
await realm.syncSession.waitForDownload();
final noCar = realm.find<Car>(carId);
// The Car is no longer in the realm because of
// the compensating write from the server.
expect(noCar, isNull);

このシナリオでのクライアント エラーは、Atlas App Services の権限外のオブジェクトを書込み (write) しようとする場合と同じです。

[INFO] Realm: Connection[1]: Session[1]: Received: ERROR "Client attempted
a write that is outside of permissions or query filters; it has been reverted"
(error_code=231, try_again=true, error_action=Warning)
[INFO] Realm: Connection[1]: Session[1]: Reporting compensating write
for client version 25 in server version 2879: Client attempted a write that
is outside of permissions or query filters; it has been reverted
[ERROR] Realm: SyncSessionError message: Client attempted a write that
is outside of permissions or query filters; it has been reverted
Logs: https://services.cloud.mongodb.com/groups/5f60207f14dfb25d23101102/apps/639340a757271cb5e3a0f0cf/logs?co_id=6424433efb0c6bbcc330347c
category: SyncErrorCategory.session code: SyncSessionErrorCode.compensatingWrite isFatal: false

App Services ログのエラー メッセージは、クエリ サブスクリプションの問題ではなく、権限の問題であることを判断するのに役立つ追加情報を提供します。 この例では、エラー メッセージは、 オブジェクトがユーザーのロールと一致しないことを示しています。

Error:
Client attempted a write that is outside of permissions or query filters;
it has been reverted (ProtocolErrorCode=231)
Details:
{
"Car": {
"6424433fd4d9f52ee93ad591": "write to \"6424433fd4d9f52ee93ad591\" in table \"Car\" was denied by write filter in role \"owner-read-write\""
}
}

戻る

サブスクリプションの管理