Atlas Device Sync with Flexible Sync uses subscriptions and permissions to determine which data to sync between Atlas and your app.
You can add, update, and remove query subscriptions to determine which data syncs to the client device.
Requisitos previos
Nota
Requisitos previos de sincronización flexible
Para habilitar la sincronización flexible en su aplicación, se requiere un clúster Atlas no fragmentado en ejecución MongoDB 5.0 o superior
To use Flexible Sync in a Flutter application:
Align Subscriptions with Backend App
Your client-side subscription queries must align with the Device Sync configuration in your backend App Services App.
You subscription queries can either:
Consulta todos los objetos de un tipo. Crea la consulta con Realm.all().
Objetos de consulta que coinciden con los campos consultables de aplicación de backend. Cree la query utilizando Realm.query() y una query de Realm Query Language que incluya uno o más campos consultables. El SDK de Realm lanza un error si intentas crear una suscripción usando campos que no son consultables.
Para obtener más información sobre cómo configurar campos consultables, consulte Campos consultables en la documentación de App Services.
Para obtener más información sobre las limitaciones del uso del lenguaje de consulta Realm con Flexible Sync, consulte la sección Limitaciones de RQL de Flexible Sync.
Subscribe to Queries
Novedades en la versión v1.6.0.
Flutter v1.6.0 agrega APIs experimentales que se suscriben y se desuscriben de los resultados de una query. Estas API abstraen los detalles de añadir y remover suscripciones manualmente.
For all subscriptions, you need an authenticated user and a synced realm.
Si necesita más control sobre las suscripciones por motivos de optimización del rendimiento o de lógica empresarial, puede administrar manualmente el conjunto de suscripciones mediante subscriptions API. Consulte la sección "Consideraciones de rendimiento" en esta página para obtener más información.
Subscribe to a Query
You can subscribe to the RealmResults of a query using the subscribe() method. When called, the SDK creates the new subscription and adds it to the MutableSubscriptionSet, similar to manually creating subscription.
You can optionally pass a unique subscription name for the query. If you add a subscription with the same name as an existing subscription, the SDK overwrites the existing subscription.
Si no se pasa un nombre de suscripción, el nombre se establece en null y el identificador de la suscripción se basa en el string del query. Esto significa que cada vez que tu string del query cambie, subscribe() crea una nueva suscripción.
To subscribe to a query, pass the following arguments to subscribe():
RealmResults query: Required. ARealmResultsobject that you can create using the Realm Query Language.String name: Opcional. Nombre de la suscripción al que puedes referenciar.bool update: Optional. When true, adding a subscription with an existing name replaces the existing query with the new query. When false, the SDK throws an exception for duplicate subscriptions. Only use with named subscriptions.
In the following example, we subscribe to two new named queries.
final boatQuery = realm.all<Boat>(); final bigPlaneQuery = realm.query<Plane>("numSeats > 100"); final boatSubscription = await boatQuery.subscribe(name: "boats"); final planeSubscription = await bigPlaneQuery.subscribe(name: "big-planes");
Tip
Specify a Subscription Name
We recommend that you always specify a subscription name, especially if your application uses multiple subscriptions. This makes finding and managing your subscriptions easier.
Wait for a Query Subscription to Sync
Cuando te suscribes a los resultados de una query, los resultados no contienen objetos hasta que se hayan descargado los datos sincronizados. Cuando debas esperar a que finalice la descarga de los objetos sincronizados, configura la opción waitForSyncMode.
Este ejemplo utiliza la opción firstTime, que es el comportamiento por defecto. Una suscripción con el comportamiento firstTime solo espera que la sincronización termine cuando se crea por primera vez una suscripción.
final bigPlaneQuery = realm.query<Plane>("numSeats > 100"); final planeSubscription = await bigPlaneQuery.subscribe( name: "firstTimeSync", waitForSyncMode: WaitForSyncMode.firstTime, );
The other supported waitForSyncMode options are:
always: Espera a que se descarguen los objetos coincidentes cada vez que se inicia la aplicación. La aplicación debe tener conexión a internet cada vez que se inicia.never: Never wait to download matching objects. The app needs an internet connection for the user to authenticate the first time the app launches, but can open offline on subsequent launches using cached credentials.
Opcionalmente, puedes especificar un token de cancelación para limitar cuánto tiempo sigue ejecutar la sincronizar descargar:
final bigPlaneQuery = realm.query<Plane>("numSeats > 200"); final planeSubscription = await bigPlaneQuery.subscribe( name: "alwaysWaitSync", waitForSyncMode: WaitForSyncMode.always, cancellationToken: TimeoutCancellationToken(Duration(seconds: 5)), );
Darse de baja de una consulta
Las suscripciones se mantienen entre sesiones de usuario salvo que se dé de baja de ellas. Puedes cancelar la suscripción de los resultados de una query usando unsubscribe().
This removes the subscription from the list of active subscriptions, similar to manually removing a subscription. Note that the results list may still contain objects after calling unsubscribe() if another subscription exists that contains overlapping objects.
When you call unsubscribe() on a query, the SDK removes any subscriptions with queries that exactly match the one you call unsubscribe() on. This method returns before objects matching the removed subscription are deleted from the realm. Sync continues in the background based on the new set of subscriptions.
planeQuery.unsubscribe(); trainQuery.unsubscribe();
Manually Manage Subscriptions
When configuring Flexible Sync on the backend, you specify which fields your client application can query. In the client application, use the Realm.subscriptions property to manage a set of subscriptions to specific queries on queryable fields.
You can do the following with your subscriptions:
Obtenga una lista de todas las suscripciones
Add subscriptions
Comprobar el estado de la suscripción
Actualizar una suscripción con una nueva query
Remove subscriptions
Cuando los datos coinciden con la suscripción y el usuario autenticado tiene los permisos adecuados, Device Sync sincroniza los datos del backend con la app del cliente.
Subscription sets persist across sessions even if you no longer include the subscription in your code. Subscription information is stored in the synced realm's database file. You must explicitly remove a subscription for it to stop attempting to sync matching data.
You can specify a string name for your subscription. If you do not give your subscription a name, the name is set to null.
When you create a subscription, Realm looks for data matching a query on a specific object type. In your Flexible Sync subscriptions, you can have subscriptions on several different object types or several queries on the same object type.
Obtener suscripciones
When using Flexible Sync, you can access a SubscriptionSet, a collection of subscriptions, through the Realm.subscriptions property.
You can use this subscriptions set to add queries to this list of subscriptions and update existing subscriptions, as shown in the examples below.
final subscriptions = realm.subscriptions;
Agregar una query al conjunto de suscripciones
You must perform all mutations on the subscriptions set within an update block. To create an update block, call SubscriptionSet.update().
The update block callback function includes a MutableSubscriptionSet() object as an argument. You can modify your method on the SubscriptionSet to add a query to the subscription.
Importante
Flexible Sync no es compatible con todos los operadores disponibles en Realm Query Language. Consulta Limitaciones de RQL en Flexible Sync para obtener detalles.
El método MutableSubscriptionSet.add() toma tres argumentos:
RealmResults query: Obligatorio. UnRealmResultsobjeto que puede crearse mediante la consulta del lenguaje de consulta Realm.String name: Opcional. Nombre de la suscripción al que puedes referenciar.bool update: Optional. When true, adding a subscription with an existing name replaces the existing query with the new query. Only use with named subscriptions.
Nota
Duplicate Subscriptions
If you add a duplicate unnamed subscription with the same query, Realm automatically removes it; adding an identical named subscription is a no-op. Therefore, in both cases, duplicate subscriptions are ignored.
You can add a single query, or batch multiple queries within a SubscriptionSet.update block. Performing query updates is an expensive operation on the server. We strongly advise designing your application to minimize subscription updates. You can do this by creating all subscriptions in a single update block the first time the user launches the app and batching any follow-up changes to the subscription set.
In the example below, we subscribe to two queries.
final planeQuery = realm.all<Plane>(); final longTrainQuery = realm.query<Train>("numCars >= 5"); realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.add(planeQuery, name: "planes"); mutableSubscriptions.add(longTrainQuery, name: 'long-trains', update: true); }); await realm.subscriptions.waitForSynchronization();
Update Subscriptions with a New Query
You can update a named subscription with a new query. To update a subscription's query, open an update block with SubscriptionSet.update(). In the callback function of the update block, pass the following arguments to MutableSubscriptionSet.add():
The new query
The name of the subscription that you want to update
update: true
No se puede actualizar una suscripción sin nombre. También se puede eliminar la suscripción sin nombre y crear una nueva con la consulta deseada.
En el siguiente ejemplo, los trenes largos se redefinen como cualquier tren que tenga más de 10 vagones.
final longerTrainQuery = realm.query<Train>("numCars > 10"); realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.add(longerTrainQuery, name: 'long-trains', update: true); });
Eliminar suscripciones
To remove subscriptions from the subscription set, you can:
Eliminar una sola suscripción con la consulta indicada
Remove a single subscription with the given name
Remueve una única suscripción con la referencia de la suscripción
Remove all subscriptions for a Realm object type
Remove all subscriptions
When you remove a subscription query, the server also removes synced data from the client device.
Remove a Subscription by Query
Within an update block, you can remove a specific subscription by query. Open an update block with SubscriptionSet.update(). Pass the Subscription to MutableSubscriptionSet.removeByQuery().
En el siguiente ejemplo, se elimina la suscripción para todos los objetos de Plane.
realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.removeByQuery(realm.all<Plane>()); });
Remove a Subscription by Name
Within an update block, you can remove a specific subscription by name. Pass the name to MutableSubscriptionSet.removeByName()
realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.removeByName('long-trains'); });
Eliminar una suscripción por referencia
You can remove a subscription if you have a reference to its Subscription object. Within a subscription update block, pass the Subscription reference to MutableSubscriptionSet.remove().
final sub = realm.subscriptions[0]; realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.remove(sub); });
Remove All Subscriptions for an Object Type
You can remove all subscriptions for a given Realm object type. Within a subscription update block, call MutableSubscriptionSet.removeByType().
realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.removeByType<Train>(); });
Remove All Subscriptions
En un bloque de actualización de suscripción, puedes remover todas las suscripciones sin nombre del conjunto de suscripciones con MutableSubscriptionSet.clear().
realm.subscriptions.update((MutableSubscriptionSet mutableSubscriptions) { mutableSubscriptions.clear(); });
Espere a que los cambios de suscripción se sincronicen
Mutating the subscription set within an update block is only one part of changing a subscription. After the local subscription change, the realm synchronizes with the server to resolve any updates to the data due to the subscription change. This includes adding or removing data from the synced realm.
Use Realm.subscriptions.waitForSynchronization() to wait for the server to acknowledge this set of subscriptions. If the server rejects the change, and an exception is thrown.
Puede ocurrir una excepción si:
Te suscribes a una consulta no compatible. Suscribirte a una consulta no compatible pausará la sincronización. Para reanudarla, elimina la consulta no compatible.
Está realizando una acción no válida, como agregar un objeto que no coincide con una suscripción. Esto activa un reinicio del cliente: se borran los datos del dominio y se crea una nueva copia sin ninguna suscripción en el conjunto.
await realm.subscriptions.waitForSynchronization();
Estado de la suscripción
Use the Realm.subscriptions.state property to read the current state of the subscription set.
The superseded state is a SubscriptionSetState that can occur when another thread updates a subscription on a different instance of the subscription set. If the state becomes superseded, you must obtain a new instance of the subscription set before you can update it.
Nota
Estado de suscripción 'Completo'
The subscription set state "complete" does not mean "sync is done" or "all documents have been synced". "Complete" means the following two things have happened:
The subscription has become the active subscription set that is currently being synchronized with the server.
The documents that matched the subscription at the time the subscription was sent to the server are now on the local device. Note that this does not necessarily include all documents that currently match the subscription.
El SDK de Realm no proporciona una manera de verificar si todos los documentos que coinciden con una suscripción se han sincronizado con el dispositivo.
Requisitos y limitaciones de RQL de Flexible Sync
Requisitos de suscripción a campos consultables indexados
Adding an indexed queryable field to your App can improve performance for simple queries on data that is strongly partitioned. For example, an app where queries strongly map data to a device, store, or user, such as user_id == $0, “641374b03725038381d2e1fb”, is a good candidate for an indexed queryable field. However, an indexed queryable field has specific requirements for use in a query subscription:
The indexed queryable field must be used in every subscription query. It cannot be missing from the query.
The indexed queryable field must use an
==orINcomparison against a constant at least once in the subscription query. (El campo consultable debe utilizar una comparación o contra una constante al menos una vez en la consulta de suscripción.) For example,user_id == $0, "641374b03725038381d2e1fb"orstore_id IN $0, {1,2,3}. (Por ejemplo, o .)
Opcionalmente, puedes incluir una comparación de AND siempre que el campo indexado consultable se compare directamente con una constante usando == o IN al menos una vez. Por ejemplo, store_id IN {1,2,3} AND region=="Northeast" o store_id == 1 AND (active_promotions < 5 OR num_employees < 10).
Invalid Flexible Sync queries on an indexed queryable field include queries where:
The indexed queryable field does not use
ANDwith the rest of the query. For examplestore_id IN {1,2,3} OR region=="Northeast"is invalid because it usesORinstead ofAND. Similarly,store_id == 1 AND active_promotions < 5 OR num_employees < 10is invalid because theANDonly applies to the term next to it, not the entire query.El campo consultable indexado no se utiliza en un operador de igualdad. Por ejemplo,
store_id > 2 AND region=="Northeast"no es válido porque solo utiliza el operador>con el campo consultable indexado y no tiene una comparación de igualdad.The query is missing the indexed queryable field entirely. For example,
region=="Northeastortruepredicateare invalid because they do not contain the indexed queryable field.
Unsupported Query Operators in Flexible Sync
Flexible Sync has some limitations when using RQL operators. When you write the query subscription that determines which data to sync, the server does not support these query operators. However, you can still use the full range of RQL features to query the synced data set in the client application.
Tipo de operador | Operadores no compatibles |
|---|---|
Aggregate Operators |
|
Query Suffixes |
|
Las queries que no distinguen entre mayúsculas y minúsculas ([c]) no pueden usar índices de manera eficaz. Como resultado, no se recomiendan las queries que no distinguen entre mayúsculas y minúsculas, ya que podrían provocar problemas de rendimiento.
Flexible Sync only supports @count for array fields.
List Queries
Flexible Sync admite la consulta de listas usando el operador IN.
Puede consultar una lista de constantes para ver si contiene el valor de un campo consultable:
// Query a constant list for a queryable field value "priority IN { 1, 2, 3 }"
If a queryable field has an array value, you can query to see if it contains a constant value:
// Query an array-valued queryable field for a constant value "'comedy' IN genres"
Advertencia
You cannot compare two lists with each other in a Flexible Sync query. Note that this is valid Realm Query Language syntax outside of Flexible Sync queries.
// Invalid Flexible Sync query. Do not do this! "{'comedy', 'horror', 'suspense'} IN genres" // Another invalid Flexible Sync query. Do not do this! "ANY {'comedy', 'horror', 'suspense'} != ANY genres"
Objetos incrustados o vinculados
Flexible Sync does not support querying on properties in Embedded Objects or links. For example, obj1.field == "foo".
Query Size Limit
The size limit for any given query subscription in your subscription set is 256 kB. Exceeding this limit results in a LimitsExceeded Error.
Consideraciones sobre el rendimiento
API Efficiency
Administrar varias suscripciones con la .subscribe() API (descrita en la sección "Suscribirse a consultas") es menos eficiente que realizar actualizaciones por lotes cuando se administran manualmente mediante la API de conjuntos de suscripciones. Para un mejor rendimiento al realizar cambios en varias suscripciones, utilice la subscriptions.update API (descrita en la sección "Administrar suscripciones manualmente").
Actualizaciones de grupos para mejorar el rendimiento
Every write transaction for a subscription set has a performance cost. If you need to make multiple updates to a Realm object during a session, consider keeping edited objects in memory until all changes are complete. This improves sync performance by only writing the complete and updated object to your realm instead of every change.