Search code examples
sqljsonpostgresqljsonb

Postgres Json(b) int array contains any of array values


TLDR: How do I find every record that contains a json number array which contains one number of the given array/list.

Ok, I tried a lot, read a lot of other Threads but I didn't find a working solution yet. I have some JSON Objects in Postgres I'd like to find. They contain the index of a multiple choice select.

Objects:

[{"id": 5, "list": [1, 2, 3]}, {"id": 6, "list": [4, 5, 6]}]

And I need so select all objects which contain the Items 1 OR 5. For now, I can only check for one value

SELECT '[1, 2, 3, 4]'::jsonb @> '3'::jsonb as result;

or if both are present:

SELECT '[1,2,3,4]'::jsonb @> '[1, 2]'::jsonb as result;

Or, the interesting thing, if I have an array of strings, I can do what I need:

SELECT '["1","2","3","4"]'::jsonb ?| array['1', '5'] as result;

How do I apply this to JSON number arrays? I tried casting to int[] and a lot of other stuff. Nothing worked. I either need to convert the number array to a text array to work with it or find the right cast for the right side. This also didn't work: ?| array[1, 5]::text[]

Solution 1:

Well, the first thing that came to my and Mao Tsuns mind, to use OR, still looks like the fastest and simplest solution.

Solution 2:

I finally found a way to do it shorter (like I thought it should work) but it's a casting hell (also 9.4+) and not pretty to look at:

SELECT array_to_json(translate('[1,2,3,4]'::jsonb::text, '[]', '{}')::text[])::jsonb ?| array['1','5'] as result;

Which basically transforms the integers to strings.


Solution

  • you can use OR:

    t=# SELECT '[1, 2, 3, 4]'::jsonb @> '3'::jsonb OR '[1, 2, 3, 4]'::jsonb @> '5'::jsonb as result;
     result
    --------
     t
    (1 row)
    

    or aggregate unnested jsonb array to int array to compare if it intersects:

    t=# with c(j) as (values('[1, 2, 3, 4]'::jsonb))
    , a as (select j,jsonb_array_elements(j) ja from c)
    select array_agg(ja), j, array_agg(ja::text::int) && array[5,4] from a group by j;
     array_agg |      j       | ?column?
    -----------+--------------+----------
     {1,2,3,4} | [1, 2, 3, 4] | t
    (1 row)
    

    because all jsonb operators behave as documented in your examples. And I don't see some elegant shorter solution...