Daten mit Pipeline-Vorgängen ändern

Verwenden Sie die DML-Phasen (Datenbearbeitungssprache) update(...) und delete(...), um Datenpipelines zu erstellen, die Dokumente abfragen und dann Daten löschen oder ändern können.

Versionsanforderungen

Für die auf dieser Seite beschriebenen Vorgänge ist die Firestore Enterprise-Version erforderlich.

Hinweis

Sie sollten mit dem Abfragen einer Datenbank mit Pipelinevorgängen vertraut sein.

Dokumente aktualisieren

Mit der DML-Phase update(...) können Sie Datenpipelines erstellen, die Dokumente abfragen und dann Daten hinzufügen oder ändern können.

Alle DML-Phasen müssen am Ende der Pipeline stehen. Die Dokumente, die in diese Phase gelangen, müssen das Feld __name__ enthalten, um zu ermitteln, welche Dokumente aktualisiert werden sollen. Der Vorgang schlägt fehl, wenn eines der Dokumente, die Sie aktualisieren möchten, nicht vorhanden ist.

Mit dem folgenden Vorgang wird beispielsweise eine Änderung des Datenmodells für alle Dokumente in einer Sammlungsgruppe rückwirkend übernommen. Die Pipeline fügt allen Dokumenten in der Sammlungsgruppe users, in denen dieses Feld fehlt, ein preferences.color-Feld hinzu.

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)

Dokumente löschen

Verwenden Sie die DML-Phase delete(...), um Datenpipelines zu erstellen, die Dokumente abfragen und dann Daten löschen können. Um versehentliche Massenlöschungen zu verhindern, sollten Pipelines, die mit delete(...) enden, mindestens eine where(...)-Phase enthalten. Alle DML-Phasen müssen am Ende der Pipeline stehen.

Mit der folgenden Pipeline werden beispielsweise alle users-Dokumente gelöscht, bei denen address.users auf USA gesetzt ist und __create_time__ weniger als 10 Tage beträgt:

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)

Ausführungsmodi und Konsistenz

DML-Phasen können entweder innerhalb einer expliziten Transaktion oder außerhalb einer Transaktion ausgeführt werden:

Transaktionale Ausführung

Transaktionale DML bietet atomare Alles-oder-Nichts-Konsistenz für Dokumente und unterstützt alle Pipeline-Abfragephasen. Das macht sie ideal für Arbeitslasten, die strikte Konsistenzgarantien erfordern, bei denen also Teilaktualisierungen inakzeptabel sind, oder die auf komplexen Abfragephasen basieren, sofern die Änderungen innerhalb der Größenbeschränkungen für Transaktionen liegen.

Wenn Sie eine DML-Pipeline innerhalb einer expliziten Transaktion ausführen (mit runTransaction/transaction.execute(pipeline) oder der Option atomic: true), gilt Folgendes:

  • Alle Mutationen werden entweder zusammen festgeschrieben oder bei einem Fehler vollständig zurückgesetzt.
  • Alle Pipeline-Abfragephasen werden vor einer DML-Phase unterstützt (einschließlich where-, add_fields-, union-, find_nearest- und aggregate-Unterabfragen).
  • In einer Transaktion müssen alle Lesevorgänge vor allen Schreibvorgängen ausgeführt werden. Da eine DML-Pipeline Schreibvorgänge ausführt, können Sie nach dem Ausführen einer DML-Pipeline in derselben Transaktion keine Lesevorgänge ausführen. Sie können beispielsweise eine DML-Pipeline nicht mehrmals in einer einzelnen Transaktion aufrufen und nach dem Ausführen einer DML-Pipeline keine Dokumente lesen. Der Versuch, einen Lesevorgang nach der Ausführung einer DML-Pipeline auszuführen, schlägt mit dem Fehler FAILED_PRECONDITION fehl: Cannot have reads after the first write in a Firestore transaction.
  • Der Vorgang unterliegt den Transaktionslimits der Firestore Enterprise-Version.

Nicht-transaktionale Ausführung

Nicht-transaktionale DML ist für Bulk-Änderungen an einer großen Anzahl von Dokumenten konzipiert, ohne dass es zu Einschränkungen bei der Transaktionsgröße kommt. Durch das Sperren auf Dokumentebene und nicht für den gesamten Vorgang können andere gleichzeitige Vorgänge fortgesetzt werden, während Dokumente in großem Umfang aktualisiert werden.

Wenn DML-Phasen außerhalb einer expliziten Transaktion ausgeführt werden:

  • Unabhängige Aktualisierungen und Teilerfolg: Jedes Dokument wird unabhängig von den anderen aktualisiert und Vorgänge sind nicht dokumentübergreifend atomar. Die Pipeline wird beim ersten Fehler angehalten und zuvor geänderte Dokumente bleiben in der Datenbank gespeichert, was zu einem Teilerfolg führen kann.
  • Unterstützung für Phasen:
    • Erweiterte und mehrstufige Abfragen (z. B. aggregate(...), distinct(...), unnest(...), find_nearest(...), union(...), Joins und Unterabfragen) werden vor DML außerhalb einer Transaktion nicht unterstützt. Verwenden Sie eine Transaktion, wenn Ihre Pipeline diese Phasen erfordert.
  • Limit von 20 Suchvorgängen für Sicherheitsregeln: Jede Gruppe von Dokumenten wird in einer eigenen Transaktion ausgeführt und die Sicherheitsregeln werden neu bewertet. Wenn in Ihren Sicherheitsregeln get()- oder exists()-Lookups verwendet werden, wird für jeden Batch ein Lookup ausgeführt. Nach dem Schreiben von etwa 200 Dokumenten wird das Limit von 20 Lookups pro Anfrage erreicht und der Vorgang schlägt mit PERMISSION_DENIED fehl. Verwenden Sie benutzerdefinierte Anforderungen anstelle von get()- oder exists()-Suchvorgängen, um dieses Limit zu vermeiden.

Beschränkungen

Beachten Sie die folgenden Einschränkungen für DML-Phasen:

  • DML-Phasen müssen die letzte Phase in einer Pipeline-Definition sein, bevor .execute() aufgerufen wird.
  • Wenn sie außerhalb einer Transaktion ausgeführt werden, wird jedes Dokument unabhängig von den anderen aktualisiert und es kann zu Teilerfolgen kommen.
  • Nicht-transaktionale DML-Vorgänge, die Sicherheitsregeln mit get()- oder exists()-Suchvorgängen unterliegen, sind aufgrund des Limits von 20 Suchvorgängen auf etwa 200 Dokumente beschränkt.
  • Transaktionale DML-Vorgänge sind durch die Transaktionslimits der Firestore Enterprise-Version begrenzt. Da Firestore-Transaktionen nach dem ersten Schreibvorgang keine Lesevorgänge zulassen, können Sie eine DML-Pipeline nicht mehrmals innerhalb einer einzelnen Transaktion aufrufen und auch keine Dokumente nach dem Ausführen einer DML-Pipeline lesen. Der Versuch, einen Lesevorgang nach einem DML-Schreibvorgang auszuführen, schlägt mit dem Fehler FAILED_PRECONDITION fehl: Cannot have reads after the first write in a Firestore transaction.
  • Wenn die Phase vor der DML-Phase mehrere Dokumente mit derselben __name__ erzeugt oder dieselbe Zieldokument-ID ergibt, wird jede Mutation nacheinander verarbeitet:
    • Für upsert(...) und update(...): Bei mehreren Mutationen, die auf dasselbe Dokument abzielen, gilt die Semantik „Letzte gewinnt“, wobei die zuletzt verarbeitete Mutation den endgültigen Status des Dokuments bestimmt.
    • Für delete(...): Beim ersten Versuch wird das Dokument gelöscht. Nachfolgende Versuche für dasselbe Dokument sind No-Ops.
    • Für insert(...): Beim ersten Versuch wird das Dokument erstellt. Nachfolgende Versuche, die auf dieselbe Dokument-ID abzielen, schlagen mit einem ALREADY_EXISTS-Fehler fehl.