UPSERT¶
Ключевое слово UPSERT
может быть использовано для проверки существования определенных документов, а также для их обновления/замены, если они существуют, или создания, если их не существует.
Каждая операция UPSERT
ограничена одной коллекцией, и имя коллекции не должно быть динамическим. Только один оператор UPSERT
для коллекции разрешен в одном запросе AQL, и за ним не могут следовать операции чтения или записи, которые обращаются к той же коллекции, операции обхода или функции AQL, которые могут читать документы.
Синтаксис¶
Синтаксис операций upsert
и repsert
следующий:
UPSERT searchExpression INSERT insertExpression UPDATE updateExpression IN collection
UPSERT searchExpression INSERT insertExpression REPLACE updateExpression IN collection
Оба варианта могут опционально заканчиваться предложением OPTIONS { ... }
.
При использовании варианта UPDATE
операции upsert
найденный документ будет частично обновлен, то есть будут обновлены или добавлены только атрибуты, указанные в updateExpression. При использовании REPLACE
варианта операции upsert
(repsert
), существующие документы будут заменены контекстами updateExpression.
Обновление документа изменит номер ревизии документа на генерируемое сервером значение. Системные атрибуты _id
, _key
и _rev
не могут быть обновлены, _from
и _to
могут.
Выражение searchExpression содержит документ, который нужно искать. Это должен быть литерал объекта без динамических имен атрибутов. Если в коллекции такой документ не найден, в коллекцию будет вставлен новый документ, указанный в insertExpression.
Если хотя бы один документ в коллекции соответствует searchExpression, он будет обновлен с помощью updateExpression. Если более одного документа в коллекции соответствует поисковомувыражению, не определено, какой из документов будет обновлен. Поэтому часто целесообразно убедиться другими способами (например, с помощью уникальных индексов, логики приложения и т.д.), что только один документ соответствует searchExpression.
Следующий запрос будет искать в коллекции users документ с определенным значением атрибута name. Если документ существует, то его атрибут logins будет увеличен на единицу. Если он не существует, будет вставлен новый документ, состоящий из атрибутов name, logins и dateCreated:
1 2 3 |
|
Обратите внимание, что в случае UPDATE
можно ссылаться на предыдущую версию документа, используя псевдо-значение OLD
.
Параметры запроса¶
ignoreErrors
¶
Опция ignoreErrors
может быть использована для подавления ошибок запроса, которые могут возникнуть при попытке нарушить ограничения уникального ключа.
keepNull
¶
При обновлении или замене атрибута с нулевым значением ArangoDB не будет удалять атрибут из документа, а сохранит для него нулевое значение. Чтобы избавиться от атрибутов в операции upsert, установите для них значение null
и предоставьте опцию keepNull
.
mergeObjects
¶
Опция mergeObjects
определяет, будет ли содержимое объекта объединено, если атрибут объекта присутствует как в запросе UPDATE
, так и в обновляемом документе.
Значение по умолчанию для mergeObjects
равно true
, поэтому нет необходимости указывать его явно.
waitForSync
¶
Для обеспечения долговечности данных при возврате запроса обновления существует опция запроса waitForSync
.
ignoreRevs
¶
Чтобы случайно не обновить документы, которые были написаны и обновлены с момента последнего получения, вы можете использовать опцию ignoreRevs
, чтобы позволить ArangoDB сравнивать значение _rev
и добиваться успеха, только если они совпадают, или позволить ArangoDB игнорировать их (по умолчанию):
1 2 3 4 5 |
|
Вам необходимо добавить значение _rev
в updateExpression. Оно не будет использоваться в searchExpression. Хуже того, если вы используете устаревшее значение _rev
в searchExpression, UPSERT
запустит путь INSERT
вместо пути UPDATE
, потому что он не нашел документ, точно соответствующий searchExpression.
exclusive
¶
Движок RocksDB не требует блокировок на уровне коллекции. Различные операции записи в одну и ту же коллекцию не блокируют друг друга, пока нет конфликтов запись-запись на одних и тех же документах. С точки зрения разработки приложений может быть желательным иметь исключительный доступ на запись в коллекции, чтобы упростить разработку. Обратите внимание, что записи не блокируют чтения в RocksDB. Исключительный доступ также может ускорить запросы на модификацию, так как мы избегаем проверки конфликтов.
Используйте опцию exclusive
для достижения этого эффекта на основе каждого запроса:
1 2 3 4 5 |
|
indexHint
¶
Опция indexHint
будет использоваться в качестве подсказки для поиска документа, выполняемого в рамках операции UPSERT
, и может помочь в таких случаях, как UPSERT
не выбирает лучший индекс автоматически.
1 2 3 4 |
|
Подсказка индекса передается во внутренний цикл FOR
, который используется для поиска. Также смотрите Опция indexHint
операции FOR
.
forceIndexHint
¶
Делает индекс или индексы, указанные в indexHint
, обязательными, если они включены. По умолчанию false
. Также смотрите Опция forceIndexHint
операции FOR
.
1 2 3 4 |
|
Возвращаемые документы¶
Операторы UPSERT
могут по желанию возвращать данные. Для этого за ними должен следовать оператор RETURN
(допускаются также промежуточные операторы LET
). Эти операторы могут по желанию выполнять вычисления и ссылаться на псевдо-значения OLD
и NEW
. В случае, если upsert выполнил операцию вставки, OLD
будет иметь значение null
. В случае если upsert выполнил операцию обновления или замены, OLD
будет содержать предыдущую версию документа, до обновления/замены.
NEW
всегда будет заполнен. Он будет содержать вставленный документ, если upsert выполнил вставку, или обновленный/замещенный документ, если он выполнил обновление/замену.
Это также можно использовать для проверки того, выполнил ли upsert вставку или обновление:
1 2 3 4 |
|
Транзакционность¶
На одном сервере апсерты выполняются транзакционно по принципу "все или ничего".
Если используется движок RocksDB и включены промежуточные фиксации, запрос может выполнять промежуточные фиксации транзакций в случае, если запущенная транзакция (AQL-запрос) достигнет заданных пороговых значений размера. В этом случае операции запроса, выполненные до сих пор, будут зафиксированы и не будут откатаны в случае последующего отмены/отката. Это поведение можно контролировать, изменяя настройки промежуточной фиксации для движка RocksDB.
Для коллекций с шардированием весь запрос и/или операция upsert могут не быть транзакционными, особенно если они затрагивают разные шарды и/или DB-серверы.
Ограничения¶
-
Части поиска и вставки/обновления/замены выполняются одна за другой, так что в промежутке могут выполняться другие операции в других потоках. Это означает, что если несколько запросов UPSERT выполняются одновременно, все они могут определить, что целевой документ не существует, а затем создать его несколько раз!
Обратите внимание, что из-за этого разрыва между поиском и вставкой/обновлением/заменой, даже при наличии уникального индекса могут возникать ошибки дублирования ключей или конфликты. Но если они возникнут, код приложения/клиента может повторно выполнить тот же самый запрос.
Чтобы этого не произошло, необходимо добавить уникальный индекс к атрибуту(ам) поиска. Обратите внимание, что в кластере уникальный индекс может быть создан только в том случае, если он равен атрибуту ключа шарда коллекции или, по крайней мере, содержит его как часть.
Альтернативой тому, чтобы заставить оператор UPSERT работать атомарно, является использование опции
exclusive
для ограничения параллелизма записи для этой коллекции до 1, что помогает избежать конфликтов, но плохо для throughput! -
При использовании очень больших транзакций в UPSERT (например, UPSERT над всеми документами в коллекции) может быть запущен промежуточный коммит. Этот промежуточный коммит запишет данные, которые были изменены на данный момент. Однако это будет иметь побочный эффект: атомарность этой операции больше не может быть гарантирована, и ArangoDB не может гарантировать, что чтение ваших собственных записей в upsert будет работать.
1 |
|
- Атрибут(ы) поиска из поискового выражения должен быть проиндексирован для улучшения производительности UPSERT. В идеале, поисковое выражение содержит ключ шарда, так как это позволяет ограничить поиск одним шардом.