Поиск по содержимому JSON-документов
Поиск по JSON — это способ находить строки таблицы по содержимому JSON-документа, хранящегося в колонке типа Json или JsonDocument: по существованию пути в документе и по значению, расположенному в указанном пути. Путь задаётся выражением JsonPath, а проверки выполняются уже существующими функциями JSON_EXISTS и JSON_VALUE. Типичные сценарии:
- поиск документов с определённым вложенным полем;
- поиск документов, у которых поле по заданному пути равно нужному значению;
- фильтрация полуструктурированных данных без заранее зафиксированной схемы.
В YDB поиск по JSON можно выполнять двумя основными способами:
- без индекса — сканированием таблицы и применением функций
JSON_EXISTS/JSON_VALUEк каждой строке. Подход прост, но плохо масштабируется: объём работы по сканированию растёт вместе с размером таблицы. - с JSON-индексом — созданием JSON-индекса по JSON-колонке. Этот подход предназначен для масштабируемого поиска.
Поиск по JSON с JSON-индексом
Ключевая идея JSON-индекса — построение инвертированного индекса по путям и парам «путь + значение». Каждый путь JSON-документа (а для проверок равенства — и скалярное значение по этому пути) кодируется в токен. Для каждого токена в индексе хранится список значений первичного ключа соответствующих строк таблицы. Запросы к индексу сводятся к инвертированному поиску по этим токенам — по той же схеме, что и у полнотекстового индекса, но с собственным токенизатором JSON.
JSON-индекс не хранит копию JSON-документа целиком, а раскладывает его на отдельные токены:
- для каждого пути в JSON-дереве создаётся токен «путь существует»;
- если путь ведёт к скалярному значению (строке, числу, логическому значению или
null), дополнительно создаётся токен «путь + значение».
Массивы при этом прозрачны: элементы массива индексируются под тем же путём, что и сам массив, поэтому индекс одинаково отвечает на запросы к $.items, $.items[0] и $.items[*].
Например, для документа:
{
"id": 42042,
"name": "Michael",
"email": null,
"items": [null, "str"],
"parts": {
"key": "k1",
"value": false
}
}
концептуально индексируются следующие токены (без учёта специфики внутреннего представления):
| Токен | Что позволяет найти |
|---|---|
$ |
документ не равен NULL |
$.id / $.id == 42042 |
путь id существует / равен 42042 |
$.name / $.name == "Michael" |
путь name существует / равен "Michael" |
$.email / $.email == null |
путь email существует / равен null |
$.items / $.items == null / $.items == "str" |
путь items существует / содержит элемент null / содержит элемент "str" |
$.parts |
путь parts существует |
$.parts.key / $.parts.key == "k1" |
вложенный путь существует / равен "k1" |
$.parts.value / $.parts.value == false |
вложенный путь существует / равен false |
JSON-индекс позволяет:
- находить строки по существованию пути через JSON_EXISTS;
- находить строки по значению в пути через JSON_VALUE или фильтр-предикат внутри
JSON_EXISTS.
При выполнении запроса JSON-индекс может быть автоматически задействован оптимизатором. Достаточно написать обычное условие WHERE с выражениями JSON_EXISTS / JSON_VALUE по индексированной JSON-колонке — оптимизатор распознает такой предикат и применит чтение по JSON-индексу вместо перебора записей исходной таблицы. Кроме того, JSON-индекс, как и любой другой вторичный индекс, можно принудительно задействовать, указав его имя в секции VIEW IndexName.
Если предикат не удаётся превратить в обращение к индексу, поведение YDB зависит от того, был ли в запросе явно указан требуемый JSON-индекс:
- при автоматическом выборе индекса оптимизатором запрос просто выполняется без ускорения индексом (результат остаётся корректным);
- при явном указании индекса через выражение
VIEWвозвращается ошибка.
Подробнее см. VIEW (JSON-индекс).
Дополнительная информация:
- JSON-индексы — обзор возможностей, поддерживаемых предикатов и ограничений.
- Функции для работы с JSON — справка по
JSON_EXISTS,JSON_VALUEи языку JsonPath. - VIEW (JSON-индекс) — синтаксис запросов с JSON-индексом.