JSON-пути
Since version 2.3, Tarantool supports JSON path updates. You can update or upsert formatted tuple / space / index fields by name (not only by field number). Updates of nested structures are also supported.
Пример:
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 хоп.