Перейти к содержанию

REPLACE

Каждая операция REPLACE ограничена одной коллекцией, и имя коллекции не должно быть динамическим. В одном запросе AQL допускается только один оператор REPLACE для коллекции, и за ним не могут следовать операции чтения или записи, обращающиеся к той же коллекции, операции обхода или функции AQL, которые могут читать документы.

Нельзя заменить системные атрибуты _id, _key и _rev, но можно заменить атрибуты _from и _to.

Замена документа изменяет номер ревизии документа (атрибут _rev) на генерируемое сервером значение.

Синтаксис

Для операции замены существует два синтаксиса:

REPLACE document IN collection
REPLACE keyExpression WITH document IN collection

Оба варианта могут опционально заканчиваться предложением OPTIONS { ... }.

collection должно содержать имя коллекции, в которой должен быть заменен документ.

document должен быть объектом и содержать атрибуты и значения для установки. Все существующие атрибуты в хранимом документе удаляются из него и устанавливаются только предоставленные атрибуты (за исключением неизменяемых атрибутов _id и _key и управляемого системой атрибута _rev). Это отличает операцию REPLACE от операции UPDATE, которая влияет только на атрибуты, указанные в операции, и не изменяет другие атрибуты хранимого документа.

REPLACE <document> IN <collection>

При использовании первого синтаксиса объект document должен иметь атрибут _key с ключом документа. Существующий документ с этим ключом заменяется атрибутами, предоставленными объектом document (за исключением системных атрибутов _id, _key и _rev).

Следующий запрос заменяет документ, идентифицированный ключом my_key в коллекции users, устанавливая только атрибуты name и status. Ключ передается через атрибут _key наряду с другими атрибутами:

1
REPLACE { _key: "my_key", name: "Jon", status: "active" } IN users

Следующий запрос недействителен, поскольку объект не содержит атрибута _key, и поэтому невозможно определить заменяемый документ:

1
REPLACE { name: "Jon" } IN users

Вы можете объединить операцию REPLACE с циклом FOR для определения необходимых ключевых атрибутов, как показано ниже:

1
2
FOR u IN users
  REPLACE { _key: u._key, name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users

Обратите внимание, что операции REPLACE и FOR независимы друг от друга, и u не определяет автоматически документ для оператора REPLACE. Таким образом, следующий запрос является некорректным:

1
2
FOR u IN users
  REPLACE { name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users

REPLACE <keyExpression> WITH <document> IN <collection>

При использовании второго синтаксиса документ для замены определяется keyExpression. Это может быть либо строка с ключом документа, либо объект, содержащий атрибут _key с ключом документа, либо выражение, которое оценивается как одно из этих двух. Существующий документ с этим ключом заменяется атрибутами, предоставленными объектом document (за исключением системных атрибутов _id, _key и _rev).

Следующий запрос заменяет документ, идентифицированный ключом my_key в коллекции users, устанавливая только атрибуты name и status. Ключ передается в виде строки в keyExpression. Атрибуты для установки передаются отдельно в виде объекта document:

1
REPLACE "my_key" WITH { name: "Jon", status: "active" } IN users

Объект document может содержать атрибут _key, но он игнорируется.

Вы не можете определить документ для замены с помощью атрибута _id или передать идентификатор документа в виде строки (например, "users/john"). Однако вы можете использовать PARSE_IDENTIFIER(<id>).key в качестве keyExpression, чтобы получить ключ документа в виде строки:

1
2
LET key = PARSE_IDENTIFIER("users/john").key
REPLACE key WITH { ... } IN users

Сравнение синтаксисов

Оба синтаксиса операции REPLACE позволяют вам определить документ для изменения и атрибуты для установки. Документ для обновления эффективно идентифицируется ключом документа в сочетании с указанной коллекцией.

Операция REPLACE поддерживает различные способы указания ключа документа. Вы можете выбрать наиболее удобный для вас вариант синтаксиса.

Следующие запросы эквивалентны:

1
2
FOR u IN users
  REPLACE u WITH { name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users
1
2
FOR u IN users
  REPLACE u._key WITH { name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users
1
2
FOR u IN users
  REPLACE { _key: u._key } WITH { name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users
1
2
FOR u IN users
  REPLACE { _key: u._key, name: CONCAT(u.firstName, " ", u.lastName), status: u.status } IN users

Выражения динамических ключей

Операция REPLACE может заменять произвольные документы, используя любой из двух синтаксисов:

1
2
FOR i IN 1..1000
  REPLACE { _key: CONCAT("test", i), name: "Paula", status: "active" } IN users
1
2
FOR i IN 1..1000
  REPLACE CONCAT("test", i) WITH { name: "Paula", status: "active" } IN users

Нацелить на другую коллекцию

Документы, которые изменяет операция REPLACE, могут находиться в другой коллекции, чем те, которые были созданы предшествующей операцией FOR:

1
2
3
FOR u IN users
  FILTER u.active == false
  REPLACE u WITH { status: "inactive", name: u.name } IN backup

Обратите внимание, как документы считываются из коллекции users, но заменяются в другой коллекции backup. Для того чтобы это сработало, обе коллекции должны использовать совпадающие ключи документов.

Хотя переменная u содержит целый документ, она используется только для определения целевого документа. Атрибут _key объекта извлекается, и целевой документ определяется только значением строки ключа документа и указанной коллекцией операции REPLACE (backup). Ссылка на исходную коллекцию (users) отсутствует.

Параметры запроса

Вы можете опционально задать параметры запроса для операции REPLACE:

1
REPLACE ... IN users OPTIONS { ... }

ignoreErrors

Вы можете использовать ignoreErrors для подавления ошибок запроса, которые могут возникнуть при попытке заменить несуществующие документы или при нарушении ограничений уникального ключа:

1
2
3
4
FOR i IN 1..1000
  REPLACE CONCAT("test", i)
  WITH { foobar: true } IN users
  OPTIONS { ignoreErrors: true }

Вы не можете изменять системные атрибуты _id, _key и _rev, но попытки изменить их игнорируются и не считаются ошибками.

waitForSync

Для обеспечения долговечности данных при возврате запроса замены существует опция запроса waitForSync:

1
2
3
4
FOR i IN 1..1000
  REPLACE CONCAT("test", i)
  WITH { foobar: true } IN users
  OPTIONS { waitForSync: true }

ignoreRevs

Чтобы случайно не перезаписать документы, которые были изменены с момента последнего извлечения, вы можете использовать опцию ignoreRevs, чтобы либо позволить ArangoDB сравнивать значение _rev и добиваться успеха, только если они совпадают, либо позволить ArangoDB игнорировать их (по умолчанию):

1
2
3
4
FOR i IN 1..1000
  REPLACE { _key: CONCAT("test", i), _rev: "1287623" }
  WITH { foobar: true } IN users
  OPTIONS { ignoreRevs: false }

exclusive

Движок RocksDB не требует блокировок на уровне коллекции. Различные операции записи в одну и ту же коллекцию не блокируют друг друга, если нет конфликтов запись-запись на одних и тех же документах. С точки зрения разработки приложений может быть желательным иметь исключительный доступ на запись в коллекции, чтобы упростить разработку. Обратите внимание, что записи не блокируют чтения в RocksDB. Исключительный доступ также может ускорить запросы на модификацию, поскольку мы избегаем проверки конфликтов.

Используйте опцию exclusive для достижения этого эффекта на основе каждого запроса:

1
2
3
4
FOR doc IN collection
  REPLACE doc
  WITH { replaced: true } IN collection
  OPTIONS { exclusive: true }

refillIndexCaches

Нужно ли обновлять существующие записи в кэше границ в памяти, если документы границ заменяются.

1
2
REPLACE { _key: "123", _from: "vert/C", _to: "vert/D" } IN edgeColl
  OPTIONS { refillIndexCaches: true }

Возвращение измененных документов

При желании можно вернуть документы, измененные запросом. В этом случае за операцией REPLACE должна следовать операция RETURN. Допускаются также промежуточные операции LET. Эти операции могут ссылаться на псевдопеременные OLD и NEW. Псевдопеременная OLD ссылается на ревизии документа до замены, а NEW - на ревизии документа после замены.

И OLD, и NEW содержат все атрибуты документа, даже те, которые не указаны в выражении replace.

1
2
3
4
REPLACE document IN collection options RETURN OLD
REPLACE document IN collection options RETURN NEW
REPLACE keyExpression WITH document IN collection options RETURN OLD
REPLACE keyExpression WITH document IN collection options RETURN NEW

Ниже приведен пример с использованием переменной previous для возврата исходных документов до их модификации. Для каждого замененного документа возвращается ключ документа:

1
2
3
4
FOR u IN users
  REPLACE u WITH { value: "test" } IN users
  LET previous = OLD
  RETURN previous._key

Следующий запрос использует псевдо-значение NEW, чтобы вернуть замененные документы без некоторых их системных атрибутов:

1
2
3
4
FOR u IN users
  REPLACE u WITH { value: "test" } IN users
  LET replaced = NEW
  RETURN UNSET(replaced, "_key", "_id", "_rev")

Транзакционность

На одном сервере операции замены выполняются транзакционно по принципу "все или ничего".

Если используется движок RocksDB и включены промежуточные фиксации, запрос может выполнять промежуточные фиксации транзакций в случае, если запущенная транзакция (AQL-запрос) достигает заданных пороговых значений размера. В этом случае операции запроса, выполненные до этого момента, фиксируются и не откатываются в случае последующего отмены/отката. Это поведение можно контролировать, изменяя настройки промежуточной фиксации для движка RocksDB.

Для коллекций с шардированием вся операция запроса и/или замены может не быть транзакционной, особенно если она затрагивает разные шарды и/или DB-серверы.

Комментарии