Modifica datos con operaciones de canalización

Usa las etapas del lenguaje de manipulación de datos (DML) update(...) y delete(...) para construir canalizaciones de datos que puedan consultar documentos y, luego, borrar o modificar datos.

Requisitos de la edición

Las operaciones que se describen en esta página requieren la edición Enterprise de Firestore.

Antes de comenzar

Debes conocer cómo consultar una base de datos con operaciones de canalización.

Actualiza documentos

Usa la etapa de DML update(...) para construir canalizaciones de datos que puedan consultar documentos y, luego, agregar o modificar datos.

Todas las etapas de DML deben estar al final de la canalización. Los documentos que ingresan a esta etapa deben incluir el campo __name__ para identificar qué documentos se deben actualizar. La operación fallará si alguno de los documentos que intentas actualizar no existe.

Por ejemplo, la siguiente operación reabastece un cambio en el modelo de datos para todos los documentos de un grupo de colecciones. La canalización agrega un campo preferences.color a todos los documentos del grupo de colecciones users que no tienen ese campo.

Node.js
const snapshot = await db.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant(null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Constant, Field, Not

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Not(Field.of("preferences.color").exists()))
    .add_fields(Constant.of(None).as_("preferences.color"))
    .remove_fields("color")
    .update()
    .execute()
)
Java
Pipeline.Snapshot snapshot = firestore.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant((String) null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute().get();
Go
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.Not(firestore.FieldExists(firestore.FieldOf("preferences.color")))).
	AddFields(firestore.Selectables(
		firestore.ConstantOfNull().As("preferences.color"),
	)).
	RemoveFields(firestore.Fields("color")).
	Update().
	Execute(ctx)

Borra documentos

Usa la etapa de DML delete(...) para construir canalizaciones de datos que puedan consultar documentos y, luego, borrar datos. Para evitar eliminaciones masivas accidentales, las canalizaciones que terminan en delete(...) deben incluir al menos una etapa where(...). Todas las etapas de DML deben estar al final de la canalización.

Por ejemplo, la siguiente canalización borra todos los documentos users con address.users establecido en USA y con __create_time__ de menos de 10 días:

Node.js
const pipeline = db.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").timestampAdd("day", 10).lessThan(currentTimestamp()))
  .delete();
await pipeline.execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import CurrentTimestamp, Field

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Field.of("address.country").equal("USA"))
    .where(
        Field.of("__create_time__")
        .timestamp_add("day", 10)
        .less_than(CurrentTimestamp())
    )
    .delete()
    .execute()
)
Java
Pipeline.Snapshot deleteResults = firestore.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").add(constant(10)).lessThan(currentTimestamp()))
  .delete()
  .execute().get();
Go
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.FieldOf("address.country").Equal("USA")).
	Where(firestore.FieldOf("__create_time__").Add(firestore.ConstantOf(10)).LessThan(firestore.CurrentTimestamp())).
	Delete().
	Execute(ctx)

Modos de ejecución y coherencia

Las etapas de DML se pueden ejecutar dentro de una transacción explícita o fuera de una transacción:

Ejecución transaccional

El DML transaccional proporciona coherencia atómica de todo o nada en todos los documentos y admite el conjunto completo de etapas de consulta de canalización. Esto la hace ideal para las cargas de trabajo que requieren garantías de coherencia estrictas, en las que las actualizaciones parciales son inaceptables, o que dependen de etapas de consulta complejas, siempre que las modificaciones se ajusten a los límites de tamaño de las transacciones.

La ejecución de una canalización de DML dentro de una transacción explícita (con runTransaction/transaction.execute(pipeline) o la opción atomic: true) tiene el siguiente comportamiento:

  • Todas las mutaciones se confirman juntas o se revierten por completo en caso de falla.
  • Todas las etapas de consulta de canalización se admiten antes de una etapa de DML (incluidas las subconsultas where, add_fields, union, find_nearest y aggregate).
  • En una transacción, todas las operaciones de lectura deben ejecutarse antes de cualquier operación de escritura. Debido a que una canalización de DML realiza operaciones de escritura, no puedes ejecutar lecturas después de ejecutar una canalización de DML en la misma transacción. Por ejemplo, no puedes llamar a una canalización de DML varias veces en una sola transacción ni leer documentos después de ejecutar una canalización de DML. Si intentas ejecutar una lectura después de que se ejecute una canalización de DML, se producirá un error FAILED_PRECONDITION: Cannot have reads after the first write in a Firestore transaction.
  • La operación está sujeta a los límites de transacciones de la edición Enterprise de Firestore.

Ejecución no transaccional

El DML no transaccional está diseñado para realizar modificaciones masivas en una gran cantidad de documentos sin que se apliquen las limitaciones de tamaño de las transacciones. Cuando se bloquea a nivel del documento, en lugar de en toda la operación, se permite que otras operaciones simultáneas continúen mientras se actualizan los documentos a gran escala.

Cuando las etapas de DML se ejecutan fuera de una transacción explícita, ocurre lo siguiente:

  • Actualizaciones independientes y éxito parcial: Cada documento se actualiza de forma independiente, y las operaciones no son atómicas en todos los documentos. La canalización se detiene en el primer error, y los documentos modificados con anterioridad permanecen escritos en la base de datos, lo que permite que se produzca un éxito parcial.
  • Asistencia para etapas:
    • Las etapas avanzadas y de varias consultas (como aggregate(...), distinct(...), unnest(...), find_nearest(...), union(...), uniones y subconsultas) no se admiten antes de DML fuera de una transacción. Usa una transacción si tu canalización requiere estas etapas.
  • Límite de 20 búsquedas de las reglas de seguridad: Cada lote de documentos se ejecuta en su propia transacción y vuelve a evaluar las reglas de seguridad. Si tus reglas de seguridad usan búsquedas de get() o exists(), cada lote ejecuta una búsqueda, alcanza el límite de 20 búsquedas por solicitud después de escribir aproximadamente 200 documentos y falla con PERMISSION_DENIED. Usa reclamaciones personalizadas en lugar de búsquedas de get() o exists() para evitar este límite.

Limitaciones

Ten en cuenta las siguientes limitaciones para las etapas de DML:

  • Las etapas de DML deben ser la última etapa en una definición de canalización antes de llamar a .execute().
  • Cuando se ejecuta fuera de una transacción, cada documento se actualiza de forma independiente, lo que permite que se produzca un éxito parcial.
  • Las operaciones de DML no transaccionales sujetas a reglas de seguridad con búsquedas de get() o exists() tienen un límite de aproximadamente 200 documentos debido al límite de 20 búsquedas.
  • Las operaciones de DML transaccionales están limitadas por los límites de transacciones de la edición Enterprise de Firestore. Debido a que las transacciones de Firestore no permiten lecturas después de la primera escritura, no puedes llamar a una canalización de DML varias veces dentro de una sola transacción, ni puedes leer documentos después de ejecutar una canalización de DML. Si intentas ejecutar una lectura después de una escritura de DML, se produce un error FAILED_PRECONDITION: Cannot have reads after the first write in a Firestore transaction.
  • Si la etapa que precede a la etapa de DML produce varios documentos con el mismo __name__ o se evalúa con el mismo ID de documento de destino, cada mutación se procesa de forma secuencial:
    • Para upsert(...) y update(...): Las mutaciones múltiples que se orientan al mismo documento siguen la semántica de "gana la última", en la que la última mutación procesada determina el estado final del documento.
    • Para delete(...): El primer intento borra el documento, y los intentos posteriores para el mismo documento no realizan ninguna operación.
    • Para insert(...): El primer intento crea el documento, y los intentos posteriores que se dirigen al mismo ID de documento fallan con un error ALREADY_EXISTS.