Составной запрос массива JSONB в Postgres? - PullRequest
1 голос
/ 06 июня 2019

Таблица: test, столбец JSONB: content:

create table test (id bigserial primary key, content jsonb);

content содержит список списков фиксированной длины:

insert into test values (1, '[["first 1", "second 3"]]');
insert into test values (2, '[["first 1", "second 2"], ["first 2", "second 3"]]');
insert into test values (3, '[["first 1", "second 2"], ["first 1", "second 3"]]');
insert into test values (4, '[["first 2", "second 3"], ["first 1", "second 2"], ["first 1", "second 2"]]');

Что такое правильный Postgresсинтаксис для запроса, который возвращает все строки, где хотя бы один из элементов content удовлетворяет (first element = "first 1") AND (second element ILIKE "%3%")?

То есть в приведенном выше примере следует выбратьстроки 1 и 3, но не 2 или 4.

Бонусный вопрос : каков самый эффективный способ выполнить такой запрос (если существует несколько альтернатив)?Имеет ли смысл заглянуть в GIN через JSONB с pg_trgm?(Есть миллионы строк, значения внутренней строки обычно имеют длину 10-100 символов, и каждый список content содержит 0-1000 списков (чаще всего 0).)

Спасибо!

Ответы [ 2 ]

3 голосов
/ 06 июня 2019

Вы должны разделить массивы верхнего уровня и проверить элементы оттуда:

select distinct id, content
FROM test
JOIN lateral (
    select elems 
    FROM jsonb_array_elements(content) jae(elems)
) all_arrays ON TRUE
WHERE elems ->> 0 = 'first 1'
and elems ->> 1 ilike '%3%'
ORDER BY 1;

Что касается лучшего способа сделать это, это во многом зависит от ваших фактических данных - сколько строк, насколько велики эти структуры jsonb и т. Д. В целом, однако, такой поиск, как ilike "% 3%", будет полезен из индексов, основанных на pg_trgm, потому что они не могут использовать традиционные индексы btree.

Редактировать: запрос @ Abelisto в комментариях лучше, потому что он должен быть более производительным, особенно если контент может содержать тысячи элементов:

select * from test 
where exists 
  (select 1 
   from jsonb_array_elements(content) jae(elems) 
   where elems ->> 0 = 'first 1' 
   and elems ->> 1 ilike '%3%'
  );
1 голос
/ 06 июня 2019

Внутренний выбор расширяет элементы массива в отдельные строки с помощью jsonb_array_elements, внешний выбор выполняет необходимую фильтрацию.См. SQL Fiddle для живого примера.

select * from (
select id, jsonb_array_elements(content) as item from test  
) as expandedtest
where item->>0 like 'first 1' and item->>1 like '%3%'
...