Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /
Sync Data

Manage Sync Subscriptions - React Native SDK

Flexible Sync usa suscripciones y permisos para determinar qué datos sincronizar con tu aplicación. Debes tener al menos una suscripción antes de poder leer o escribir en un dominio con Flexible Sync habilitado. @realm/react La biblioteca agiliza los permisos y las consultas para las suscripciones de sincronización.

Puedes agregar, actualizar y eliminar suscripciones de consultas para controlar qué datos se sincronizan con el dispositivo cliente. En Realm.js v12.0.0 y versiones posteriores, puedes suscribirte a consultas en lugar de administrar las suscripciones manualmente, o además de hacerlo.

No puedes crear suscripciones para Ingesta de datos y objetos asimétricos porque solo envían datos al backend de App Services.

Importante

Limitaciones de las consultas de Flexible Sync

Las suscripciones de sincronización flexible solo admiten un subconjunto de operadores de consulta RQL. Consulte la documentación sobre las limitaciones de RQL de sincronización flexible para obtener información sobre los operadores no compatibles.

You need to meet the following requirements before you can use Atlas Device Sync with the React Native SDK:

Before you can add Flexible Sync subscriptions to a React Native client, you must:

  1. Configure Flexible Sync on the backend

  2. Initialize the app client

  3. Authenticate a user in the client

  4. Configure a synced Realm

Your client-side subscription queries must align with the Device Sync configuration in your backend App Services App.

Subscription queries return all objects of a type. You can filter results with a Realm Query Language query that includes one or more queryable fields.

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.

New in version realm@12.0.0.

realm@12.0.0 añade APIs experimentales que se suscriben y desuscriben de los resultados de una query. Estas APIs abstraen los detalles de agregar y remover suscripciones manualmente.

For all subscriptions, you need an authenticated user and a Flexible Sync realm.

Changed in version realm@12.3.0: Geospatial data supported in Atlas Device Sync

En Realm.js v12.3.0 y versiones posteriores, puede crear suscripciones a consultas geoespaciales. Si intenta suscribirse a una consulta geoespacial con una versión anterior del SDK, recibirá un error de servidor con una escritura compensatoria.

Para obtener más información, consulte Geospatial - React Native SDK.

We recommend that you name your subscriptions. This makes finding and managing your subscriptions easier. Subscription names must be unique. Trying to add a subscription with the same name as an existing subscription overwrites the existing subscription.

To subscribe to a query:

  1. Query for the objects that you want to read and write.

  2. Llama a subscribe() en los resultados de la query para crear una suscripción de sincronización para objetos que coincidan con la query.

  3. Pasa un objeto SubscriptionOptions que contiene la propiedad name a subscribe().

import React, {useEffect, useState} from 'react';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, FlatList} from 'react-native';
import {Bird} from '../../models';
import {Subscription} from 'realm/dist/bundle';
export const BasicSubscription = () => {
const realm = useRealm();
// Get all local birds that have not been seen yet.
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
// Create an async function so that we can `await` the
// promise from `.subscribe()`.
const createSubscription = async () => {
await seenBirds.subscribe({
name: 'Birds I have seen',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Birds I have seen');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

Most of the time, you should give your subscriptions a name. If you don't, the name is set to null.

Si usa filtered() en una suscripción de consulta sin nombre, el identificador de la suscripción se basa en la consulta filtered. Esto significa que cada vez que cambie la cadena de consulta, subscribe() creará una nueva suscripción.

Referencia de API

Al suscribirse a los resultados de una consulta, estos no contienen objetos hasta que se descargan los datos sincronizados. Si necesita esperar a que se descarguen los objetos sincronizados, configure la opción waitForSync.

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.

import React, {useEffect, useState} from 'react';
import {BSON, WaitForSync} from 'realm';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button, TextInput, FlatList} from 'react-native';
import {Bird} from '../../models';
import {Subscription} from 'realm/dist/bundle';
export const WaitFirstTime = () => {
const realm = useRealm();
const [birdName, setBirdName] = useState('Change me!');
// Get local birds that have been marked as "haveSeen".
const seenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == true'),
);
const [seenBirdsSubscription, setSeenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Only wait for sync to finish on the initial sync.
await seenBirds.subscribe({
behavior: WaitForSync.FirstTime,
name: 'First time sync only',
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('First time sync only');
// ... and set it to a stateful variable or manage it in `useEffect`.
setSeenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

The other supported WaitForSync 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 valor timeout para limitar cuánto tiempo dura la descarga de la sincronización:

export const AlwaysWait = () => {
const realm = useRealm();
// Get all local birds that have not been seen yet.
const unSeenBirds = useQuery(Bird, collection =>
collection.filtered('haveSeen == false'),
);
const [unSeenBirdsSubscription, setUnseenBirdsSubscription] =
useState<Subscription | null>();
useEffect(() => {
const createSubscription = async () => {
// Add subscription with timeout.
// If timeout expires before sync is completed, currently-downloaded
// objects are returned and sync download continues in the background.
await unSeenBirds.subscribe({
behavior: WaitForSync.Always,
name: 'Always wait',
timeout: 500,
});
};
createSubscription().catch(console.error);
// Get the subscription...
const subscription = realm.subscriptions.findByName('Always wait');
// ... and set it to a stateful variable or manage it in `useEffect`.
setUnseenBirdsSubscription(subscription);
}, []);
return (
// Work with the subscribed results list or modify the subscription...
<></>
);
};

Referencia de API

Subscriptions persist across user sessions unless you unsubscribe from them. You can unsubscribe from a query's results using unsubscribe().

Esto elimina la suscripción de la lista de suscripciones activas, de forma similar a eliminar una suscripción manualmente.

Una lista de resultados aún puede contener objetos después de llamar a unsubscribe() si existe otra suscripción que contiene objetos superpuestos.

Cuando llamas a unsubscribe(), se elimina la suscripción asociada. Las suscripciones se eliminan por nombre. Si no tienen un nombre, unsubscribe() remueve cualquier query que coincida exactamente con la que llamas en unsubscribe().

The unsubscribe() 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.

import React, {useEffect, useState} from 'react';
import {useRealm, useQuery} from '@realm/react';
import {View, Text, Button} from 'react-native';
import {Bird} from '../../models';
import {Subscription} from 'realm/dist/bundle';
export const Unsubscribe = () => {
const realm = useRealm();
const birds = useQuery(Bird);
const unsubscribeFromQuery = () => {
birds.unsubscribe();
};
return (
<View>
<Button
title="Unsubscribe"
onPress={() => {
unsubscribeFromQuery();
}}
/>
</View>
);
};

Referencia de API

Puede utilizar la API de suscripciones para administrar manualmente un conjunto de suscripciones a consultas específicas en campos consultables.

Puede utilizar la API Realm.subscriptions para administrar un conjunto de suscripciones a consultas específicas en campos consultables.

Si @realm/react usas, puedes administrar las suscripciones de dominio dentro de un RealmProvider correctamente configurado. El gancho useRealm() te da acceso al dominio abierto.

You can do the following with your 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.

When you create a subscription, Realm looks for data matching a query on a specific object type. You can have subscriptions on several different object types. You can also have multiple queries on the same object type.

Importante

Object Links

You must add both an object and its linked object to the subscription set to see a linked object.

Si los resultados de tu suscripción contienen un objeto con una propiedad que enlaza a un objeto no contenido en los resultados, el enlace parecerá ser nulo. No hay forma de distinguir si el valor de esa propiedad es legítimamente nulo, o si el objeto al que se enlaza existe pero está fuera del alcance de la suscripción de la consulta.

Dentro de un RealmProvider configurado para Flexible Sync, puedes acceder a un SubscriptionSet. Un SubscriptionSet es una colección de todas las suscripciones de tu aplicación.

import React, {useEffect, useState} from 'react';
import {Text, FlatList} from 'react-native';
import {useRealm, useQuery} from '@realm/react';
import {Bird} from '../Models/Bird';
function SubscriptionManager() {
const realm = useRealm();
// Pass object model to useQuery and filter results.
// This does not create a subscription.
const seenBirds = useQuery(Bird, birds => {
return birds.filtered('haveSeen == true');
});
const [subscriptions, setSubcriptions] = useState<
App.Sync.SubscriptionSet | undefined
>();
useEffect(() => {
const createSubscription = async () => {
// Create subscription for filtered results.
await realm.subscriptions.update(mutableSubs => {
mutableSubs.add(seenBirds, {name: 'seen birds'});
});
};
createSubscription().catch(console.error);
// Set to state variable.
setSubcriptions(realm.subscriptions);
}, []);
return (
<FlatList
data={subscriptions}
keyExtractor={subscription => subscription.id.toString()}
renderItem={({item}) => <Text>{item.name}</Text>}
/>
);
}

Referencia de API

In the following example, completed and progressMinutes have been set as queryable fields in an App Services App. In the client code, we create filtered queries and then subscribe to their results:

  • Completed tasks

  • Tareas completadas que han superado los 120 progressMinutes

Note that useQuery() needs an active subscription to return results. If no subscription has been added yet, useQuery() returns an empty result, which is not a valid query for MutableSubscriptionSet.add().

import React, {useEffect} from 'react';
import {Text, FlatList} from 'react-native';
import {useRealm, useQuery} from '@realm/react';
function SubscriptionManager() {
const realm = useRealm();
const seenBirds = useQuery(Bird, birds => {
return birds.filtered('haveSeen == true');
});
useEffect(() => {
realm.subscriptions.update(
(mutableSubs: Realm.App.Sync.MutableSubscriptionSet) => {
// Create subscription for filtered collection.
mutableSubs.add(seenBirds, {name: 'seenBirds'});
},
);
});
return (
<FlatList
data={seenBirds}
keyExtractor={item => item._id.toString()}
renderItem={({item}) => <Text>{item._id.toString()}</Text>}
/>
);
}

You must have at least one subscription before you can read from or write to a Flexible Sync realm. Initial subscriptions let you define subscriptions when you configure a synced realm.

To open a synced realm with initial subscriptions, add an initialSubscriptions property to a RealmProvider's sync configuration.

You can't use the @realm/react library hooks useQuery and useObject when setting initial subscriptions. Instead, use the Realm.js read and write operations.

import React from 'react';
import {AppProvider, UserProvider} from '@realm/react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
import {Text, FlatList} from 'react-native';
const {RealmProvider, useQuery} = RealmContext;
function AppWrapper() {
return (
<AppProvider id={APP_ID}>
<UserProvider fallback={LogIn}>
<RealmProvider
sync={{
flexible: true,
initialSubscriptions: {
update(subs, realm) {
subs.add(realm.objects('Turtle'));
},
},
onError: console.log,
}}>
<SubscriptionManager />
</RealmProvider>
</UserProvider>
</AppProvider>
);
}
function SubscriptionManager() {
// Pass object model to useQuery to get all objects of type `Turtle`.
// These results automatically update with changes from other devices
// because we created a subscription with `initialSubscriptions`.
const allTurtles = useQuery('Turtle');
return (
<FlatList
data={allTurtles}
keyExtractor={turtle => turtle._id.toString()}
renderItem={({item}) => <Text>{item._id}</Text>}
/>
);
}

By default, initial subscriptions are only created the first time a realm is opened. If your app needs to rerun this initial subscription every time the app starts, you can set rerunOnOpen to true. You might need to do this to rerun dynamic time ranges or other queries that require a recomputation of static variables for the subscription.

Referencia de API

Puede comprobar el estado de la suscripción para ver si el servidor ha reconocido la suscripción y el dispositivo ha descargado los datos localmente.

You can use subscription state to:

  • Trigger error handling

  • Mostrar si la transacción está pendiente o se ha completado

  • Descubre cuándo se reemplaza un conjunto de suscripciones y deberías obtener una nueva instancia del conjunto de suscripciones para guardar un cambio de suscripción.

import React, {useEffect} from 'react';
import {Text, View} from 'react-native';
import {useRealm, useQuery} from '@realm/react';
import {Bird} from '../Models/Bird';
function SubscriptionManager() {
const realm = useRealm();
const seenBirds = useQuery(Bird, birds => {
return birds.filtered('haveSeen == true');
});
useEffect(() => {
realm.subscriptions.update(
(mutableSubs: Realm.App.Sync.MutableSubscriptionSet) => {
// Create subscription for filtered collection.
mutableSubs.add(seenBirds, {name: 'seenBirds'});
},
);
});
// Returns state of all subscriptions, not individual subscriptions.
// In this case, it's just the subscription for `Bird` objects where
// `haveSeen` is true.
const allSubscriptionState = realm.subscriptions.state;
return (
<View>
<Text >
Status of all subscriptions: {allSubscriptionState}
</Text>
</View>
);
}

New in version realm@12.0.0.

Realm.js v12.0.0 added the SubscriptionSetState enum that you can use to get the status of a subscription.

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.

You can update a named subscription with a new query. To update a subscription's query, pass the new query and a subscription option with the name of the subscription that you want to update to the MutableSubscriptionSet.add() method. Like adding a new subscription, you must update a subscription within a transaction by calling subscriptions.update().

The following example, redefines long-running tasks as any tasks that take more than 180 minutes.

realm.subscriptions.update((mutableSubs) => {
mutableSubs.add(
tasks.filtered('status == "completed" && progressMinutes > 180'),
{
name: "longRunningTasksSubscription",
}
);
});

Nota

Attempting to update a subscription that has the SubscriptionOptions.throwOnUpdate field set to true throws an exception.

Referencia de API

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.

Puedes remover suscripciones de las siguientes formas:

  • Remueve una sola suscripción con una query específica

  • Remover una sola suscripción con un nombre específico

  • Remove all subscriptions to a specific object model

  • Remover todas las suscripciones sin nombre

  • Remove all subscriptions

When you remove a subscription query, the server also removes synced data from the client device.

Los ejemplos de esta sección suponen que está trabajando con @realm/react y un RealmProvider configurado correctamente.

import {useEffect} from 'react';
// get realm context from createRealmContext()
import {RealmContext} from '../RealmConfig';
const {useRealm} = RealmContext;
function SubscriptionManager() {
const realm = useRealm();
useEffect(() => {
realm.subscriptions.update(mutableSubs => {
// Remove subscription for object type `Turtle`,
// which we added in `initialSubscriptions`.
mutableSubs.removeByObjectType('Turtle');
});
});
return (
// ...
);
}

You can remove a specific subscription by query by executing a transaction on the subscriptions set. Pass the query to MutableSubscriptionSet.remove() within a write transaction.

realm.subscriptions.update((mutableSubs) => {
// remove a subscription with a specific query
mutableSubs.remove(tasks.filtered('owner == "Ben"'));
});

To remove a specific subscription by name, execute a transaction on the subscriptions set. Within the transaction, pass the name to MutableSubscriptionSet.removeByName().

realm.subscriptions.update((mutableSubs) => {
// remove a subscription with a specific name
mutableSubs.removeByName("longRunningTasksSubscription");
});

If you have a reference to a subscription, you can remove that subscription. To do so, execute a transaction on the subscriptions set. Within the transaction, pass the reference variable to MutableSubscriptionSet.removeSubscription().

let subscriptionReference;
realm.subscriptions.update((mutableSubs) => {
subscriptionReference = mutableSubs.add(realm.objects("Task"));
});
// later..
realm.subscriptions.removeSubscription(subscriptionReference);

Para eliminar todas las suscripciones de un tipo de objeto específico, ejecute una transacción en el conjunto de suscripciones. Dentro de la transacción, pase el tipo de objeto como una cadena a MutableSubscriptionSet.removeByObjectType().

realm.subscriptions.update((mutableSubs) => {
mutableSubs.removeByObjectType("Team");
});

New in version realm@v12.0.0.

Es posible que desee eliminar las suscripciones sin nombre que son transitorias o generadas dinámicamente, pero dejar las suscripciones con nombre en su lugar.

Puede eliminar todas las suscripciones sin nombre del conjunto de suscripciones llamando a .removeUnnamed() en mutableSubs. .removeUnnamed() devuelve la cantidad de suscripciones sin nombre eliminadas.

// Remove unnamed subscriptions.
let numberRemovedSubscriptions = 0;
await realm.subscriptions.update((mutableSubs) => {
numberRemovedSubscriptions = mutableSubs.removeUnnamed();
});

Referencia de API

To remove all subscriptions from the subscriptions set, execute a transaction on the subscriptions set. Call MutableSubscriptionSet.removeAll() within a write transaction.

realm.subscriptions.update((mutableSubs) => {
mutableSubs.removeAll();
});

Administrar múltiples suscripciones con las subscribe() unsubscribe() API y descritas en la sección Suscribirse a consultas es menos eficiente que realizar actualizaciones por lotes cuando administra las suscripciones manualmente.

Para lograr un mejor rendimiento al realizar cambios en varias suscripciones, utiliza la subscriptions API para actualizar todas las suscripciones en una sola transacción. Para aprender cómo, consulta Administrar suscripciones manualmente.

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.

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 == or IN comparison 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" or store_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 AND with the rest of the query. For example store_id IN {1,2,3} OR region=="Northeast" is invalid because it uses OR instead of AND. Similarly, store_id == 1 AND active_promotions < 5 OR num_employees < 10 is invalid because the AND only 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=="Northeast or truepredicate are invalid because they do not contain the indexed queryable field.

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

@avg, @count, @max, @min, @sum

Query Suffixes

DISTINCT, SORT, LIMIT

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.

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"

Flexible Sync does not support querying on properties in Embedded Objects or links. For example, obj1.field == "foo".

The size limit for any given query subscription in your subscription set is 256 kB. Exceeding this limit results in a LimitsExceeded Error.

Volver

Configurar un Realm sincronizado