同期された Realm へのデータの書き込み - Kotlin SDK
項目一覧
Flexible Syncを使用して同期された Realm にデータを書き込む場合、 ローカル Realm への書き込みと同じ 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 データモデルとサブスクリプションを持つクライアント アプリを使用します。
この例では、クライアントアプリは次のオブジェクトモデルを使用します。
class Item : RealmObject { var _id: ObjectId = ObjectId() var ownerId: String = "" var itemName: String = "" var complexity: Int = 0 }
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.id
と一致しない Atlas コレクション内のオブジェクトは、この Realm に同期できません。
クライアント データモデルと構成
オブジェクト モデルを使用して、例では 同期された Realm を構成し、このサブスクリプション クエリに一致するオブジェクトを同期します。
val app = App.create(FLEXIBLE_APP_ID) val user = app.login(credentials) val flexSyncConfig = SyncConfiguration.Builder(user, setOf(Item::class)) // Add subscription .initialSubscriptions { realm -> add( // Get Items from Atlas that match the Realm Query Language query. // Uses the queryable field `complexity`. // Query matches objects with complexity less than or equal to 4. realm.query<Item>("complexity <= 4"), "simple-items" ) } .build() val syncRealm = Realm.open(flexSyncConfig) syncRealm.subscriptions.waitForSynchronization() Log.v("Successfully opened realm: ${syncRealm.configuration}")
complexity
プロパティの値が4
より大きい Atlas コレクション内のオブジェクトは、この Realm に同期できません。
同期された Realm への書き込み
Flexible Sync レルムへの書込みは、書込みが権限とFlexible Sync サブスクライブ クエリに一致するかどうかに応じて、大きく 2 つのカテゴリのいずれかに分類されます。
成功した書込み: 書込まれたオブジェクトは、クエリ サブスクリプションとユーザーの権限の両方に一致します。 オブジェクトは Realm に正常に書込み (write) され、App Services バックエンドや他のデバイスに正常に同期されます。
書込みの考慮事項: 書込まれたオブジェクトがサブスクリプション クエリと一致しないか、ユーザーに書込みを実行するための十分な権限がありません。 Realm は、代替書込み操作で不正な書込みを元に戻します。
Tip
クエリ サブスクリプションに一致しないオブジェクトを書き込みにする場合は、オブジェクトがクエリ サブスクリプションに一致する別の Realm を開くことができます。 あるいは、権限やサブスクライブ クエリを強制しない、同期されていない Realm に オブジェクトを書込むこともできます。
成功した書込み
書き込みがクライアント内のユーザー権限とクエリ サブスクリプションの両方に一致する場合、Realm Kotlin 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. val userId = user.id val newItem = Item().apply { ownerId = userId itemName = "This item meets sync criteria" complexity = 3 } syncRealm.write { // `newItem` is successfully written to the realm and synced to Atlas // because its data matches the subscription query (complexity <= 4) // and its `ownerId` field matches the user ID. copyToRealm(newItem) }
書込みの考慮
書込み (write) がクエリ サブスクリプションまたはユーザー権限のいずれにも一致しない場合、Realm は書込み (write) を元に戻し、 ComposingWriteException をスローします。
詳しくは、クエリ サブスクリプションの範囲外のデータ、またはユーザーの権限と一致しないデータを書き込むと、次の状況が発生します。
クライアント Realm には「不正」書込みの概念が存在しないため、書込みは Realm が App Services バックエンドで変更セットを解決するまで最初に成功します。
同期時に、サーバーはルールと権限を適用します。 サーバーは、ユーザーには書込み (write) を実行する権限がないと判断します。
サーバーは、「書き込み補正」と呼ばれる元に戻す操作をクライアントに送信します。
クライアントの Realm は不正な書込み (write) 操作を元に戻します。
特定のオブジェクトへの不正な書込み (write) と、それに対応する書込み (write) の間にあるクライアント側の書込み (write) は、いずれもなくなります。 実際には、これは書き込みが成功しているように見えても、Realm が App Services バックエンドと同期し、書き込みを実行すると、オブジェクトは「表示されなくなります」。
この問題が発生した場合は、 Atlas App Services の ログを参照するか、クライアントのCompensationsWriteInfoオブジェクトを使用して、エラーに関する追加情報を取得できます。
書込み (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. val itemTooComplex = Item().apply { ownerId = user.id itemName = "This item is too complex" complexity = 7 } syncRealm.write { copyToRealm(itemTooComplex) }
[Session][CompensatingWrite(231)] Client attempted a write that is disallowed by permissions, or modifies an object outside the current query, and the server undid the change.
App Services ログに次のエラー メッセージが表示されます。
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\" in table \"Item\" not allowed; object is outside of the current query view" } }
権限と一致しない書込み
上記で詳細に説明されている Device Sync 構成の権限を考慮すると、このオブジェクトを書込み (write) しようとすると、 ownerId
プロパティがログイン ユーザーのuser.id
と一致しないため、埋め込みエラーが発生します。
// 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. val itemWithWrongOwner = Item().apply { ownerId = "not the current user" itemName = "A simple item" complexity = 1 } syncRealm.write { copyToRealm(itemWithWrongOwner) }
[Session][CompensatingWrite(231)] Client attempted a write that is disallowed by permissions, or modifies an object outside the current query, and the server undid the change.
App Services ログに次のエラー メッセージが表示されます。
Error: Client attempted a write that is outside of permissions or query filters; it has been reverted (ProtocolErrorCode=231) Details: { "Item": { "63bdfc40f16be7b1e8c7e4b7": "write to \"63bdfc40f16be7b1e8c7e4b7\" in table \"Item\" was denied by write filter in role \"readOwnWriteOwn\"" } }
書込み (write) エラー情報の置換
バージョン 1.9.0 の新機能。
ComposingWriteInfoオブジェクトを使用して、補完書込み (write) が発生する理由に関する追加情報をクライアントで取得できます。これは次の提供を提供します。
クライアントが書込み (write) しようとしたオブジェクトの
objectType
特定のオブジェクトの
primaryKey
書き込みエラーを修正するための
reason
この情報は、 App Services ログで確認できる情報と同じです。 Kotlin SDK は、便宜上とデバッグの目的でこのオブジェクトをクライアント上で公開します。
以下は、書込みエラーの修正に関する情報をログに記録する方法の例です。
val syncErrorHandler = SyncSession.ErrorHandler { session, error -> runBlocking { if (error is CompensatingWriteException) { error.writes.forEach { writeInfo -> val errorMessage = """ A write was rejected with a compensating write error The write to object type: ${writeInfo.objectType} With primary key of: ${writeInfo.primaryKey} Was rejected because: ${writeInfo.reason} """.trimIndent() Log.e(errorMessage) } } } }
A write was rejected with a compensating write error The write to object type: Item With primary key of: RealmAny{type=OBJECT_ID, value=BsonObjectId(649f2c38835cc0346b861b74)} Was rejected because: write to "649f2c38835cc0346b861b74" in table "Item" not allowed; object is outside of the current query view
このメッセージの
Item
は、このページのオブジェクトモデルで使用されるItem
オブジェクトです。プライマリキーは、クライアントが書込みを試みた特定のオブジェクトの
objectId
です。table "Item"
は、このオブジェクトが同期する Atlas コレクションを参照します。この例の理由
object is outside of the current query view
は、クエリ サブスクリプションがオブジェクトのcomplexity
プロパティが4
以下であることを要求するように設定され、クライアントがこの境界外でオブジェクトを書込み (write) しようとしたためです。
パフォーマンス向上のためのグループ書込み
サブスクリプションセットのすべての書込みトランザクション (write transaction) にはパフォーマンス コストがかかります。 セッション中に Realm オブジェクトを複数更新する必要がある場合は、すべての変更が完了するまで編集されたオブジェクトをメモリ内に保持することを検討してください。 これにより、すべての変更ではなく、完全で更新されたオブジェクトのみが Realm に書き込まれるため、同期のパフォーマンスが向上します。