JSON-пути
Начиная с версии 2.3, в Tarantool поддерживается обновление по JSON-путям. Обновлять и выполнять upsert полей форматированных кортежей / пространств / индексов можно по имени (а не только по номеру поля). Также поддерживается обновление вложенных структур.
Пример:
tarantool> box.cfg{};> format = {};> format[1] = {'field1', 'unsigned'};> format[2] = {'field2', 'map'};> format[3] = {'field3', 'array'};> format[4] = {'field4', 'string', is_nullable = true}---...tarantool> s = box.schema.create_space('test', {format = format});> _ = s:create_index('pk')---...tarantool> t = {> 1,> {> key1 = 'value',> key2 = 10> },> {> 2,> 3,> {key3 = 20}> }> }---...tarantool> t = s:replace(t)---...tarantool> t:update({{'=', 'field2.key1', 'new_value'}})---- [1, {'key1': 'new_value', 'key2': 10}, [2, 3, {'key3': 20}]]...tarantool> t:update({{'+', 'field3[2]', 1}})---- [1, {'key1': 'value', 'key2': 10}, [2, 4, {'key3': 20}]]...tarantool> s:update({1}, {{'!', 'field4', 'inserted value'}})---- [1, {'key1': 'value', 'key2': 10}, [2, 3, {'key3': 20}], 'inserted value']...tarantool> s:update({1}, {{'#', '[2].key2', 1}, {'=', '[3][3].key4', 'value4'}})---- [1, {'key1': 'value'}, [2, 3, {'key3': 20, 'key4': 'value4'}], 'inserted value']...tarantool> s:upsert({1, {k = 'v'}, {}}, {{'#', '[2].key1', 1}})---...tarantool> s:select{}---- - [1, {}, [2, 3, {'key3': 20, 'key4': 'value4'}], 'inserted value']...
Обратите внимание, что имена полей, которые выглядят как JSON-пути, обрабатываются аналогично доступу к полям кортежа через JSON: сначала весь путь интерпретируется как имя поля; если такого имени не существует, то оно обрабатывается как путь.
Например, для имени поля field.name.like.json это обновление
{object-name}:update({..., 'field.name.like.json', ...})
обновит именно это поле целиком, а не ключи field -> name ->
like -> json. Если это имя нужно вам как часть большего пути, то
его нужно обернуть в кавычки "" или квадратные скобки []:
{object-name}:update({..., '["field.name.like.json"].next.fields', ...})
Есть несколько правил для обновления через JSON:
-
Операция
'!'не может использоваться для создания всех промежуточных узлов пути. Например,{'!', 'field1[1].field3', ...}не может создать поля'field1'и'[1]'— они должны существовать. -
Операция
'#'при применении к ассоциативным массивам не может удалить более одного ключа за раз. То есть её аргумент всегда должен быть равен 1 для ассоциативных массивов.{'#', 'field1.field2', 1}– разрешено;{'#', 'field1.field2', 10}– не разрешено.Это ограничение возникает из-за проблемы, что ключи на ассоциативном массиве все равно не упорядочены, а
'#'с более чем 1 ключом приведет к неопределенному поведению. -
Операция
'!'для ассоциативных массивов не может создать ключ, если он уже существует. -
Если ассоциативный массив содержит нестроковые ключи (логические значения, числа, ассоциативные массивы, массивы — что угодно), то такие ключи нельзя обновить через JSON-пути. Однако обновлять строковые ключи в таком массиве по-прежнему разрешено.
Почему обновления с помощью JSON-путей хороши, и их следует предпочитать, когда нужно обновить только часть кортежа:
- Они занимают меньше места в WAL, так как при обновлении сохраняются только ключи, операции и аргументы. Хранить обновление одного глубоко вложенного поля выгоднее, чем всего кортежа.
- Они работают быстрее. Во-первых, потому что реализованы на C и не имеют проблем с Lua GC и динамической типизацией. Во-вторых, некоторые случаи работы с JSON-путями сильно оптимизированы. Например, обновление по одному JSON-пути требует O(1) памяти независимо от глубины пути (не считая аргументов обновления).
- Они доступны удалённым клиентам, как и любые другие DML-операции. До появления обновлений по JSON-путям в Tarantool для обновления глубоко вложенной части кортежа было необходимо получить кортеж, обновить его в памяти и отправить обратно — 2 сетевых перехода. С JSON-путями это может быть 1 переход, если обновление можно описать в виде пути.