Используйте этапы языка управления данными (DML) update(...) и delete(...), чтобы создавать конвейеры данных, которые могут запрашивать документы, а затем удалять или изменять данные.
Требования к версии
Для выполнения операций, описанных на этой странице, требуется версия Firestore Enterprise.
Подготовка
Вы должны уметь запрашивать данные из базы данных с помощью операций конвейера.
Обновить документы
Используйте этап DML update(...), чтобы создавать конвейеры данных, которые могут запрашивать документы, а затем добавлять или изменять данные.
Все этапы DML должны находиться в конце конвейера.
Документы, поступающие на этот этап, должны содержать поле __name__, чтобы определить, какие документы нужно обновить. Если хотя бы один из документов, которые вы пытаетесь обновить, не существует, операция завершится ошибкой.
Например, следующая операция заполняет изменение модели данных для всех документов в группе коллекций. Конвейер добавляет поле preferences.color во все документы группы коллекций users, в которых оно отсутствует.
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();
Проложить маршрут
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)
Удалить документы
Используйте этап DML delete(...), чтобы создать конвейеры данных, которые могут запрашивать документы, а затем удалять данные.
Чтобы предотвратить случайное массовое удаление, конвейеры, заканчивающиеся на delete(...), должны включать хотя бы один этап where(...).
Все этапы DML должны находиться в конце конвейера.
Например, следующий конвейер удаляет все документы users, у которых для поля address.users задано значение USA, а для поля __create_time__ – менее 10 дней:
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();
Проложить маршрут
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)
Режимы выполнения и согласованность
Этапы DML могут выполняться как внутри явной транзакции, так и вне ее:
Транзакционное выполнение
Транзакционный DML обеспечивает атомарную согласованность по принципу "все или ничего" для всех документов и поддерживает полный набор этапов конвейера запросов. Это делает его идеальным для рабочих нагрузок, требующих строгих гарантий согласованности, когда частичные обновления неприемлемы, или зависящих от сложных этапов запроса, при условии, что изменения укладываются в ограничения на размер транзакции.
При запуске конвейера DML в явной транзакции (с использованием runTransaction/transaction.execute(pipeline) или параметра atomic: true) происходит следующее:
- Все изменения либо фиксируются вместе, либо полностью откатываются в случае сбоя.
- Перед этапом DML поддерживаются все этапы запроса конвейера (включая подзапросы
where,add_fields,union,find_nearestиaggregate). - В транзакции все операции чтения должны выполняться до операций записи. Поскольку конвейер DML выполняет запись, вы не можете выполнять чтение после запуска конвейера DML в той же транзакции. Например, вы не можете вызвать конвейер DML несколько раз в одной транзакции, а также не можете читать документы после выполнения конвейера DML. Попытка выполнить чтение после запуска конвейера DML завершается ошибкой
FAILED_PRECONDITION:Cannot have reads after the first write in a Firestore transaction. - На операцию распространяются ограничения на транзакции Firestore Enterprise.
Выполнение без транзакций
Нетранзакционный DML предназначен для массовых изменений в большом количестве документов без ограничений на размер транзакции. Блокировка на уровне документа, а не всей операции позволяет выполнять другие параллельные операции при масштабном обновлении документов.
Если этапы DML выполняются вне явной транзакции:
- Независимые обновления и частичный успех. Каждый документ обновляется независимо от других, и операции не являются атомарными для всех документов. Конвейер останавливается при первой ошибке, а ранее измененные документы остаются записанными в базу данных, что позволяет добиться частичного успеха.
- Поддержка этапов:
- Расширенные этапы и этапы с несколькими запросами (например,
aggregate(...),distinct(...),unnest(...),find_nearest(...),union(...), объединения и подзапросы) не поддерживаются перед DML вне транзакции. Если в вашей воронке есть эти этапы, используйте транзакцию.
- Расширенные этапы и этапы с несколькими запросами (например,
- Ограничение на 20 поисков в правилах безопасности. Каждый пакет документов выполняется в рамках собственной транзакции и повторно оценивает правила безопасности. Если в правилах безопасности используются запросы
get()илиexists(), каждый пакет выполняет запрос, достигая лимита в 20 запросов на запрос после записи примерно 200 документов и завершаясь с ошибкойPERMISSION_DENIED. Чтобы избежать этого ограничения, используйте специальные утверждения вместо поиска поget()илиexists().
Ограничения
Для этапов DML действуют следующие ограничения:
- Этапы DML должны быть последними в определении конвейера перед вызовом
.execute(). - Если запустить его вне транзакции, каждый документ будет обновляться независимо от других, что может привести к частичному успеху.
- Операции DML, не связанные с транзакциями и регулируемые правилами безопасности с поиском
get()илиexists(), ограничены примерно 200 документами из-за лимита в 20 поисков. - Транзакционные операции DML ограничены лимитами транзакций Firestore Enterprise. Поскольку транзакции Firestore не допускают чтение после первой записи, вы не можете вызывать конвейер DML несколько раз в рамках одной транзакции, а также не можете читать документы после выполнения конвейера DML.
Попытка выполнить чтение после сбоя записи DML с ошибкой
FAILED_PRECONDITION:Cannot have reads after the first write in a Firestore transaction. - Если на этапе, предшествующем этапу DML, создается несколько документов с одинаковым значением
__name__или вычисляется один и тот же идентификатор целевого документа, каждое изменение обрабатывается последовательно:- Для
upsert(...)иupdate(...): если несколько изменений нацелены на один и тот же документ, применяется правило "последний побеждает", то есть последнее обработанное изменение определяет окончательное состояние документа. - Для
delete(...): при первой попытке документ удаляется, а последующие попытки для того же документа не выполняются. - Для
insert(...): при первой попытке создается документ, а последующие попытки, нацеленные на тот же идентификатор документа, завершаются ошибкойALREADY_EXISTS.
- Для