同期された Realm へのデータの書き込み - C++ SDK
項目一覧
Flexible Syncを使用して同期された Realm にデータを書き込む場合、同期されていない Realm で CRUD 操作を実行する場合と同じ API を使用できます。 ただし、アプリケーションを開発する際に注意すべき動作の違いがいくつかあります。
同期された Realm に書き込む場合、書き込み操作は次の両方と一致する必要があります。
同期サブスクライブのクエリ
App Services App での権限
クエリ サブスクリプションまたはユーザーの権限のいずれにも一致しないデータを書込み (write) しようとすると、Realm は修復書込み (write) と呼ばれる致命的でないエラー操作で書込みを元に戻します。
アプリの権限の設定の詳細については、App Services ドキュメントの「ロールベースの権限」および「 Device Sync 権限ガイド 」を参照してください。
権限拒否エラー、書込みエラーの修正、およびその他の Device Sync エラーの種類の詳細については、App Services ドキュメントの「同期エラー 」を参照してください。
同期するデータの決定
同期された Realm に書き込むデータは、次の基準によって決まります。
Device Sync の構成
アプリ内の権限
Realm を開くときに使用される Flexible Sync サブスクライブ クエリ
このページの例では、次の Device Sync 構成を持つ Atlas App Services アプリと、次の Realm SDK データモデルとサブスクリプションを持つクライアント アプリを使用します。
この例では、クライアントアプリは次のオブジェクトモデルを使用します。
struct Item { realm::primary_key<realm::object_id> _id{realm::object_id::generate()}; std::string ownerId; std::string itemName; int64_t complexity; }; REALM_SCHEMA(Item, _id, ownerId, itemName, complexity)
App Services の構成
上記の例のオブジェクトモデルに基づいて、Device Sync は次のクエリ可能なフィールドで構成されます。
_id
(常に含まれる)complexity
ownerId
App Services App には、ユーザーが自分のデータのみを読み書きできるように構成された権限があります。
{ "roles": [ { "name": "readOwnWriteOwn", "apply_when": {}, "document_filters": { "write": { "ownerId": "%%user.id" }, "read": { "ownerId": "%%user.id" } }, "read": true, "write": true, "insert": true, "delete": true, "search": true } ] }
ownerId
がログイン ユーザーのuser.identifier()
と一致しない Atlas コレクション内のオブジェクトは、この Realm に同期できません。
クライアント データモデルと構成
このページの例では、この同期クエリとオブジェクトモデルを含む次の同期された Realm 構成を使用します。
auto appConfig = realm::App::configuration(); appConfig.app_id = APP_ID; auto app = realm::App(appConfig); auto user = app.login(realm::App::credentials::anonymous()).get(); auto dbConfig = user.flexible_sync_configuration(); auto syncRealm = realm::db(dbConfig); // Add subscription auto subscriptionUpdateSuccess = syncRealm.subscriptions() .update([](realm::mutable_sync_subscription_set &subs) { // Get Items from Atlas that match this query. // Uses the queryable field `complexity`. // Sync Item objects with complexity less than or equal to 4. subs.add<realm::Item>( "simple items", [](auto &obj) { return obj.complexity <= 4; }); }) .get();
struct Item { realm::primary_key<realm::object_id> _id{realm::object_id::generate()}; std::string ownerId; std::string itemName; int64_t complexity; }; REALM_SCHEMA(Item, _id, ownerId, itemName, complexity)
この同期クエリでは、 complexity
プロパティの値が4
より大きい Atlas コレクション内のオブジェクトは、この Realm に同期できません。
同期された Realm への書き込み
Flexible Sync レルムへの書込みは、書込みが権限とFlexible Sync サブスクライブ クエリに一致するかどうかに応じて、大きく 2 つのカテゴリのいずれかに分類されます。
成功した書込み: 書込まれたオブジェクトは、クエリ サブスクリプションとユーザーの権限の両方に一致します。 オブジェクトは Realm に正常に書込み (write) され、App Services バックエンドや他のデバイスに正常に同期されます。
書込みの考慮事項: 書込まれたオブジェクトがサブスクリプション クエリと一致しないか、ユーザーに書込みを実行するための十分な権限がありません。 Realm は、代替書込み操作で不正な書込みを元に戻します。
Tip
クエリ サブスクリプションに一致しないオブジェクトを書き込みにする場合は、オブジェクトがクエリ サブスクリプションに一致する別の Realm を開くことができます。 あるいは、権限やサブスクライブ クエリを強制しない、同期されていない Realm に オブジェクトを書込むこともできます。
成功した書込み
書込み (write) がクライアント内のユーザー権限とクエリ サブスクリプションの両方に一致する場合、Realm C++ SDK は同期された Realm にオブジェクトを正常に書込むことができます。 このオブジェクトは、デバイスがネットワークに接続している場合に App Services バックエンドと同期します。
// Per the Device Sync permissions, users can only read and write data // where the `Item.ownerId` property matches their own user ID. auto simpleItem = realm::Item{.ownerId = user.identifier(), .itemName = "This item meets sync criteria", .complexity = 3}; // `simpleItem` successfully writes to the realm and syncs to Atlas // because its data matches the subscription query (complexity <= 4) // and its `ownerId` field matches the user ID. syncRealm.write([&] { syncRealm.add(std::move(simpleItem)); });
書込みの考慮
書込み (write) がクエリ サブスクリプションまたはユーザー権限のいずれにも一致しない場合、Realm は書込み (write) を元に戻し、 comments 書込み (write) _error_infoオブジェクトの配列を提供します。
詳しくは、クエリ サブスクリプションの範囲外のデータ、またはユーザーの権限と一致しないデータを書き込むと、次の状況が発生します。
クライアント Realm には「不正」書込みの概念が存在しないため、書込みは Realm が App Services バックエンドで変更セットを解決するまで最初に成功します。
同期時に、サーバーはルールと権限を適用します。 サーバーは、ユーザーには書込み (write) を実行する権限がないと判断します。
サーバーは、「書き込み補正」と呼ばれる元に戻す操作をクライアントに送信します。
クライアントの Realm は不正な書込み (write) 操作を元に戻します。
特定のオブジェクトへの不正な書込み (write) と、それに対応する書込み (write) の間にあるクライアント側の書込み (write) は、いずれもなくなります。 実際には、これは書き込みが成功しているように見えても、Realm が App Services バックエンドと同期し、書き込みを実行すると、オブジェクトは「表示されなくなります」。
この問題が発生した場合は、 App Services ログを参照するか、クライアントでcompensating_writes_info()
関数を使用して、エラーに関する追加情報を取得できます。 詳細については、このページの「書込みエラー情報の置換 」セクションを参照してください。
書込み (write) がクエリ サブスクリプションと一致しない
上記で詳細に説明されている Flexible Sync レルムの構成では、このオブジェクトを書込み (write) しようとすると、オブジェクトがクエリ サブスクリプションと一致しないため、書込みエラーが回避されます。
// The complexity of this item is `7`. This is outside the bounds // of the subscription query, which triggers a compensating write. auto complexItem = realm::Item{._id = primaryKey, .ownerId = user.identifier(), .itemName = "Test compensating writes", .complexity = 7}; // This should trigger a compensating write error when it tries to sync // due to server-side permissions, which gets logged with the error handler. syncRealm.write([&] { syncRealm.add(std::move(complexItem)); });
Connection[2]: Session[10]: Received: ERROR "Client attempted a write that is not allowed; it has been reverted" (error_code=231, is_fatal=false, error_action=Warning)
App Services ログに次のエラー メッセージが表示されます。
Error: Client attempted a write that is not allowed; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "ObjectID(\"6557ddb0bf050934870ca0f5\")": "write to ObjectID(\"6557ddb0bf050934870ca0f5\") in table \"Item\" not allowed; object is outside of the current query view" } }
権限と一致しない書込み
上記で詳細に説明されている Device Sync 構成の権限を考慮すると、このオブジェクトを書込み (write) しようとすると、 ownerId
プロパティがログイン ユーザーのuser.identifier()
と一致しないため、埋め込みエラーが発生します。
// The `ownerId` of this item does not match the user ID of the logged-in // user. The user does not have permissions to make this write, which // triggers a compensating write. auto itemWithWrongOwner = realm::Item{ .ownerId = "not the current user", .itemName = "Trigger an incorrect permissions compensating write", .complexity = 1}; syncRealm.write([&] { syncRealm.add(std::move(itemWithWrongOwner)); });
Connection[2]: Session[11]: Received: ERROR "Client attempted a write that is not allowed; it has been reverted" (error_code=231, is_fatal=false, error_action=Warning)
App Services ログに次のエラー メッセージが表示されます。
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "ObjectID(\"6557ddbabf050934870ca0f8\")": "write to ObjectID(\"6557ddbabf050934870ca0f8\") in table \"Item\" was denied by write filter in role \"readOwnWriteOwn\"" } }
書込み (write) エラー情報の置換
comments_writes_info()関数を使用すると、補完書込み (write) が発生する理由に関する追加情報をクライアントで取得できます。この関数は、次の内容を含むcompensating_write_error_info
構造体の配列を提供します。
クライアントが書込み (write) しようとしたオブジェクトの
object_name
特定のオブジェクトの
primary_key
書き込みエラーを修正するための
reason
この情報は、 App Services ログで確認できる情報と同じです。 C++ SDK は、便宜上とデバッグの目的でこのオブジェクトをクライアント上で公開します。
以下は、書込みエラーの修正に関する情報をログに記録する方法の例です。
auto info = receivedSyncError.compensating_writes_info(); for (auto &v : info) { std::cout << "A write was rejected with a compensating write error.\n"; std::cout << "An object of type " << v.object_name << "\n"; std::cout << "was rejected because " << v.reason << ".\n"; }
A write was rejected with a compensating write error. An object of type Item was rejected because write to ObjectID("6557ddb0bf050934870ca0f5") in table "Item" not allowed; object is outside of the current query view.
このメッセージの
Item
は、このページのオブジェクトモデルで使用されるItem
オブジェクトです。table "Item"
は、このオブジェクトが同期する Atlas コレクションを参照します。このメッセージにある理由
object is outside of the current query view
は、クエリ サブスクリプションがオブジェクトのcomplexity
プロパティが4
以下であることを要求するように設定されていたためです。 クライアントがこの境界外でオブジェクトを書込み (write) しようとしました。プライマリキーは、クライアントが書込みを試みた特定のオブジェクトの
objectId
です。
パフォーマンス向上のためのグループ書込み
サブスクリプションセットのすべての書込みトランザクション (write transaction) にはパフォーマンス コストがかかります。 セッション中に Realm オブジェクトを複数更新する必要がある場合は、すべての変更が完了するまで編集されたオブジェクトをメモリ内に保持することを検討してください。 これにより、すべての変更ではなく、完全で更新されたオブジェクトのみが Realm に書き込まれるため、同期のパフォーマンスが向上します。