0

How do i query for jsonb keys which intersect:

Ex:

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

here, row 1 and row 3 has key k1 and row 3 and row 4 has key k5.

So, the answer should be rows 1,3 & 4.

2
  • Why do you have a column named column? Is that "column" related to your question? Commented Aug 31, 2018 at 7:01
  • sorry. it was just an example. updated :) Commented Aug 31, 2018 at 7:02

2 Answers 2

1

Assuming the following setup:

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');

You can get those rows by first normalizing the data, then doing a self join on the normalized data. To normalize the data you need to unnest the JSON values twice: once for flattening the arrays and then another time to extract the keys from the JSON values:

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);

The above returns:

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

Or use it with an IN condition to get the original rows:

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))

returns:

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

This type of query would be easier if you didn't store the key/value pairs in an array, '{"k1": "v1", "k2": "v22"}' would make a lot more sense to me than [{"k1": "v1"}, {"k2": "v22"}]

Sign up to request clarification or add additional context in comments.

1 Comment

i agree with how it should be stored. this is an already existing table and has to be backward compatible. cannot migrate to the new structure yet.
1

You can try this :

--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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.