0

I'm trying to pull elements from JSONB column. I have table like:

id NUMBER
data JSONB

data structure is:

[{
    "id": "abcd",
    "validTo": "timestamp"
}, ...]

I'm querying that row with SELECT * FROM testtable WHERE data @> '[{"id": "abcd"}]', and it almost works like I want to.

The trouble is data column is huge, like 100k records, so I would like to pull only data elements I'm looking for. For example if I would query for SELECT * FROM testtable WHERE data @> '[{"id": "abcd"}]' OR data @> '[{"id": "abcde"}]' I expect data column to contain only records with id abcd or abcde. Like that:

[
{"id": "abcd"},
{"id": "abcde"}
]

It would be okay if query would return separate entries with single data record.

I have no ideas how to solve it, trying lot options since days.

2
  • This sounds as if a properly normalized one-to-many relationship would be a lot better Commented May 4, 2020 at 11:35
  • Yeah, I totally agree, I'm going to write migration soon, but I need it to make hot fix. Commented May 4, 2020 at 14:12

3 Answers 3

1

To have separate output for records having multiple matches

with a (id, data) as (
  values
    (1, '[{"id": "abcd", "validTo": 2}, {"id": "abcde", "validTo": 4}]'::jsonb),
    (2, '[{"id": "abcd", "validTo": 3}, {"id": "abc", "validTo": 6}]'::jsonb),
    (3, '[{"id": "abc", "validTo": 5}]'::jsonb)
)
select id, jsonb_array_elements(jsonb_path_query_array(data, '$[*] ? (@.id=="abcd" || @.id=="abcde")'))
from a;

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

Comments

0

You will need to unnest, filter and aggregate back:

select t.id, j.*
from testtable t
  join lateral (
     select jsonb_agg(e.x) as data
     from jsonb_array_elements(t.data) as e(x)
     where e.x @> '{"id": "abcd"}'
        or e.x @> '{"id": "abcde"}'
  ) as j on true

Online example

With Postgres 12 you could use jsonb_path_query_array() as an alternative, but that would require to repeat the conditions:

select t.id,
       jsonb_path_query_array(data, '$[*] ? (@.id == "abcd" || @.id == "abcde")')
from testtable t
where t.data @> '[{"id": "abcd"}]'
   or t.data @> '[{"id": "abcde"}]'

Comments

0

Didn't quite get your question.Are you asking that the answer should only contain data column without id column .Then I think this is the query:

Select data from testtable where id="abcd" or id="abcde";

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.