запрос ключей JSON, которые пересекаются в Postgres JSONB - PullRequest
0 голосов
/ 31 августа 2018

Как запросить ключи jsonb, которые пересекаются:

Пример:

     kv                                 |        column1                   
-----------------------------------------------------------
[{"k1": "v1"}, {"k2": "v22"}]           | web
[{"k10": "v5"}, {"k9": "v21"}]          | mobile
[{"k1": "v1"}, {"k5": "v24"}]           | web1
[{"k5": "v1"}, {"k55": "v24"}]          | web1

здесь, строка 1 и строка 3 имеют ключ k1, а строка 3 и строка 4 имеют ключ k5.

Итак, ответом должны быть строки 1,3 и 4.

Ответы [ 2 ]

0 голосов
/ 31 августа 2018

При условии следующей настройки:

create table data (id serial, kv jsonb, col1 text);

insert into data (kv, col1)
values
('[{"k1": "v1"}, {"k2": "v22"}]', 'web'),
('[{"k10": "v5"}, {"k9": "v21"}]', 'mobile'),
('[{"k1": "v1"}, {"k5": "v24"}]', 'web1'),
('[{"k5": "v1"}, {"k55": "v24"}]', 'web1');

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

with normalized as (
  select d.id, t2.*
  from data d
    join jsonb_array_elements(kv) as t1(kv) on true
    join jsonb_each_text(t1.kv) as t2(k,val) on true
)
select n1.*
from normalized n1
where exists (select *
              from normalized n2
              where n1.id <> n2.id 
                and n1.k = n2.k);

Вышеуказанное возвращает:

id | k  | val
---+----+----
 1 | k1 | v1 
 3 | k1 | v1 
 3 | k5 | v24
 4 | k5 | v1 

Или используйте его с условием IN, чтобы получить исходные строки:

with normalized as (
  select d.id, t2.*
  from data d
    join jsonb_array_elements(kv) as t1(kv) on true
    join jsonb_each_text(t1.kv) as t2(k,val) on true
)
select *
from data
where id in (select n1.id
            from normalized n1
            where exists (select *
                          from normalized n2
                          where n1.id <> n2.id 
                            and n1.k = n2.k))

возвращается:

id | kv                             | col1
---+--------------------------------+-----
 1 | [{"k1": "v1"}, {"k2": "v22"}]  | web 
 3 | [{"k1": "v1"}, {"k5": "v24"}]  | web1
 4 | [{"k5": "v1"}, {"k55": "v24"}] | web1

Этот тип запроса будет проще, если вы не сохраните пары ключ / значение в массиве, '{"k1": "v1", "k2": "v22"}' будет иметь для меня гораздо больший смысл, чем [{"k1": "v1"}, {"k2": "v22"}]

0 голосов
/ 31 августа 2018

Вы можете попробовать это:

--This part is to simulate your table
with yourTable as (
select (string_to_array(t,'|'))[1]::jsonb kv,(string_to_array(t,'|'))[2] column1 from (
select unnest(string_to_array($$[{"k1": "v1"}, {"k2": "v22"}]           | web
[{"k10": "v5"}, {"k9": "v21"}]          | mobile
[{"k1": "v1"}, {"k5": "v24"}]           | web1
[{"k5": "v1"}, {"k55": "v24"}]          | web1$$::character varying,E'\n')) t

) b
) 
-- This is your request :
   select distinct kv,column1 from (
        select *,count(*) over (partition by elt) nb_inter from (
          select kv,column1,jsonb_object_keys(jsonb_array_elements(kv)) elt from yourTable
          ) a 
        ) b
where nb_inter >1
...