How to get the SUM of values on a JSONB column - postgresql

I've got a 1-dimension JSONB on postgresql like this:
SELECT '{"a": 1, "b": 2, "c": 3}'::jsonb;
How to get the SUM of values on a JSONB column?
Like the sum of 1+2+3?
PostgreSQL has the jsonb_object_keys function, but I was looking for something like "jsonb_object_values" (I know that this function does not exists)
# select jsonb_object_keys( '{"a": 1, "b": 2, "c": 3}'::jsonb );
jsonb_object_keys
-------------------
a
b
c
(3 rows)

The jsonb_each_text() function expands a set of JSON objects into rows in (key, value) format. Since it returns a set of rows, you should use it as a row source. Since it returns data in the text format, you should cast it to the appropriate type before further processing.
SELECT sum(v::integer)
FROM jsonb_each_text('{"a": 1, "b": 2, "c": 3}'::jsonb) j(k,v);

Related

Select all values for given key from JSON array column

I have a json array column, foo:
[{"a": "my_key_type", "b": "foo"}]
[{"a": "my_other_key_type", "b": "foo"}]
[]
[{"a": "my_key_type", "b": "bar"}, {"a": "my_other_key_type", "b": "baz"}]
I would like to select into an array all a values for each JSON element in the column. For instance:
['my_key_type']
['my_other_key_type']
[]
['my_key_type','my_other_key_type']
I was initially pursuing json_object_keys, but that doesn't seem to accomplish what I'm actually after, and it doesn't work on an array type. I have not found a function in the documentation that seems to accomplish what I'm after.
I was able to solve this with a bit of workaround by exploding the JSON elements into rows, and then using array_agg:
with keys as (
select
id,
json_array_elements(my_column)->>'a' as e
from my_table
)
select
id,
array_agg(e) as a
from keys
group by id;
This gives:
id | a
----------------
1 | {my_key_type}
2 | {my_other_key_type}
4 | {my_key_type,my_other_key_type}

Querying jsonb that contains arrays and booleans

I have a table with a jsonb field. The content looks like this:
select myfield from mytable
{"1": [1, 2, 3], "2": [1, 2], "3": [1, 2], "4": true}
{}
{"1": [6, 11]}
{"3": [2], "4": true}
So far I've managed to run queries like:
-- Select all rows that, for key "1", are null
select addl from live_feeds where (addl->>'1') is null
-- Select all rows that, for key "4", are true
select addl from live_feeds where (addl->>'4')::boolean is true;
But after trying a few different approaches, I couldn't get this one to work:
Select all rows that, for key "1", include value 1
What kind of casting/operator should I use for this?
You could do this (function json doc)
#>: Does the left JSON value contain within it the right value?
select addl from live_feeds where (addl->>'1')::jsonb #> '[1]'

PostgreSQL jsonb - omit multiple nested keys

The task is to remove multiple nested keys from jsonb field.
Is there any way to shorten this expression without writing a custom function?
SELECT jsonb '{"a": {"b":1, "c": 2, "d": 3}}' #- '{a,b}' #- '{a,d}';
suppose we need to delete more than 2 keys
There is no way to shorten the expression. If your goal is to pass to the query a single array of keys to be deleted you can use jsonb_set() with jsonb_each():
with my_table(json_col) as (
values
(jsonb '{"a": {"b":1, "c": 2, "d": 3}}')
)
select jsonb_set(json_col, '{a}', jsonb_object_agg(key, value))
from my_table
cross join jsonb_each(json_col->'a')
where key <> all('{b, d}') -- input
group by json_col -- use PK here if exists
jsonb_set
-----------------
{"a": {"c": 2}}
(1 row)
The solution is obviously more expensive but may be handy when dealing with many keys to be deleted.
NVM, figured it out)
For this particular case, we can re-assign property with removed keys (flat):
SELECT jsonb_build_object('a', ('{ "b":1, "c": 2, "d": 3 }' - ARRAY['b','d']));
More general approach:
SELECT json_col || jsonb_build_object('<key>',
((json_col->'<key>') - ARRAY['key-1', 'key-2', 'key-n']));
Not very useful for deep paths, but works ok with 1-level nesting.

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

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.
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.
I actually found that this way works to.
select jsonb_build_object('key', column->'key') from table;
reference:
https://www.reddit.com/r/PostgreSQL/comments/73auce/new_user_to_postgres_can_i_grab_multiple_keys_of/
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'

How can I merge an existing jsonb field when upserting a record in Postgres?

Say I have a table on Postgres with a jsonb column containing {"a": 1, "b": 2}. Now I'd like to upsert a record with the same id and {"b": 10, "c": 20} as the jsonb column value.
Consequently, I'd like the jsonb field of the row to contain {"a": 1, "b": 10, "c": 20}. How can this be achieved?
If you want an "upsert", you can do this with insert ... on conflict...
insert into the_table (id, json_column)
values (1, '{"b": 10, "c": 20}'::jsonb)
on conflict (id) do update
set json_column = table_name.json_column || excluded.json_column;
If concatenate 2 jsonb value, you achieve what you want, for example:
select '{"a": 1, "b": 2}'::jsonb || '{"b": 10, "c": 20}'::jsonb
produces: "{"a": 1, "b": 10, "c": 20}"
if both operands are objects with a common key field name, the value of the field in the result will just be the value from the right hand operand.
https://www.postgresql.org/docs/current/static/functions-json.html
Well, my example is not about merging 2 json fields, but arrays instead:
insert into tag_lists (article_id, tags) values (1, '{job}')
on conflict (article_id)
do update set tags = (
select array_agg(distinct x) from unnest(tag_lists.tags || excluded.tags) x
);
Thanks to this answer for providing comprehensive snippets