Эффективный поиск по всему вложенному JSONB 1 уровня в Postgres

Допустим, нам нужно проверить, содержит ли столбец jsonb конкретное значение, совпадающее с подстрокой в ​​любом значении (не вложенное, только первый уровень).

Как эффективно оптимизировать запрос для поиска по всему JSONB столбец (это означает каждый ключ) для значения?

Есть ли хорошая альтернатива ILIKE %val% на jsonb тип данных приведен к тексту?

jsonb_each_text(jsonb_column) ILIKE '%val%'

В качестве примера рассмотрим эти данные:

SELECT 
  '{
   "col1": "somevalue", 
   "col2": 5.5, 
   "col3": 2016-01-01, 
   "col4": "othervalue", 
   "col5": "yet_another_value"
  }'::JSONB

Как бы вы пошли на оптимизацию запроса, когда нужно искать шаблон? %val% в записях, содержащих разные ключи конфигурации для разных строк в столбце jsonb?

Я знаю, что поиск с предшествующим и последующим % знак неэффективен, поэтому ищет лучший путь, но с трудом находит его. Кроме того, индексирование всех полей в столбце json явным образом не вариант, поскольку они различаются для каждого типа записи и создают огромный набор индексов (не каждая строка имеет одинаковый набор ключей).

Вопрос

Есть ли лучшая альтернатива извлечению каждой пары ключ-значение в текст и выполнению поиска ILIKE/POSIX?

1 ответ

Если вы знаете, что вам нужно будет запросить только несколько известных ключей, то вы можете просто проиндексировать эти выражения.

Это слишком простой, но понятный пример:

create table foo as SELECT '{"col1": "somevalue", "col2": 5.5, "col3": "2016-01-01", "col4": "othervalue", "col5": "yet_another_value"}'::JSONB as bar;

create index pickfoo1 on foo ((bar #>> '{col1}'));
create index pickfoo2 on foo ((bar #>> '{col2}'));

Это основная идея, даже если она бесполезна для запросов, но вы можете делать больше вещей (в зависимости от ваших потребностей).

Например: если вам нужно только сопоставление без учета регистра, этого будет достаточно:

-- Create index over lowered value:
create index pickfoo1 on foo (lower(bar #>> '{col1}'));
create index pickfoo2 on foo (lower(bar #>> '{col2}'));

-- Check that it matches:
select * from foo where lower(bar #>> '{col1}') = lower('soMEvaLUe');

ПРИМЕЧАНИЕ. Это только пример: если вы выполните объяснение по сравнению с предыдущим выбором, вы увидите, что postgres фактически выполняет последовательное сканирование вместо использования индекса. Но это потому, что мы тестируем таблицу с одной строкой, что не является обычным. Но я уверен, что вы могли бы проверить это с большим столом;-)

Несмотря на огромные таблицы, даже подобные запросы должны выигрывать от индекса, если первая карта не появляется в начале строки (но это не вопрос jsonb, а вопрос самих индексов btree).

Если вам нужно оптимизировать запросы, такие как:

select * from foo where bar #>> '{col1}' ilike '%MEvaL%';

... тогда вам следует рассмотреть возможность использования индексов GIN или GIST.

Другие вопросы по тегам