Puede consultar datos almacenados en MongoDB Atlas directamente desde el código de su aplicación Android utilizando MongoClient del SDK Java de Realm con el 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.
Las siguientes acciones permiten el acceso a un clúster MongoDB Atlas vinculado desde una aplicación de Android mediante el SDK de Realm.
Nota
Cada operación descrita en esta página utiliza una consulta para buscar coincidencias en determinados documentos de la colección en la que se ejecuta la operación. Cuando un filtro busca coincidencias en varios documentos de una colección, estos se devuelven en un orden indeterminado a menos que se especifique un parámetro de ordenación. Esto significa que si no se especifica una ordenación para los... findOne(), o, updateOne() deleteOne() su operación podría coincidir con cualquier documento que coincida con la consulta. Para más información sobre la ordenación, consulte cursor.sort().
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:
El conjunto de datos es grande o el dispositivo cliente tiene restricciones para cargar todo el conjunto de datos
Estás recuperando documentos que no están modelados en Realm
Su aplicación necesita acceder a colecciones que no tienen esquemas estrictos
Un servicio que no es de Realm genera colecciones a las que desea acceder
Si bien no son exhaustivos, estos son algunos casos de uso comunes para consultar MongoDB directamente.
Requisitos previos
Antes de poder consultar MongoDB desde su aplicación Android, 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.
Configurar el proyecto
Configurar el proyecto
Siga los pasos de la guía Instalar el SDK de Java de Realm.
Vincular un clúster de servicios de MongoDB Atlas
Sigue los pasos de la guía "Vincular una fuente de datos de MongoDB". Asigna un nombre significativo a tu servicio; lo necesitarás para conectarte al clúster mediante el SDK de Realm.
Importar dependencias de Realm
Para las operaciones CRUD en una colección remota de MongoDB, utilizará una o más de las siguientes declaraciones import:
// Base Realm Packages import io.realm.mongodb.App; import io.realm.mongodb.AppConfiguration; // Realm Authentication Packages import io.realm.mongodb.User; import io.realm.mongodb.Credentials; // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient; import io.realm.mongodb.mongo.MongoDatabase; import io.realm.mongodb.mongo.MongoCollection; // Utility Packages import org.bson.Document;
// Base Realm Packages import io.realm.mongodb.App import io.realm.mongodb.AppConfiguration // Realm Authentication Packages import io.realm.mongodb.User import io.realm.mongodb.Credentials // MongoDB Service Packages import io.realm.mongodb.mongo.MongoClient import io.realm.mongodb.mongo.MongoDatabase import io.realm.mongodb.mongo.MongoCollection // Utility Packages import org.bson.Document
Crear una instancia de un manejador de colección de MongoDB
Para conectarse a una instancia de MongoDB, necesitará un usuario con acceso a una colección de MongoDB. Inicie sesión en su aplicación como tal y use el siguiente código para instanciar un identificador local de colección de MongoDB.
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle");
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) Log.v("EXAMPLE", "Successfully instantiated the MongoDB collection handle")
Nota
Uso de clases personalizadas con MongoDB
Para usar clases distintas a la Document clase integrada con MongoDB, puede agregar códecs a sus MongoCollection instancias. En el ejemplo anterior, agregamos PojoCodecProvider para admitir objetos Java estándar (POJO). La compatibilidad con objetos personalizados requiere dos proveedores de códecs:
el proveedor de códec predeterminado, que proporciona soporte para tipos Java integrados (a los que se accede a través de
AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY)PojoCodecProvider, que crea automáticamente nuevos códecs para admitir clases POJO
El SDK revisa los registros en orden hasta que uno devuelve un códec para la clase solicitada. Por lo tanto, debe listar primero el registro de códec predeterminado y PojoCodecProvider siempre debe ser el último CodecProvider, ya que puede proporcionar un códec para casi cualquier clase.
Datos de ejemplo
Los siguientes ejemplos operan en una colección de MongoDB que describe el inventario en una cadena de tiendas de plantas. Considera la siguiente colección de documentos que describen varios tipos de plantas en venta en un almacenar:
import org.bson.codecs.pojo.annotations.BsonProperty; import org.bson.types.ObjectId; public class Plant { private ObjectId id; private String name; private String sunlight; private String color; private String type; private String partition; // empty constructor required for MongoDB Data Access POJO codec compatibility public Plant() {} public Plant(ObjectId id, String name, String sunlight, String color, String type, String partition) { this.id = id; this.name = name; this.sunlight = sunlight; this.color = color; this.type = type; this.partition = partition; } public ObjectId getId() { return id; } public void setId(ObjectId id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSunlight() { return sunlight; } public void setSunlight(String sunlight) { this.sunlight = sunlight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getPartition() { return partition; } public void setPartition(String partition) { this.partition = partition; } public String toString() { return "Plant [id=" + id + ", name=" + name + ", sunlight=" + sunlight + ", color=" + color + ", type=" + type + ", partition=" + partition + "]"; } }
User user = app.currentUser(); MongoClient mongoClient = user.getMongoClient("mongodb-atlas"); MongoDatabase mongoDatabase = mongoClient.getDatabase("plant-data-database"); // registry to handle POJOs (Plain Old Java Objects) CodecRegistry pojoCodecRegistry = fromRegistries(AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, fromProviders(PojoCodecProvider.builder().automatic(true).build())); MongoCollection<Plant> mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant.class).withCodecRegistry(pojoCodecRegistry); mongoCollection.insertMany(Arrays.asList( new Plant(new ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42"), new Plant(new ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42"), new Plant(new ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42"), new Plant(new ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42"), new Plant(new ObjectId(), "petunia", "full", "purple", "annual", "Store 47"))); Log.v("EXAMPLE", "Successfully inserted the sample data.");
import org.bson.codecs.pojo.annotations.BsonProperty import org.bson.types.ObjectId open class Plant(val id : ObjectId = ObjectId(), var name : String? = null, var sunlight : String? = null, var color : String? = null, var type : String? = null, ("_partition") // specify that this is a field-level annotation var partition : String? = null) { override fun toString(): String { return "Plant [id=$id, name=$name, sunlight=$sunlight, color=$color, type=$type, partition=$partition]" } }
val user = app.currentUser() val mongoClient = user!!.getMongoClient("mongodb-atlas") val mongoDatabase = mongoClient.getDatabase("plant-data-database") // registry to handle POJOs (Plain Old Java Objects) val pojoCodecRegistry = CodecRegistries.fromRegistries( AppConfiguration.DEFAULT_BSON_CODEC_REGISTRY, CodecRegistries.fromProviders( PojoCodecProvider.builder().automatic(true).build())) val mongoCollection = mongoDatabase.getCollection( "plant-data-collection", Plant::class.java).withCodecRegistry(pojoCodecRegistry) mongoCollection.insertMany( listOf( Plant( ObjectId(), "venus flytrap", "full", "white", "perennial", "Store 42" ), Plant( ObjectId(), "sweet basil", "partial", "green", "annual", "Store 42" ), Plant( ObjectId(), "thai basil", "partial", "green", "perennial", "Store 42" ), Plant( ObjectId(), "helianthus", "full", "yellow", "annual", "Store 42" ), Plant( ObjectId(), "petunia", "full", "purple", "annual", "Store 47" ) ) ) Log.v("EXAMPLE", "Successfully Successfully inserted the sample data.")
Crear documentos
Estos fragmentos de código muestran cómo insertar uno o más documentos en una colección de MongoDB desde una aplicación móvil. Las operaciones de inserción toman como argumento uno o más documentos para agregarlos a MongoDB y devuelven una RealmResultTask que se resuelve en un objeto que contiene los resultados de la ejecución de la operación.
Inserta un solo documento
Puede insertar un solo documento utilizando collection.insertOne().
El siguiente fragmento inserta un único documento que describe una planta de "lirio de los valles" en una colección de documentos que describen plantas a la venta en un grupo de tiendas:
Plant plant = new Plant( new ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47"); mongoCollection.insertOne(plant).getAsync(task -> { if (task.isSuccess()) { Log.v("EXAMPLE", "successfully inserted a document with id: " + task.get().getInsertedId()); } else { Log.e("EXAMPLE", "failed to insert documents with: " + task.getError().getErrorMessage()); } });
val plant = Plant( ObjectId(), "lily of the valley", "full", "white", "perennial", "Store 47" ) mongoCollection?.insertOne(plant)?.getAsync { task -> if (task.isSuccess) { Log.v( "EXAMPLE", "successfully inserted a document with id: ${task.get().insertedId}" ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully inserted a document with id: BsonObjectId{value=5f19...}
Inserta varios documentos
Puede insertar varios documentos al mismo tiempo utilizando 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:
List<Plant> plants = Arrays.asList( new Plant(new ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47"), new Plant(new ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42"), new Plant(new ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42")); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val plants = listOf( Plant( ObjectId(), "rhubarb", "full", "red", "perennial", "Store 47" ), Plant( ObjectId(), "wisteria lilac", "partial", "purple", "perennial", "Store 42" ), Plant( ObjectId(), "daffodil", "full", "yellow", "perennial", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v( "EXAMPLE", "successfully inserted $insertedCount documents into the collection." ) } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully inserted 3 documents into the collection.
Lea los documentos
Estos fragmentos de código muestran cómo leer datos almacenados en una colección de MongoDB desde una aplicación móvil. Las operaciones de lectura utilizan consultas para especificar qué documentos devolver de la base de datos. Las operaciones de lectura devuelven una tarea.que se resuelve en un solo documento coincidente (en el caso findOne() de), un long valor numérico (en el caso count() de) o un iterador que le permite recorrer la colección de documentos coincidentes (en el caso find() de).
Buscar un solo documento
Puede encontrar un solo documento utilizando collection.findOne().
El siguiente fragmento encuentra un solo documento de una colección de documentos que describen plantas a la venta en un grupo de tiendas donde el campo del documento de planta type contiene el valor de cadena "perenne":
Document queryFilter = new Document("type", "perennial"); mongoCollection.findOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { Plant result = task.get(); Log.v("EXAMPLE", "successfully found a document: " + result); } else { Log.e("EXAMPLE", "failed to find document with: ", task.getError()); } });
val queryFilter = Document("type", "perennial") mongoCollection.findOne(queryFilter) .getAsync { task -> if (task.isSuccess) { val result = task.get() Log.v("EXAMPLE", "successfully found a document: $result") } else { Log.e("EXAMPLE", "failed to find document with: ${task.error}") } }
La ejecución de este fragmento produce un resultado similar al siguiente:
V/EXAMPLE: successfully found a document: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42]
Encuentra varios documentos
Puede encontrar varios documentos utilizando collection.find().
El siguiente fragmento busca todos los documentos en una colección de documentos que describen plantas en venta en un grupo de tiendas que contienen un campo llamado _partition con un valor de "Tienda 42":
Document queryFilter = new Document("_partition", "Store 42"); RealmResultTask<MongoCursor<Plant>> findTask = mongoCollection.find(queryFilter).iterator(); findTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Plant> results = task.get(); Log.v("EXAMPLE", "successfully found all plants for Store 42:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to find documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 42") val findTask = mongoCollection.find(queryFilter).iterator() findTask.getAsync { task -> if (task.isSuccess) { val results = task.get() Log.v("EXAMPLE", "successfully found all plants for Store 42:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to find documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully found all plants for Store 42: V/EXAMPLE: Plant [id=5f18..., name=venus flytrap, sunlight=full, color=white, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=sweet basil, sunlight=partial, color=green, type=annual, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=thai basil, sunlight=partial, color=green, type=perennial, partition=Store 42] V/EXAMPLE: Plant [id=5f18..., name=helianthus, sunlight=full, color=yellow, type=annual, partition=Store 42]
Contar documentos en la colección
Puedes contar los documentos de una colección usando collection.count(). Puedes especificar una consulta opcional para determinar qué documentos contar. Si no especificas una consulta, la acción 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:
mongoCollection.count().getAsync(task -> { if (task.isSuccess()) { long count = task.get(); Log.v("EXAMPLE", "successfully counted, number of documents in the collection: " + count); } else { Log.e("EXAMPLE", "failed to count documents with: ", task.getError()); } });
mongoCollection.count().getAsync { task -> if (task.isSuccess) { val count = task.get() Log.v("EXAMPLE", "successfully counted, number of documents in the collection: $count") } else { Log.e("EXAMPLE", "failed to count documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully counted, number of documents in the collection: 5
Update Documents
Estos fragmentos de código muestran cómo actualizar los datos almacenados en una colección de MongoDB desde una aplicación móvil. Las operaciones de actualización utilizan consultas para especificar qué documentos actualizar y operadores de actualización para describir cómo modificar los documentos que coinciden con la consulta. Las operaciones de actualización devuelven una tarea que se resuelve en un objeto que contiene los resultados de la ejecución de la operación.
Actualiza un solo documento
Puede actualizar un solo documento utilizando collection.updateOne().
El siguiente fragmento actualiza un documento de una colección que describe plantas a la venta en un grupo de tiendas. Esta operación busca un documento cuyo name campo contiene el valor "petunia" y cambia el valor del campo del primer documento coincidente sunlight a "parcial":
Document queryFilter = new Document("name", "petunia"); Document updateDocument = new Document("$set", new Document("sunlight", "partial")); mongoCollection.updateOne(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully updated a document."); } else { Log.v("EXAMPLE", "did not update a document."); } } else { Log.e("EXAMPLE", "failed to update document with: ", task.getError()); } });
val queryFilter = Document("name", "petunia") val updateDocument = Document("\$set", Document("sunlight", "partial")) mongoCollection.updateOne(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count == 1L) { Log.v("EXAMPLE", "successfully updated a document.") } else { Log.v("EXAMPLE", "did not update a document.") } } else { Log.e("EXAMPLE", "failed to update document with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully updated a document.
Actualiza varios documentos
Puede actualizar varios documentos utilizando collection.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":
Document queryFilter = new Document("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("_partition", "Store 51")); mongoCollection.updateMany(queryFilter, updateDocument).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getModifiedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully updated " + count + " documents."); } else { Log.v("EXAMPLE", "did not update any documents."); } } else { Log.e("EXAMPLE", "failed to update documents with: ", task.getError()); } });
val queryFilter = Document("_partition", "Store 47") val updateDocument = Document("\$set", Document("_partition", "Store 51")) mongoCollection.updateMany(queryFilter, updateDocument).getAsync { task -> if (task.isSuccess) { val count = task.get().modifiedCount if (count != 0L) { Log.v("EXAMPLE", "successfully updated $count documents.") } else { Log.v("EXAMPLE", "did not update any documents.") } } else { Log.e("EXAMPLE", "failed to update documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully updated 2 documents.
Insertar documentos
Si una operación de actualización no coincide con ningún documento de la colección, puede insertar automáticamente un solo documento nuevo en la colección que coincida con la consulta de actualización configurando la opción upsert en true.
El siguiente fragmento actualiza un documento de una colección que describe plantas en venta en un grupo de tiendas o inserta un nuevo documento si ningún documento coincide con la consulta. Esta operación busca documentos donde:
El campo
sunlighttiene un valor de "completo"El campo
typetiene un valor de "perenne"El campo
colortiene un valor de "verde"el campo
_partitiontiene el valor "Store 47".
Debido a que este fragmento establece la opción upsert en true, si ningún documento coincide con la consulta, MongoDB crea un nuevo documento que incluye tanto la consulta como las actualizaciones especificadas:
Document queryFilter = new Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47"); Document updateDocument = new Document("$set", new Document("name", "sweet basil")); UpdateOptions updateOptions = new UpdateOptions().upsert(true); mongoCollection.updateOne(queryFilter, updateDocument, updateOptions).getAsync(task -> { if (task.isSuccess()) { if(task.get().getUpsertedId() != null) { Log.v("EXAMPLE", "successfully upserted a document with id " + task.get().getUpsertedId()); } else { Log.v("EXAMPLE", "successfully updated a document."); } } else { Log.e("EXAMPLE", "failed to update or insert document with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full") .append("type", "perennial") .append("color", "green") .append("_partition", "Store 47") val updateDocument = Document("\$set", Document("name", "sweet basil")) val updateOptions = UpdateOptions().upsert(true) mongoCollection.updateOne(queryFilter, updateDocument, updateOptions) .getAsync { task -> if (task.isSuccess) { if (task.get().upsertedId != null) { Log.v("EXAMPLE", "successfully upserted a document with id ${task.get().upsertedId}") } else { Log.v("EXAMPLE", "successfully updated a document.") } } else { Log.e("EXAMPLE", "failed to update or insert document with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully upserted a document with id: BsonObjectId{value=5f19...}
Delete Documents
Estos fragmentos de código muestran cómo eliminar documentos almacenados en una colección de MongoDB desde una aplicación móvil. Las operaciones de eliminación utilizan una consulta para especificar los documentos que se eliminarán y devuelven una tarea que se resuelve en un objeto que contiene los resultados de la ejecución de la operación.
Borrar un único documento
Puedes eliminar un solo documento de una colección utilizando collection.deleteOne().
El siguiente fragmento elimina un documento de una colección que describe plantas en venta en un grupo de tiendas. Esta operación busca un documento cuyo color campo tenga el valor "verde" y elimina el primer documento que coincida con la consulta:
Document queryFilter = new Document("color", "green"); mongoCollection.deleteOne(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count == 1) { Log.v("EXAMPLE", "successfully deleted a document."); } else { Log.v("EXAMPLE", "did not delete a document."); } } else { Log.e("EXAMPLE", "failed to delete document with: ", task.getError()); } });
val queryFilter = Document("color", "green") mongoCollection.deleteOne(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count == 1L) { Log.v("EXAMPLE", "successfully deleted a document.") } else { Log.v("EXAMPLE", "did not delete a document.") } } else { Log.e("EXAMPLE", "failed to delete document with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully deleted a document.
Borra varios documentos
Puede borrar varios elementos de una colección usando collection.deleteMany().
El siguiente snippet borra todos los documentos en una colección de documentos que describe plantas en venta en un grupo de tiendas que compara con la query que coincide con documentos que contienen tanto un valor de campo sunlight de "full" como un valor de campo type de "annual".
Document queryFilter = new Document("sunlight", "full") .append("type", "annual"); mongoCollection.deleteMany(queryFilter).getAsync(task -> { if (task.isSuccess()) { long count = task.get().getDeletedCount(); if (count != 0) { Log.v("EXAMPLE", "successfully deleted " + count + " documents."); } else { Log.v("EXAMPLE", "did not delete any documents."); } } else { Log.e("EXAMPLE", "failed to delete documents with: ", task.getError()); } });
val queryFilter = Document("sunlight", "full").append("type", "annual") mongoCollection.deleteMany(queryFilter).getAsync { task -> if (task.isSuccess) { val count = task.get().deletedCount if (count != 0L) { Log.v("EXAMPLE", "successfully deleted $count documents.") } else { Log.v("EXAMPLE", "did not delete any documents.") } } else { Log.e("EXAMPLE", "failed to delete documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: succcessfully deleted 2 documents.
Esté atento a los cambios
Estos fragmentos de código demuestran cómo configurar y ejecutar operaciones de vigilancia en una colección.
Importante
Limitaciones de la tecnología sin servidor
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.
Esté atento a los cambios en una colección
Puedes abrir un flujo de cambios realizados en una colección llamando a collection.watch() o collection.watchAsync(). Puedes supervisar los cambios en documentos específicos de una colección pasando los identificadores de los objetos que deseas supervisar como un número variable de argumentos.
El siguiente fragmento vigila los cambios en cualquier documento de la colección plants:
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection.watchAsync(); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ", result.getError()); } }); Plant triffid = new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"); mongoCollection.insertOne(triffid).getAsync(task -> { if (task.isSuccess()) { BsonObjectId insertedId = task.get().getInsertedId().asObjectId(); Log.v("EXAMPLE", "successfully inserted a document with id " + insertedId); } else { Log.e("EXAMPLE", "failed to insert document with: ", task.getError()); } });
val watcher = mongoCollection.watchAsync() watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to changes in the collection with : ${result.error}") } }] val triffid = Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ) mongoCollection.insertOne(triffid).getAsync { task -> if (task.isSuccess) { val insertedId = task.get().insertedId.asObjectId() Log.v("EXAMPLE", "successfully inserted a document with id $insertedId") } else { Log.e("EXAMPLE", "failed to insert document with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully inserted a document with id BsonObjectId{value=5f6bb...} V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=triffid, sunlight=low, color=green, type=perennial, partition=Store 47]
Esté atento a los cambios en una colección con un filtro
Puedes abrir un flujo de cambios realizados en documentos de una colección que cumplan ciertos criterios llamando a collection.watchWithFilter() o collection.watchWithFilterAsync(). Ambos métodos aceptan un parámetro Document o BsonDocument que se utiliza como la query de un operador $match para procesar cada evento de base de datos que ocurre mientras se supervisa la colección.
El siguiente fragmento monitorea los cambios en los documentos de la colección plants, pero solo activa la devolución de llamada proporcionada para los eventos correspondientes a los documentos que pertenecen a la partición denominada "Store 42":
RealmEventStreamAsyncTask<Plant> watcher = mongoCollection .watchWithFilterAsync(new Document("fullDocument._partition", "Store 42")); watcher.get(result -> { if (result.isSuccess()) { Log.v("EXAMPLE", "Event type: " + result.get().getOperationType() + " full document: " + result.get().getFullDocument()); } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ", result.getError()); } }); List<Plant> plants = Arrays.asList( new Plant( new ObjectId(), "triffid", "low", "green", "perennial", "Store 47"), new Plant( new ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" )); mongoCollection.insertMany(plants).getAsync(task -> { if (task.isSuccess()) { int insertedCount = task.get().getInsertedIds().size(); Log.v("EXAMPLE", "successfully inserted " + insertedCount + " documents into the collection."); } else { Log.e("EXAMPLE", "failed to insert documents with: ", task.getError()); } });
val watcher = mongoCollection .watchWithFilterAsync(Document("fullDocument._partition", "Store 42")) watcher[{ result -> if (result.isSuccess) { Log.v("EXAMPLE", "Event type: ${result.get().operationType} full document: ${result.get().fullDocument}") } else { Log.e("EXAMPLE", "failed to subscribe to filtered changes in the collection with : ${result.error}") } }] val plants = listOf( Plant( ObjectId(), "triffid", "low", "green", "perennial", "Store 47" ), Plant( ObjectId(), "venomous tentacula", "low", "brown", "annual", "Store 42" ) ) mongoCollection.insertMany(plants).getAsync { task -> if (task.isSuccess) { val insertedCount = task.get().insertedIds.size Log.v("EXAMPLE", "successfully inserted $insertedCount documents into the collection.") } else { Log.e("EXAMPLE", "failed to insert documents with: ${task.error}") } }
Al ejecutar este fragmento se obtiene un resultado similar al siguiente:
V/EXAMPLE: successfully inserted 2 documents into the collection V/EXAMPLE: Event type: INSERT full document: Plant [id=5f6bb..., name=venomous tentacula, sunlight=low, color=brown, type=annual, partition=Store 42]
Documentos agregados en una colección
Las operaciones de agregación ejecutan todos los documentos de una colección a través de una serie de etapas de agregación de datos denominadas canalización de agregación. La agregación permite filtrar y transformar documentos, recopilar datos resumidos sobre grupos de documentos relacionados y realizar otras operaciones complejas de datos.
Puede ejecutar una canalización de agregación utilizando collection.aggregate().
Una operación de agregación acepta una lista de etapas de agregación como entrada y devuelve una Tarea que se resuelve en una colección de documentos procesados por la canalización.
Filtrar Documentos
Puede utilizar la etapa $match para filtrar documentos según un filtro de consulta de API de consulta.
{ "$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":
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$match", new Document("type", new Document("$eq", "perennial")))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$match", Document("type", Document("\$eq", "perennial") ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Documentos de grupo
Puede usar la etapa $group para agregar datos de resumen de uno o más documentos. MongoDB agrupa documentos según la expresión definida en el _id valor del campo de la $group etapa. Puede hacer referencia a un campo de documento específico anteponiendo al nombre del $ campo.
El siguiente fragmento agrupa todos los documentos de la colección plants por su valor type y agrega un recuento de la cantidad de cada tipo:
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$group", new Document("_id", "$type") .append("totalCount", new Document("$sum", 1)))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$group", Document("_id", "\$type") .append("totalCount", Document("\$sum", 1)) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Campos del documento del proyecto
Puede usar la etapa $project para incluir u omitir campos específicos de los documentos. Además, puede calcular nuevos campos mediante operadores de agregación. Las proyecciones funcionan de dos maneras:
Incluir explícitamente los campos con el valor 1. Esto tiene el efecto secundario de excluir implícitamente todos los campos no especificados.
Excluye implícitamente los campos con un valor de 0. Esto tiene el efecto secundario de incluir implícitamente todos los campos no especificados.
Estos dos métodos de proyección son mutuamente excluyentes: si incluye campos explícitamente, no puede excluirlos explícitamente, y viceversa.
Nota
El _id campo es un caso especial: siempre se incluye en todas las consultas, a menos que se especifique lo contrario. Por esta razón, se puede excluir el _id campo con un 0 valor e incluir simultáneamente otros campos,_partition como, con un 1 valor. Solo el caso especial de exclusión del _id campo permite tanto la exclusión como la inclusión en una sola $project etapa.
{ "$project": { "<Field Name>": <0 | 1 | Expression>, ... } }
Ejemplo
La siguiente etapa $project omite el campo _id, incluye el campo name y crea un nuevo campo llamado storeNumber. El storeNumber se genera mediante dos operadores de agregación:
$splitSepara el valor_partitionen dos segmentos de cadena que rodean el espacio. Por ejemplo, el valor "Store 42" dividido de esta manera devuelve una matriz con dos elementos: "Store" y "42".$arrayElemAtSelecciona un elemento específico de un array según el segundo argumento. En este caso, el valor1selecciona el segundo elemento del array generado por el operador$split, ya que los arrays indexan desde0. Por ejemplo, el valor ["Store", "42"] pasado a esta operación devolvería un valor de "42".
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$project", new Document("_id", 0) .append("name", 1) .append("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList("$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$project", Document("_id", 0) .append("name", 1) .append("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Agregar campos a los documentos
Puede utilizar la etapa $addFields para agregar nuevos campos con valores calculados utilizando operadores de agregación.
Nota
$addFields es similar a $project pero no permite incluir u omitir campos.
Ejemplo
La siguiente etapa $addFields crea un nuevo campo llamado storeNumber donde el valor es la salida de dos operadores agregados que transforman el valor del campo _partition.
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$addFields", new Document("storeNumber", new Document("$arrayElemAt", Arrays.asList( new Document("$split", Arrays.asList( "$_partition", " ")), 1))))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$addFields", Document("storeNumber", Document("\$arrayElemAt", listOf( Document("\$split", listOf( "\$_partition", " " ) ), 1 ) ) ) ) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Unwind Array Values
Puede usar la etapa $unwind para transformar un documento que contiene una matriz en varios documentos que contienen valores individuales de esa matriz. Al desenrollar un campo de matriz, MongoDB copia cada documento una vez por cada elemento de dicho campo, pero reemplaza el valor de la matriz con el elemento de la matriz en cada copia.
{ $unwind: { path: <Array Field Path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
Ejemplo
La siguiente etapa $unwind crea un nuevo documento para cada elemento de la matriz items en cada documento. También añade un campo llamado itemIndex a cada nuevo documento que especifica el índice de posición del elemento en la matriz original:
// create an aggregation pipeline List<Document> pipeline = Arrays.asList( new Document("$unwind", new Document("path", "$items") .append("includeArrayIndex", "itemIndex"))); // query mongodb using the pipeline RealmResultTask<MongoCursor<Document>> aggregationTask = mongoCollection.aggregate(pipeline).iterator(); // handle success or failure of the query aggregationTask.getAsync(task -> { if (task.isSuccess()) { MongoCursor<Document> results = task.get(); // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:"); while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()); } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ", task.getError()); } });
// create an aggregation pipeline val pipeline = listOf( Document("\$unwind", Document("path", "\$items") .append("includeArrayIndex", "itemIndex")) ) // query mongodb using the pipeline val aggregationTask = mongoCollection.aggregate(pipeline).iterator() // handle success or failure of the query aggregationTask.getAsync { task: App.Result<MongoCursor<Document>> -> if (task.isSuccess) { val results = task.get() // iterate over and print the results to the log Log.v("EXAMPLE", "successfully aggregated the plants. Results:") while (results.hasNext()) { Log.v("EXAMPLE", results.next().toString()) } } else { Log.e("EXAMPLE", "failed to aggregate documents with: ${task.error}") } }
Consideremos el siguiente documento de una colección de compras:
{ _id: 123, customerId: 24601, items: [ { name: "Baseball", quantity: 5 }, { name: "Baseball Mitt", quantity: 1 }, { name: "Baseball Bat", quantity: 1 }, ] }
Si aplicamos la etapa de ejemplo $unwind a este documento, la etapa genera los siguientes tres documentos:
{ _id: 123, customerId: 24601, itemIndex: 0, items: { name: "Baseball", quantity: 5 } }, { _id: 123, customerId: 24601, itemIndex: 1, items: { name: "Baseball Mitt", quantity: 1 } }, { _id: 123, customerId: 24601, itemIndex: 2, items: { name: "Baseball Bat", quantity: 1 } }