Para agentes de IA: hay un índice de documentación disponible en https://www.mongodb.com/es/docs/llms.txt — versiones en markdown de todas las páginas están disponibles agregando .md a cualquier ruta URL.
Docs Menu

Operaciones compuestas

En esta guía, aprenderá a realizar operaciones compuestas con el controlador de Scala.

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

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

MongoDB admite las siguientes operaciones compuestas:

  • Buscar y actualizar un documento

  • Buscar y reemplazar un documento

  • Encuentra y elimina un documento

Para obtener más información sobre las operaciones atómicas y la atomicidad, consulte Atomicidad y transacciones en el manual del servidor MongoDB.

Tip

Transacciones

Si necesita realizar tareas más complejas de forma atómica, como leer y escribir en varios documentos, utilice transacciones.Las transacciones permiten ejecutar una secuencia de comandos de base de datos como una operación atómica. Para obtener más información sobre las transacciones, consulte la sección "Transacciones" en el manual del servidor MongoDB.

Esta sección muestra cómo utilizar el controlador de Scala para realizar operaciones compuestas.

Los siguientes ejemplos utilizan una colección que contiene dos documentos de muestra:

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

Los ejemplos utilizan la siguiente referencia collection para asignar cada documento a una instancia de la clase Document:

val database: MongoDatabase = mongoClient.getDatabase("compound_operations")
val collection: MongoCollection[Document] = database.getCollection("example")
val hotelCollection: MongoCollection[Document] = database.getCollection("rooms")

Cada operación compuesta devuelve un SingleObservable[Document] que emite el documento coincidente, o finaliza sin emitir ningún valor si ningún documento coincide con el filtro de consulta. Si el filtro de consulta coincide con varios documentos, el método actúa sobre la primera coincidencia, donde first se refiere al orden natural en el disco a menos que se especifique un orden de clasificación en el objeto de opciones correspondiente.

Nota

Devolver documentos después de la operación de escritura

Por defecto, una operación compuesta devuelve el documento coincidente y, a continuación, realiza la operación de escritura sobre él. Puede indicarle a la operación compuesta que realice primero la escritura pasando un objeto de opciones al método correspondiente. El ejemplo de Buscar y reemplazar muestra esta configuración.

Para buscar y actualizar un documento, utilice el método findOneAndUpdate() de la clase MongoCollection.

El siguiente ejemplo utiliza el método findOneAndUpdate() para encontrar un documento donde el campo color tenga el valor "green". Luego, actualiza el campo food en ese documento a "pizza".

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

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

  • Establezca un tiempo máximo de ejecución de 5 segundos para esta operación en la instancia de MongoDB. Si la operación tarda más, el método findOneAndUpdate() emite un MongoExecutionTimeoutException a través de la devolución de llamada onError() del observador.

val filter = equal("color", "green")
val update = set("food", "pizza")
val options = new FindOneAndUpdateOptions()
.upsert(true)
.maxTime(5, TimeUnit.SECONDS)
val observable: SingleObservable[Document] =
collection.findOneAndUpdate(filter, update, options)
observable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})
{"_id": {"$oid": "1"}, "color": "green", "food": "pizza"}
Completed

Para obtener más información sobre los métodos y las clases utilizados 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 siguiente ejemplo utiliza el método findOneAndReplace() para encontrar un documento donde el campo color tiene el valor "green". Reemplaza el documento coincidente con

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

El ejemplo también utiliza una instancia FindOneAndReplaceOptions para especificar que el documento devuelto debe estar en el estado después de la operación de reemplazo.

val replaceFilter = equal("color", "green")
val replacement = Document(
"music" -> "classical",
"color" -> "green"
)
val replaceOptions = new FindOneAndReplaceOptions()
.returnDocument(ReturnDocument.AFTER)
val replaceObservable: SingleObservable[Document] =
collection.findOneAndReplace(replaceFilter, replacement, replaceOptions)
replaceObservable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})
{"_id": {"$oid": "1"}, "music": "classical", "color": "green"}
Completed

Para obtener más información sobre los métodos y las clases utilizados 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 siguiente ejemplo utiliza el método findOneAndDelete() para encontrar y eliminar el documento con el valor más grande en su campo _id.

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

val deleteOptions = new FindOneAndDeleteOptions()
.sort(descending("_id"))
val deleteObservable: SingleObservable[Document] =
collection.findOneAndDelete(empty(), deleteOptions)
deleteObservable.subscribe(new Observer[Document] {
override def onNext(doc: Document): Unit = println(doc.toJson())
override def onError(e: Throwable): Unit = println(s"Failed: ${e.getMessage}")
override def onComplete(): Unit = println("Completed")
})
{"_id": {"$oid": "2"}, "food": "pear", "color": "yellow"}
Completed

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

Al realizar múltiples operaciones de forma atómica, las operaciones compuestas ayudan a evitar condiciones de carrera.

Para los ejemplos de esta sección, imagina que tienes un hotel con una sola habitación. Usas un pequeño programa en Scala para registrar a los huéspedes en la habitación.

El siguiente documento corresponde a la habitación del hotel:

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

El siguiente ejemplo utiliza la función bookARoomUnsafe() para registrar a un huésped en una habitación. La función realiza las operaciones de búsqueda y actualización por separado.

def bookARoomUnsafe(guestName: String): Unit = {
val availableFilter = equal("reserved", false)
val findResults = Await.result(
hotelCollection.find(availableFilter).first().toFuture(),
Duration(10, TimeUnit.SECONDS)
)
if (Option(findResults).isEmpty) {
println(s"Sorry, we are booked, $guestName")
return
}
val room = findResults
val roomName = room.getString("room")
println(s"You got the $roomName, $guestName")
val reserveUpdate = combine(
set("reserved", true),
set("guest", guestName)
)
val roomFilter = equal("_id", room.get("_id").get)
Await.result(
hotelCollection.updateOne(roomFilter, reserveUpdate).toFuture(),
Duration(10, TimeUnit.SECONDS)
)
}

Imagina que dos huéspedes, Jan y Pat, intentan reservar la misma habitación al mismo tiempo.

Jan ve este resultado:

You got the Blue Room, Jan

Pat ve este resultado:

You got the Blue Room, Pat

Al consultar la base de datos, verá el siguiente documento:

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

Tu aplicación les indicó a Pat y a Jan que habían reservado la habitación, pero la base de datos muestra una reserva solo para Jan. Esta es la secuencia de operaciones que realizó MongoDB:

  1. Encuentra y devuelve la habitación vacía a Jan.

  2. Encuentra y devuelve la habitación vacía a Pat.

  3. Actualizar la habitación con la reserva de Pat

  4. Actualizar la habitación con la reserva de Jan

Aunque Pat reservó la habitación por un momento, la actualización realizada por Jan sobrescribió la reserva de Pat porque se ejecutó en último lugar.

El siguiente ejemplo utiliza la función bookARoomSafe() para registrar a un huésped en una habitación. La función utiliza findOneAndUpdate() para realizar las operaciones de búsqueda y actualización como una única operación atómica.

def bookARoomSafe(guestName: String): Unit = {
val reserveUpdate = combine(
set("reserved", true),
set("guest", guestName)
)
val availableFilter = equal("reserved", false)
val room: Document = Await.result(
hotelCollection.findOneAndUpdate(availableFilter, reserveUpdate).toFuture(),
Duration(10, TimeUnit.SECONDS)
)
if (Option(room).isEmpty) {
println(s"Sorry, we are booked, $guestName")
return
}
val roomName = room.getString("room")
println(s"You got the $roomName, $guestName")
}

Imagina que dos huéspedes, Jan y Pat, intentan reservar la misma habitación al mismo tiempo.

Jan ve este resultado:

You got the Blue Room, Jan

Pat ve este resultado:

Sorry, we are booked, Pat

Al consultar la base de datos, verá el siguiente documento:

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

Jan reservó la habitación y Pat recibió el mensaje correcto de que no hay habitaciones disponibles. Esta es la secuencia de operaciones que realizó MongoDB:

  1. Encuentra una habitación libre para Jan y resérvala.

  2. Intenta encontrar y reservar una habitación vacía para Pat.

  3. No encuentre habitaciones vacías y complete sin emitir un valor.

Debido a que findOneAndUpdate() realiza la búsqueda y actualización de forma atómica, la operación de Pat no puede leer la habitación como disponible después de que finalice la reserva de Jan.

Importante

bloqueo de escritura

Tu instancia de MongoDB aplica un bloqueo de escritura al documento que estás modificando durante toda la duración de la operación compuesta.

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