Puede consultar datos almacenados en MongoDB Atlas directamente desde el código de su aplicación cliente utilizando el SDK de Realm React Native. Cliente MongoDB con la API de consulta. Atlas App Services proporciona reglas de acceso a datos en colecciones para recuperar resultados de forma segura según el usuario conectado o el contenido de cada documento.
Nota
Conjunto de datos de ejemplo
Los ejemplos de esta página utilizan una colección de MongoDB que describe el inventario de una cadena de almacenes de plantas. Para obtener más información sobre el esquema de la colección y el contenido del documento, consulte Datos de ejemplo.
Casos de uso
Existen variedad de razones por las que podría querer query una fuente de datos de MongoDB. Trabajar con datos en tu cliente a través de Atlas Device Sync no siempre es práctico o posible. Podrías querer query MongoDB cuando:
The data set is large or the client device has constraints against loading the entire data set
Estás recuperando documentos que no están modelados en Realm
Your app needs to access collections that don't have strict schemas
A non-Realm service generates collections that you want to access
While not exhaustive, these are some common use cases for querying MongoDB directly.
Requisitos previos
Antes de poder consultar MongoDB desde su aplicación React Native, debe configurar el acceso a datos de MongoDB en su aplicación de App Services. Para saber cómo configurar su aplicación backend para que el SDK de Realm pueda consultar Atlas, consulte "Configurar el acceso a datos de MongoDB" en la documentación de App Services.
Ejemplo
Datos de ejemplo
Los ejemplos en esta página usan la siguiente colección MongoDB que describe varias plantas a la venta en una cadena de tiendas de plantas:
{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42" }, { _id: ObjectId("5f87976b7b800b285345a8c5"), name: "sweet basil", sunlight: "partial", color: "green", type: "annual", _partition: "Store 42" }, { _id: ObjectId("5f87976b7b800b285345a8c6"), name: "thai basil", sunlight: "partial", color: "green", type: "perennial", _partition: "Store 42" }, { _id: ObjectId("5f87976b7b800b285345a8c7"), name: "helianthus", sunlight: "full", color: "yellow", type: "annual", _partition: "Store 42" }, { _id: ObjectId("5f87976b7b800b285345a8c8"), name: "petunia", sunlight: "full", color: "purple", type: "annual", _partition: "Store 47" }
Documentos en el plants La colección utiliza el siguiente esquema:
{ "title": "Plant", "bsonType": "object", "required": ["_id", "_partition", "name"], "properties": { "_id": { "bsonType": "objectId" }, "_partition": { "bsonType": "string" }, "name": { "bsonType": "string" }, "sunlight": { "bsonType": "string" }, "color": { "bsonType": "string" }, "type": { "bsonType": "string" } } }
type Plant = { _id: BSON.ObjectId; _partition: string; name: string; sunlight?: string; color?: string; type?: string; };
Conectarse a un clúster vinculado
To access a linked cluster from your client application, authenticate a user and pass the cluster name to User.mongoClient(). This returns a MongoDB service interface that you can use to access databases and collections in the cluster.
If you are using @realm/react, you can access the MongoDB client with the useUser() hook in a component wrapped by UserProvider.
import React from 'react'; import {useUser} from '@realm/react'; function QueryPlants() { // Get currently logged in user const user = useUser(); const getPlantByName = async name => { // Access linked MongoDB collection const mongodb = user.mongoClient('mongodb-atlas'); const plants = mongodb.db('example').collection('plants'); // Query the collection const response = await plants.findOne({name}); return response; }; // ... }
import React from 'react'; import {useUser} from '@realm/react'; function QueryPlants() { // Get currently logged in user const user = useUser(); const getPlantByName = async (name: string) => { // Access linked MongoDB collection const mongodb = user.mongoClient('mongodb-atlas'); const plants = mongodb.db('example').collection<Plant>('plants'); // Query the collection const response = await plants.findOne({name}); return response; }; // ... }
Operaciones de lectura
Find a Single Document
Para encontrar un solo documento, envíe una consulta que coincida con el documento a collection.findOne(). Si no envía ninguna consulta, findOne() coincide con el primer documento que encuentre en la colección.
El siguiente snippet encuentra el documento que describe las plantas "venus atrapamoscas" en la colección de documentos que describen plantas en venta en un grupo de tiendas:
const venusFlytrap = await plants.findOne({ name: "venus flytrap" }); console.log("venusFlytrap", venusFlytrap);
{ _id: ObjectId("5f87976b7b800b285345a8c4"), name: "venus flytrap", sunlight: "full", color: "white", type: "perennial", _partition: "Store 42", }
Encuentra varios documentos
Para encontrar varios documentos, envíe una consulta que coincida con los documentos a collection.find(). Si no envía ninguna consulta, find() coincide con todos los documentos de la colección.
El siguiente fragmento encuentra todos los documentos que describen plantas perennes en la colección de documentos que describen plantas en venta en un grupo de tiendas:
const perennials = await plants.find({ type: "perennial" }); console.log("perennials", perennials);
[ { _id: ObjectId("5f87976b7b800b285345a8c4"), name: 'venus flytrap', sunlight: 'full', color: 'white', type: 'perennial', _partition: 'Store 42' }, { _id: ObjectId("5f87976b7b800b285345a8c6"), name: 'thai basil', sunlight: 'partial', color: 'green', type: 'perennial', _partition: 'Store 42' }, { _id: ObjectId("5f879f83fc9013565c23360e"), name: 'lily of the valley', sunlight: 'full', color: 'white', type: 'perennial', _partition: 'Store 47' }, { _id: ObjectId("5f87a0defc9013565c233611"), name: 'rhubarb', sunlight: 'full', color: 'red', type: 'perennial', _partition: 'Store 47' }, { _id: ObjectId("5f87a0dffc9013565c233612"), name: 'wisteria lilac', sunlight: 'partial', color: 'purple', type: 'perennial', _partition: 'Store 42' }, { _id: ObjectId("5f87a0dffc9013565c233613"), name: 'daffodil', sunlight: 'full', color: 'yellow', type: 'perennial', _partition: 'Store 42' } ]
Contabilizar documentos
Para contar documentos, pasa una query que coincida con los documentos a colección.count(). Si no pasa un query, count() cuenta todos los documentos de la colección.
El siguiente fragmento cuenta la cantidad de documentos en una colección de documentos que describen plantas en venta en un grupo de tiendas:
const numPlants = await plants.count(); console.log(`There are ${numPlants} plants in the collection`);
"There are 9 plants in the collection"
Operaciones de escritura
Inserta un solo documento
Para insertar un solo documento, pásalo a collection.insertOne().
The following snippet inserts a single document describing a "lily of the valley" plant into a collection of documents that describe plants for sale in a group of stores:
const result = await plants.insertOne({ name: "lily of the valley", sunlight: "full", color: "white", type: "perennial", _partition: "Store 47", }); console.log(result);
{ insertedId: "5f879f83fc9013565c23360e", }
Inserta varios documentos
Para insertar varios documentos al mismo tiempo, páselos como una matriz a collection.insertMany().
El siguiente fragmento inserta tres documentos que describen plantas en una colección de documentos que describen plantas en venta en un grupo de tiendas:
const result = await plants.insertMany([ { name: "rhubarb", sunlight: "full", color: "red", type: "perennial", _partition: "Store 47", }, { name: "wisteria lilac", sunlight: "partial", color: "purple", type: "perennial", _partition: "Store 42", }, { name: "daffodil", sunlight: "full", color: "yellow", type: "perennial", _partition: "Store 42", }, ]); console.log(result);
{ insertedIds: [ "5f87a0defc9013565c233611", "5f87a0dffc9013565c233612", "5f87a0dffc9013565c233613", ], }
Actualiza un solo documento
Para actualizar un solo documento, pase una consulta que coincida con el documento y un documento de actualización a collection.updateOne().
The following snippet updates a single document in a collection of documents that describe plants for sale in a group of stores. This operation queries for a document where the name field contains the value "petunia" and changes the value of the first matched document's sunlight field to "partial":
const result = await plants.updateOne( { name: "petunia" }, { $set: { sunlight: "partial" } } ); console.log(result);
{ matchedCount: 1, modifiedCount: 1 }
Actualiza varios documentos
Para actualizar varios documentos al mismo tiempo, pasar una query que coincida con los documentos y una descripción de actualizar a colección.updateMany().
El siguiente fragmento actualiza varios documentos de una colección que describen plantas en venta en un grupo de tiendas. Esta operación busca documentos cuyo _partition campo contenga el valor "Tienda 47" y cambia el valor del _partition campo de cada documento coincidente a "Tienda 51":
const result = await plants.updateMany( { _partition: "Store 47" }, { $set: { _partition: "Store 51" } } ); console.log(result);
{ matchedCount: 3, modifiedCount: 3 }
Realizar inserción de documentos
Para hacer una inserción de un documento, configure la opción upsert en true en su operación de actualización. Si la query de la operación no coincide con ningún documento en la colección, una inserción inserta automáticamente un nuevo documento único en la colección que coincide con el documento de query proporcionado con la actualización aplicada a este.
El siguiente fragmento actualiza un documento de una colección que describe plantas en venta en un grupo de tiendas mediante una operación upsert. La consulta no coincide con ningún documento existente, por lo que MongoDB crea uno nuevo automáticamente.
const result = await plants.updateOne( { sunlight: "full", type: "perennial", color: "green", _partition: "Store 47", }, { $set: { name: "sweet basil" } }, { upsert: true } ); console.log(result);
{ matchedCount: 0, modifiedCount: 0, upsertedId: ObjectId("5f1f63055512f2cb67f460a3"), }
Borrar un único documento
Para eliminar un solo documento de una colección, pasa una consulta que coincida con el documento a collection.deleteOne(). Si no se pasa una query o si la query coincide con varios documentos, la operación borra el primer documento que encuentra.
The following snippet deletes one document in a collection of documents that describe plants for sale in a group of stores. This operation queries for a document where the color field has a value of "green" and deletes the first document that matches the query:
const result = await plants.deleteOne({ color: "green" }); console.log(result);
{ deletedCount: 1 }
Borra varios documentos
To delete multiple document from a collection, pass a query that matches the documents to collection.deleteMany(). If you do not pass a query, deleteMany() deletes all documents in the collection.
The following snippet deletes all documents for plants that are in "Store 51" in a collection of documents that describe plants for sale in a group of stores:
const result = await plants.deleteMany({ _partition: "Store 51", }); console.log(result);
{ deletedCount: 3 }
Notificaciones de cambio en tiempo real
You can call collection.watch() to subscribe to real-time change notifications that MongoDB emits whenever a document in the collection is added, modified, or deleted. Each notification specifies a document that changed, how it changed, and the full document after the operation that caused the event.
collection.watch() devuelve un generador asíncronoque le permite extraer de forma asincrónica eventos de cambio para las operaciones a medida que ocurren.
collection.watch() Requiere cierta configuración para funcionar con una aplicación cliente de React Native. Para supervisar los cambios en una colección, primero debe instalar los paquetes react-native-polyfill-globals y @babel/plugin-proposal-async-generator-functions.
Para utilizar collection.watch():
Instalar las dependencias.
npm install react-native-polyfill-globals text-encoding npm install --save-dev @babel/plugin-proposal-async-generator-functions Importa polyfills en un ámbito superior al lugar donde necesitas usarlos. Por ejemplo, en
index.js.import { polyfill as polyfillReadableStream } from "react-native-polyfill-globals/src/readable-stream"; import { polyfill as polyfillEncoding } from "react-native-polyfill-globals/src/encoding"; import { polyfill as polyfillFetch } from "react-native-polyfill-globals/src/fetch"; polyfillReadableStream(); polyfillEncoding(); polyfillFetch();
Importante
Serverless Limitations
No puedes observar cambios si la fuente de datos es una instancia sin servidor de Atlas. El servidor sin servidor de MongoDB actualmente no admite flujos de cambios, que se emplean en las colecciones monitoreadas para escuchar cambios.
Supervisar todos los cambios en una colección
With the @realm/react package, make sure you have configured user authentication for your app.
Para supervisar todos los cambios en una colección, llame a collection.watch() sin argumentos. Esta llamada debe estar encapsulada por un UserProvider con un usuario autenticado.
import React, {useEffect} from 'react'; import Realm from 'realm'; import {useUser, useApp, AppProvider, UserProvider} from '@realm/react'; function AppWrapper() { return ( <AppProvider id={APP_ID}> <UserProvider fallback={<LogIn />}> <NotificationSetter /> </UserProvider> </AppProvider> ); } function NotificationSetter() { // Get currently logged in user const user = useUser(); const watchForAllChanges = async ( plants, ) => { // Watch for changes to the plants collection for await (const change of plants.watch()) { switch (change.operationType) { case 'insert': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'update': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'replace': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'delete': { const {documentKey} = change; // ... do something with the change information. break; } } } }; useEffect(() => { const plants = user .mongoClient('mongodb-atlas') .db('example') .collection('plants'); // Set up notifications watchForAllChanges(plants); }, [user, watchForAllChanges]); // ... rest of component }
import React, {useEffect} from 'react'; import Realm from 'realm'; import {useUser, useApp, AppProvider, UserProvider} from '@realm/react'; function AppWrapper() { return ( <AppProvider id={APP_ID}> <UserProvider fallback={<LogIn />}> <NotificationSetter /> </UserProvider> </AppProvider> ); } function NotificationSetter() { // Get currently logged in user const user = useUser(); const watchForAllChanges = async ( plants: Realm.Services.MongoDB.MongoDBCollection<Plant>, ) => { // Watch for changes to the plants collection for await (const change of plants.watch()) { switch (change.operationType) { case 'insert': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'update': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'replace': { const {documentKey, fullDocument} = change; // ... do something with the change information. break; } case 'delete': { const {documentKey} = change; // ... do something with the change information. break; } } } }; useEffect(() => { const plants = user! .mongoClient('mongodb-atlas') .db('example') .collection<Plant>('plants'); // Set up notifications watchForAllChanges(plants); }, [user, watchForAllChanges]); // ... rest of component }
Esté atento a cambios específicos en una colección
Para observar cambios específicos en una colección, pase una consulta que coincida con los campos de eventos de cambio a collection.watch():
for await (const change of plants.watch({ filter: { operationType: "insert", "fullDocument.type": "perennial", }, })) { // The change event will always represent a newly inserted perennial const { documentKey, fullDocument } = change; console.log(`new document: ${documentKey}`, fullDocument); }
Operaciones de agregación
Las operaciones de agregación procesan todos los documentos de una colección a través de una serie de etapas denominadas pipeline de agregación. La agregación te permite filtrar y transformar documentos, recopilar datos resumidos sobre grupos de documentos relacionados y otras operaciones complejas de datos.
Ejecuta una pipeline de agregación
To execute an aggregation pipeline, pass an array of aggregation stages to collection.aggregate(). Aggregation operations return the result set of the last stage in the pipeline.
The following snippet groups all documents in the plants collection by their type value and aggregates a count of the number of each type:
const result = await plants.aggregate([ { $group: { _id: "$type", total: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]); console.log(result);
[ { _id: "annual", total: 1 }, { _id: "perennial", total: 5 }, ]
Filtrar Documentos
Puedes usar la etapa $match para filtrar documentos según la sintaxis de queryestándar de MongoDB.
{ "$match": { "<Field Name>": <Query Expression>, ... } }
Ejemplo
El siguiente filtro de etapa $match filtra documentos para incluir solo aquellos donde el campo type tenga un valor igual a "perenne":
const perennials = await plants.aggregate([ { $match: { type: { $eq: "perennial" } } }, ]); console.log(perennials);
[ { "_id": ObjectId("5f87976b7b800b285345a8c4"), "_partition": "Store 42", "color": "white", "name": "venus flytrap", "sunlight": "full", "type": "perennial" }, { "_id": ObjectId("5f87976b7b800b285345a8c6"), "_partition": "Store 42", "color": "green", "name": "thai basil", "sunlight": "partial", "type": "perennial" }, { "_id": ObjectId("5f87a0dffc9013565c233612"), "_partition": "Store 42", "color": "purple", "name": "wisteria lilac", "sunlight": "partial", "type": "perennial" }, { "_id": ObjectId("5f87a0dffc9013565c233613"), "_partition": "Store 42", "color": "yellow", "name": "daffodil", "sunlight": "full", "type": "perennial" }, { "_id": ObjectId("5f1f63055512f2cb67f460a3"), "_partition": "Store 47", "color": "green", "name": "sweet basil", "sunlight": "full", "type": "perennial" } ]
Documentos de grupo
You can use the $group stage to aggregate summary data for one or more documents. MongoDB groups documents based on the expression defined in the _id field of the $group stage. You can reference a specific document field by prefixing the field name with a $.
{ "$group": { "_id": <Group By Expression>, "<Field Name>": <Aggregation Expression>, ... } }
Ejemplo
La siguiente etapa $group organiza los documentos por valor de su campo type y calcula la cantidad de documentos de plantas en los que cada valor único type aparece.
const result = await plants.aggregate([ { $group: { _id: "$type", numItems: { $sum: 1 }, }, }, { $sort: { _id: 1 } }, ]); console.log(result);
[ { _id: "annual", numItems: 1 }, { _id: "perennial", numItems: 5 }, ]
Paginación de documentos
Para paginar los resultados puedes utilizar consultas de agregación por rangos con los operadores $match, $sort, y $limit. Para obtener más información sobre cómo paginar documentos, consulta Uso de consultas por rango en la documentación de MongoDB Server.
Ejemplo
The following example paginates through a collection of documents in ascending order.
// Paginates through list of plants // in ascending order by plant name (A -> Z) async function paginateCollectionAscending( collection, nPerPage, startValue ) { const pipeline = [{ $sort: { name: 1 } }, { $limit: nPerPage }]; // If not starting from the beginning of the collection, // only match documents greater than the previous greatest value. if (startValue !== undefined) { pipeline.unshift({ $match: { name: { $gt: startValue }, }, }); } const results = await collection.aggregate(pipeline); return results; } // Number of results to show on each page const resultsPerPage = 3; const pageOneResults = await paginateCollectionAscending( plants, resultsPerPage ); const pageTwoStartValue = pageOneResults[pageOneResults.length - 1].name; const pageTwoResults = await paginateCollectionAscending( plants, resultsPerPage, pageTwoStartValue ); // ... can keep paginating for as many plants as there are in the collection
Campos del documento del proyecto
Puede usar la etapa $project para incluir u omitir campos específicos de los documentos o para calcular nuevos campos mediante operadores de agregación. Las proyecciones funcionan de dos maneras:
Incluya de forma explícita campos con un valor de 1. Esto tiene el efecto secundario de excluir, de forma implícita, todos los campos no especificados.
Implicitly exclude fields with a value of 0. This has the side-effect of implicitly including all unspecified fields.
Estos dos métodos de proyección son mutuamente excluyentes: si incluyes explícitamente campos, no puedes excluir explícitamente campos y viceversa.
Nota
The _id field is a special case: it is always included in every query unless explicitly specified otherwise. For this reason, you can exclude the _id field with a 0 value while simultaneously including other fields, like _partition, with a 1. Only the special case of exclusion of the _id field allows both exclusion and inclusion in one $project stage.
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
Ejemplo
The following $project stage omits the _id field, includes the name field, and creates a new field named storeNumber. The storeNumber is generated using two aggregation operators:
$splitseparates the_partitionvalue into two string segments surrounding the space character. For example, the value "Store 42" split in this way returns an array with two elements: "Store" and "42".$arrayElemAtselects a specific element from an array based on the second argument. In this case, the value1selects the second element from the array generated by the$splitoperator since arrays index from0. For example, the value ["Store", "42"] passed to this operation would return a value of "42".
const result = await plants.aggregate([ { $project: { _id: 0, name: 1, storeNumber: { $arrayElemAt: [{ $split: ["$_partition", " "] }, 1], }, }, }, ]); console.log(result);
[ { "name": "venus flytrap", "storeNumber": "42" }, { "name": "thai basil", "storeNumber": "42" }, { "name": "helianthus", "storeNumber": "42" }, { "name": "wisteria lilac", "storeNumber": "42" }, { "name": "daffodil", "storeNumber": "42" }, { "name": "sweet basil", "storeNumber": "47" } ]
Agregar campos a los documentos
Puedes utilizar la etapa $addFields para añadir nuevos campos con valores calculados utilizando operadores de agregación.
{ $addFields: { <newField>: <expression>, ... } }
Nota
$addFields es similar a $project pero no te permite incluir ni omitir campos.
Ejemplo
The following $addFields stage creates a new field named storeNumber where the value is the output of two aggregate operators that transform the value of the _partition field.
const result = await plants.aggregate([ { $addFields: { storeNumber: { $arrayElemAt: [{ $split: ["$_partition", " "] }, 1], }, }, }, ]); console.log(result);
[ { "_id": ObjectId("5f87976b7b800b285345a8c4"), "_partition": "Store 42", "color": "white", "name": "venus flytrap", "storeNumber": "42", "sunlight": "full", "type": "perennial" }, { "_id": ObjectId("5f87976b7b800b285345a8c6"), "_partition": "Store 42", "color": "green", "name": "thai basil", "storeNumber": "42", "sunlight": "partial", "type": "perennial" }, { "_id": ObjectId("5f87976b7b800b285345a8c7"), "_partition": "Store 42", "color": "yellow", "name": "helianthus", "storeNumber": "42", "sunlight": "full", "type": "annual" }, { "_id": ObjectId("5f87a0dffc9013565c233612"), "_partition": "Store 42", "color": "purple", "name": "wisteria lilac", "storeNumber": "42", "sunlight": "partial", "type": "perennial" }, { "_id": ObjectId("5f87a0dffc9013565c233613"), "_partition": "Store 42", "color": "yellow", "name": "daffodil", "storeNumber": "42", "sunlight": "full", "type": "perennial" }, { "_id": ObjectId("5f1f63055512f2cb67f460a3"), "_partition": "Store 47", "color": "green", "name": "sweet basil", "storeNumber": "47", "sunlight": "full", "type": "perennial" } ]
Unwind Array Values
You can use the $unwind stage to transform a single document containing an array into multiple documents containing individual values from that array. When you unwind an array field, MongoDB copies each document once for each element of the array field but replaces the array value with the array element in each copy.
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
Ejemplo
El siguiente ejemplo utiliza la etapa $unwind para la combinación type y color de cada objeto. La canalización de agregación consta de los siguientes pasos:
Use
$groupstage with$addToSetto create new documents for eachtypewith a new fieldcolorsthat contains an array of all the the colors for that flower type that occur in the collection.Utilice la etapa
$unwindpara crear documentos separados para cada combinación de tipo y color.Use
$sortstage to sort the results in alphabetical order.
const result = await plants.aggregate([ { $group: { _id: "$type", colors: { $addToSet: "$color" } } }, { $unwind: { path: "$colors" } }, { $sort: { _id: 1, colors: 1 } }, ]); console.log(result);
[ { "_id": "annual", "colors": "yellow" }, { "_id": "perennial", "colors": "green" }, { "_id": "perennial", "colors": "purple" }, { "_id": "perennial", "colors": "white" }, { "_id": "perennial", "colors": "yellow" }, ]