How to get only the jsonb of specific keys from postgres?

Viewed 16003

I'm aware that you can remove keys from a jsonb in postgres using something like this

select '{"a": 1, "b": 2, "c":3}'::jsonb -'a';
 ?column?
----------
{"b": 2 "c":3}
(1 row)

Is there a way to only grab specific keys? Like let's say I just want to get the key-value pair of just the 'a' key.

Something like this?

select '{"a": 1, "b": 2}'::jsonb + 'a' + 'b';
 ?column?
----------
{"a": 1, "b": 2}
(1 row)

EDIT: Changed the example to to show that I'd like to grab multiple keys-value pairs from the jsonb and not just one pair.

7 Answers

You can filter down to a single key fairly easily like so:

jsonb_object(ARRAY[key, jsonb_data -> key])

...or you can filter down to multiple keys:

(SELECT jsonb_object_agg(key, value) FROM jsonb_each(jsonb_data) WHERE key IN ('a', 'b'))

Or on a more complex condition, if you want:

(
  SELECT jsonb_object_agg(key, value)
  FROM jsonb_each(jsonb_data)
  WHERE
    key NOT LIKE '__%'
    AND jsonb_typeof(value) != 'null'
)

These kinds of questions can be answered very easily by simply reading the documentation.

You can do this

SELECT jsonb_column->>'key_name_here' as 'alias_name_as_you_like' from table_name

In the case of the query asked above, it would be

select '{"a": 1, "b": 2, "c":3}'::jsonb->>'a'

You can get just the value like so:

 select '{"a": 1, "b": 2}'::jsonb-> 'a';

If you must, you can transform that back into jsonb manually, or perhaps go through an array, hstore or other intermediate type. Here's the "manual" way

 select ('{ "a": '||('{"a": 1, "b": 2}'::jsonb->'a')::text||'}')::jsonb

Paraphrasing the situation

we have a jsonb value and multiple keys in mind, a and c

select '{"a": 1, "b": 2, "c":3}'::jsonb - '{a,c}'::text[];

- is a tidy operator but gives us the opposite of what you want

{"b": 2}

solution is to wrap that in array(select jsonb_object_keys(...)) and perform the - again

select '{"a": 1, "b": 2, "c":3}'::jsonb - array(select jsonb_object_keys('{"a": 1, "b": 2, "c":3}'::jsonb - '{a,c}'::text[]));

you get a json with only those keys, a and c

{"a": 1, "c": 3}

If you want to filter multiple rows with JSONB documents in each of them:

-- Let's generate rows with JSONB column:
WITH s AS (SELECT generate_series(1, 100) num),
g AS (SELECT num, jsonb_build_object('a', s.num, 'b', s.num * 2) obj FROM s),

-- A "filter" adding (in my example only keys of "filter" document remain in result rows)
j AS (SELECT '{"a": "int", "c": "string"}'::jsonb AS filter),
a AS (SELECT (ARRAY(SELECT jsonb_object_keys(filter))) AS ar FROM j),

-- Useless keys removing
o AS (SELECT jsonb_object_agg(l.key, l.value) obj
        FROM g, LATERAL jsonb_each(g.obj) l, a 
        WHERE l.key = ANY(a.ar)
        GROUP BY num)

SELECT * FROM o ORDER BY obj->'a';
Begin;
    CREATE TEMPORARY TABLE test (id  serial, jdoc jsonb);
    insert into test(jdoc) values('{"a": {"b":"foo"}}');
    insert into test(jdoc) values('{"a": "test"}');
    insert into test(jdoc) values('{"a":[2,3,4]}');
    insert into test(jdoc) values('{"b":[2,3,4]}');
  commit;

select (jdoc->'a') from test where jdoc ? 'a'
will get all the specific key's value.
If you want JSONB of the specific key: select jdoc from test where jdoc ? 'a'

Related