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
/ /

Operaciones compuestas

En esta guía, aprenderás a realizar operaciones compuestas con el driver Java de MongoDB.

Las operaciones compuestas consisten en una operación de lectura y escritura que se ejecuta como una sola operación atómica. Una operación atómica es una operación que se completa por completo o no se completa en absoluto. Las operaciones atómicas no pueden completarse parcialmente.

Las operaciones atómicas pueden ayudarte a evitar condiciones de carrera en tu código. Una condición de carrera ocurre cuando el comportamiento del código depende del orden de eventos incontrolables.

MongoDB admite las siguientes operaciones compuestas:

  • Buscar y actualizar un documento

  • Buscar y reemplazar un documento

  • Buscar y eliminar un documento

Si necesitas realizar tareas más complejas de forma atómica, como leer y escribir en más de un documento, utiliza transacciones. Las transacciones son una funcionalidad de MongoDB y otras bases de datos que permite definir una secuencia arbitraria de comandos de base de datos como una operación atómica.

Para obtener más información sobre las operaciones atómicas y la atomicidad, consulte La entrada manual de MongoDB para atomicidad y transacciones.

Para obtener más información sobre las transacciones, consulta la entrada de la documentación de MongoDB sobre transacciones.

Esta sección muestra cómo utilizar cada operación compuesta con el controlador MongoDB Java.

Los siguientes ejemplos usan una colección que contiene estos dos documentos de muestra.

{"_id": 1, "food": "donut", "color": "green"}
{"_id": 2, "food": "pear", "color": "yellow"}

El código completo de los siguientes ejemplos está disponible en Github aquí.

Nota

¿Antes o después de guardar?

De forma predeterminada, cada operación compuesta devuelve el documento encontrado en el estado anterior a la operación de escritura. Puede recuperar el documento encontrado en el estado posterior a la operación de escritura utilizando la clase de opciones correspondiente a su operación compuesta. Puede ver un ejemplo de esta configuración en el ejemplo de Buscar y Reemplazar a continuación.

Para encontrar y actualizar un documento, usa el findOneAndUpdate() Método de la clase MongoCollection. El método findOneAndUpdate() devuelve el documento encontrado o null si no hay documentos que coincidan con la consulta.

El siguiente ejemplo utiliza el método findOneAndUpdate() para encontrar un documento con el campo color establecido en "green" y actualizar el campo food en ese documento a "pizza".

El ejemplo también utiliza una instancia de FindOneAndUpdateOptions para especificar las siguientes opciones:

  • Excluye el campo _id del documento encontrado con una proyección.

  • Especificar una inserción, que inserta el documento especificado por el filtro de query si ningún documento coincide con la query.

// <MongoCollection set up code here>
// Creates a projection to exclude the "_id" field from the retrieved documents
Bson projection = Projections.excludeId();
// Creates a filter to match documents with a "color" value of "green"
Bson filter = Filters.eq("color", "green");
// Creates an update document to set the value of "food" to "pizza"
Bson update = Updates.set("food", "pizza");
// Defines options that specify projected fields and permit upserts
FindOneAndUpdateOptions options = new FindOneAndUpdateOptions()
.projection(projection)
.upsert(true);
// Updates the first matching document with the content of the update document, applying the specified options
Document result = collection.findOneAndUpdate(filter, update, options);
// Prints the matched document in its state before the operation
System.out.println(result.toJson());

La salida del código anterior se parece a la siguiente:

{"food": "pizza", "color": "green"}

Para obtener más información sobre la clase Projections, consulte nuestra guía sobre el generador de proyecciones.

Para obtener más información sobre la operación upsert, consulte nuestra guía sobre upserts.

Para obtener más información sobre los métodos y clases mencionados en esta sección, consulte la siguiente documentación de la API:

Para buscar y reemplazar un documento, utilice el método findOneAndReplace() de la clase MongoCollection. El método findOneAndReplace() devuelve el documento encontrado o null si no hay documentos que coincidan con su consulta.

El siguiente ejemplo usa el método findOneAndReplace() para encontrar un documento con el campo color configurado en "green" y reemplazarlo con el siguiente documento:

{"music": "classical", "color": "green"}

En el ejemplo también se utiliza una instancia de FindOneAndReplaceOptions para indicar que el documento devuelto debe estar en el estado después de nuestra operación de reemplazo.

// <MongoCollection set up code here>
// Creates instructions to replace the matching document with a new document
Bson filter = Filters.eq("color", "green");
Document replace = new Document("music", "classical").append("color", "green");
// Defines options specifying that the operation should return a document in its post-operation state
FindOneAndReplaceOptions options = new FindOneAndReplaceOptions().
returnDocument(ReturnDocument.AFTER);
// Atomically finds and replaces the matching document and prints the replacement document
Document result = collection.findOneAndReplace(filter, replace, options);
System.out.println(result.toJson());

La salida del código anterior se parece a la siguiente:

{"_id": 1, "music": "classical", "color": "green"}

Para obtener más información sobre los métodos y clases mencionados en esta sección, consulte la siguiente documentación de la API:

Para buscar y eliminar un documento, utilice el método findOneAndDelete() de la clase MongoCollection. El método findOneAndDelete() devuelve el documento encontrado o null si no hay documentos que coincidan con su consulta.

El siguiente ejemplo utiliza el método findOneAndDelete() para buscar y eliminar el documento con el valor más grande en el campo _id.

El ejemplo usa una instancia FindOneAndDeleteOptions para especificar un orden descendente en el campo _id.

// <MongoCollection set up code here>
Bson sort = Sorts.descending("_id");
// Creates an empty filter to match all documents in the collection
Bson filter = Filters.empty();
// Defines options that specify a descending sort on the "_id" field
FindOneAndDeleteOptions options = new FindOneAndDeleteOptions().
sort(sort);
// Deletes the document containing the highest "_id" value and prints the deleted document
Document result = collection.findOneAndDelete(filter, options);
System.out.println(result.toJson());

La salida del código anterior se parece a la siguiente:

{"_id": 2, "food": "pear", "color": "yellow"}

Para obtener más información sobre la clase Sorts, consulta nuestra guía sobre el generador de clasificaciones.

Para obtener más información sobre los métodos y clases mencionados en esta sección, consulte la siguiente documentación de la API:

En esta sección exploramos dos ejemplos. El primer ejemplo contiene una condición de competencia, el segundo ejemplo utiliza una operación compuesta para evitar la condición de competencia presente en el primer ejemplo.

Para ambos ejemplos, imaginemos que gestionamos un hotel con una habitación y que tenemos un pequeño programa en Java para ayudarnos a gestionar el check-out de esta habitación a un cliente.

El siguiente documento en MongoDB representa la sala:

{"_id": 1, "guest": null, "room": "Blue Room", "reserved": false}

El código completo para este ejemplo está disponible en Github aquí.

Supongamos que nuestra aplicación usa este método bookARoom para registrar la habitación a un huésped:

public void bookARoom() {
// Creates a filter to match documents representing available rooms
Bson filter = Filters.eq("reserved", false);
// Retrieves a document that represents the first available room
Document myRoom = this.collection.find(filter).first();
// Prints a message if no documents representing available rooms are found
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
String myRoomName = myRoom.getString("room");
// Prints a message that guest that successfully booked the room
System.out.println("You got the " + myRoomName + " " + this.guest);
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter that matches the "_id" field of the first available room
Bson roomFilter = Filters.eq("_id", myRoom.get("_id", Integer.class));
// Updates the first matching document to mark it as reserved
this.collection.updateOne(roomFilter, update);
}

Imagina que dos huéspedes diferentes, Jan y Pat, intenten reservar la habitación con este método al mismo tiempo.

Jan ve este resultado:

You got the Blue Room Jan

Y Pat ve este resultado:

You got the Blue Room Pat

Cuando miramos nuestra base de datos, vemos lo siguiente:

{"_id": 1, "guest": "Jan", "room": "Blue Room", "reserved": true}

Pat no estará feliz. Cuando Pat llegue a nuestro hotel, Jan estará ocupando su habitación. ¿Qué salió mal?

Aquí está la secuencia de acontecimientos que ocurrió desde la perspectiva de nuestra instancia de MongoDB:

  • Encontrar y proporcionar una habitación vacía para Jan

  • Encuentra y devuelve una habitación vacía para Pat.

  • Actualizar la habitación a reservada para Pat

  • Actualizar la habitación a reservada para enero

Ten en cuenta que, por un breve momento, Pat reservó la sala, pero como la actualización de Jan fue la última en ejecutarse, nuestro documento tiene a "Jan" como invitado.

Usemos una operación compuesta para evitar la condición de competencia y siempre brindar a nuestros usuarios el mensaje correcto.

public void bookARoom(){
// Creates an update document to mark a room as reserved
Bson update = Updates.combine(Updates.set("reserved", true), Updates.set("guest", guest));
// Creates a filter to match a document representing an available room
Bson filter = Filters.eq("reserved", false);
// Updates the first document that matches the filter to mark it as reserved
Document myRoom = this.collection.findOneAndUpdate(filter, update);
// Prints a message when there are no available rooms
if (myRoom == null){
System.out.println("Sorry, we are booked " + this.guest);
return;
}
// Prints the name of the guest that successfully booked the room
String myRoomName = myRoom.getString("room");
System.out.println("You got the " + myRoomName + " " + this.guest);
}

Imagina que dos huéspedes diferentes, Jan y Pat, intenten reservar la habitación con este método al mismo tiempo.

Jan ve este resultado:

You got the Blue Room Jan

Y Pat ve este resultado:

Sorry, we are booked Pat

Cuando miramos nuestra base de datos, vemos lo siguiente:

{"_id":1, "guest":"Jan", "room":"Blue Room", "reserved":true}

Pat recibió el mensaje correcto. Aunque esté triste por no haber conseguido la reserva, al menos sabe que no debe viajar a nuestro hotel.

Aquí está la secuencia de acontecimientos que ocurrió desde la perspectiva de nuestra instancia de MongoDB:

  • Busca una sala libre para Jan y resérvala.

  • Intente encontrar una habitación libre para Pat y resérvela. Como no quedan habitaciones, devuelve null.

Importante

bloqueo de escritura

Tu instancia de MongoDB coloca un bloqueo de escritura en el documento que estás modificando durante la duración de tu operación compuesta.

Para obtener información sobre la clase Updates, consulta nuestra guía sobre el Constructor de Actualizaciones.

Para obtener más información sobre la Filters clase, consulte nuestra guía sobre el generador de filtros.

Para obtener más información sobre el método findOneAndUpdate(), consulte la documentación de la API de la clase MongoCollection.

Volver

Operaciones masivas

En esta página