Query in JSONB array of Postgres database - postgresql

Below JSON is one of the column of type JSONB in my table 'logic', I want to query to check how many rows are there with type: QUESTION (any entry within conditions).
{
"name": null,
"conditions": [
{
"type": "QUESTION",
"question": {
}
},
{
"type": "QUESTION",
"question": {
}
},
{
"type": "FIELD",
"question": {
}
}
],
"expression": "A"
}

If you want to check the number of times "type": "QUESTION" entry appears within conditions of the jsonb column throughout the table.
select count(*) FROM logic CROSS JOIN LATERAL
jsonb_array_elements(jsonb_col->'conditions')as j(typ)
WHERE j->>'type' = 'QUESTION'
If you want to check the number of times "type": "QUESTION" entry appears within conditions for each row.
select jsonb_col,count(*) FROM logic CROSS JOIN LATERAL
jsonb_array_elements(jsonb_col->'conditions')as j(typ)
WHERE j->>'type' = 'QUESTION'
group by jsonb_col
If you want to check how many rows have at least one entry within conditions with
'type' = 'QUESTION',
select count(*) FROM
(
select DISTINCT jsonb_col FROM logic CROSS JOIN LATERAL
jsonb_array_elements(jsonb_col->'conditions')as j(typ)
WHERE j->>'type' = 'QUESTION'
)s;
Use the query which you find is appropriate for you
Demo

Related

Update property of object in jsonb array and keep other properties

I have a postgres table with a jsonb colum like this:
create table if not exists doc
(
id uuid not null
constraint pkey_doc_id
primary key,
data jsonb not null
);
INSERT INTO doc (id, data) VALUES ('3cf40366-ea58-402d-b63b-c9d6fdf99ec8', '{"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8", "Tags": [{"Key": "inoivce", "Value": "70086"},{"Key": "customer", "Value": "100233"}] }' );
INSERT INTO doc (id, data) VALUES ('ae2d1119-adb9-41d2-96e9-53445eaf97ab', '{"Id": "ae2d1119-adb9-41d2-96e9-53445eaf97ab", "Tags": [{"Key": "project", "Value": "12345"},{"Key": "customer", "Value": "100233"}]}' );b9-41d2-96e9-53445eaf97ab", "Tags": [{"Key": "customer", "Value": "100233"}]}' )
Tags.Key in the first row contains a typo inoivce which I want to fix to invoice:
{
"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8",
"Tags": [{
"Key": "inoivce",
"Value": "70086"
},{
"Key": "customer",
"Value": "100233"
}]
}
I tried this:
update doc set data = jsonb_set(
data,
'{"Tags"}',
$${"Key":"invoice"}$$
) where data #> '{"Tags": [{ "Key":"inoivce"}]}';
The typo gets fixed but I'm loosing the other Tags elements in the array:
{
"Id": "3cf40366-ea58-402d-b63b-c9d6fdf99ec8",
"Tags": [{"Key": "invoice"}]
}
How can I fix the typo without removing the other elements of the Tags array?
Dbfiddle for repro.
One possible solution, not so obvious : we need a CTE because the idea here is to loop on the 'Tags' jsonb array elements using the jsonb_agg aggregate function to rebuild the array, but the SET clause of an UPDATE doesn't accept aggregate functions ...
WITH list AS
( SELECT d.id, (d.data - 'Tags') || jsonb_build_object('Tags', jsonb_agg(jsonb_set(e.content, '{Key}' :: text[], to_jsonb(replace(e.content->>'Key', 'inoivce', 'invoice'))) ORDER BY e.id)) AS data
FROM doc AS d
CROSS JOIN LATERAL jsonb_array_elements(d.data->'Tags') WITH ORDINALITY AS e(content, id)
WHERE d.data #? '$.Tags[*] ? (exists(# ? (#.Key == "inoivce")))'
GROUP BY d.id, d.data
)
UPDATE doc AS d
SET data = l.data
FROM list AS l
WHERE d.id = l.id
see the result in dbfiddle

Calculate sum for items in nested jsonb array

So I have Postgres DB table 'purchases' with columns 'id' and 'receipt'. 'id' is primary int column, the value in column 'receipt' is jsonb and can look like this:
{
"shop_name":"some_shop_name",
"items": [
{
"name": "foo",
"spent": 49,
"quantity": 1
},
{
"name": "bar",
"price": 99,
"quantity": 2
},
{
"name": "abc",
"price": 999,
"quantity": 1
},
...
]
}
There can be varied amount of items in receipt.
In the end I need to write a query so the resulting table contains purchase id and amount spent on all bar for every purchase in table as such:
id
spent
1
198
..
...
My issue:
I can't figure out how to work with jsonb inside select query along regular columns in the resulting table, I guess query should be structured as this:
SELECT p.id, %jsonb_parsing_result_here% AS spent
FROM purchases p
It's blocking me from moving further with iterating through items in FOR cycle (or maybe using another way).
You can unnest the items from the receipt column with a lateral join and the jsonb_array_elements function like:
SELECT p.id, SUM((item->>'price')::NUMERIC)
FROM purchases p,
LATERAL jsonb_array_elements(p.receipt->'items') r (item)
GROUP BY p.id
It's also possible to add a where condition, for example:
WHERE item->>'name' = 'bar

JSONB Represent jsonb key value like array

I have field extra with type jsonb in my table product. And I need get uniq key with uniq values for each key from all products row. Example data in extra field
{"SIZE": "110/116", "COLOUR": "Vit", "GENDER": "female", "AGE_GROUP": "Kids", "ALTERNATIVE_IMAGE": "some_path"}
for now I use query like this
select DISTINCT e.key, array_agg(DISTINCT e.value) as fields
from products AS p
join jsonb_each_text(p.extras) e on true
GROUP BY e.key
Ad have respnse (small part with some keys in full response all keys are present) like this
[
{
"key": "AGE_GROUP",
"fields": "{Adult,children,Kids}"
},
{
"key": "GENDER",
"fields": "{female,male,man}"
}
]
how to change it to array for fields alias ?
like this
[
{
"AGE_GROUP": ["Adult","children","Kids"]
},
{
"GENDER": ["female","male","man"]
}
]
or maybe whould be great like this
[
"some_alias": [{"AGE_GROUP": "Adult", "AGE_GROUP": "children", "AGE_GROUP": "Kids"}],
"some_alias": [{"GENDER": "female", "GENDER": "male", "GENDER": "man"}]
]
This will get you the former form I think:
select jsonb_agg(jsonb_build_object(k,v)) from (
select DISTINCT e.key, jsonb_agg(DISTINCT e.value) as fields
from products AS p
join jsonb_each_text(p.extras) e on true
GROUP BY e.key
) b(k,v);
Best regards,
Bjarni

Postgresql Search for a value in jsonb object

I have a jsonb field in a table having values of the form
{
"message": {
"sender": {
"from": "91**********"
},
"channel": "some kind of text",
"content": {
"text": "some kind of text",
"type": "text"
},
"recipient": {
"to": "91**********",
"recipient_type": "some kind of text"
},
"preferences": {
"webHookDNId": "some kind of text"
}
},
"metaData": {
"version": "some kind of text"
}
}
Now i want to search for all such value which in "to" key of the object has a certain phone number. i am using following query for this but it is not working
select * from table_name where (column1::jsonb ? '91**********') ;
? looks for a top-level key. The JSON you show only has two top-level keys, "message" and "metadata". So of course they don't match to '91**********'.
You probably want the containment operator #>:
#> '{"message":{"recipient":{"to":"91**********"}}}'
This will be supported by the either type of JSONB GIN index on your column.
You can use the -> and ->> operators to extract the value from a key:
select *
from the_table
where (the_column -> 'recipient' ->> 'to') = '91**********';
Or the #>> operator
select *
from the_table
where the_column #>> '{recipient,to}' = '91**********';

Returning set of records

I have a table that stores records broken into fields, ie. if I have this record as follows
{
"name": "John Doe"
"gender": "male"
}
Then this record is stored in the table in 2 rows, as follows
[
{ "id": "1", "col": "name", "value": "John Doe" },
{ "id": "1", "col": "gender", "value": "male" }
]
If I want to write a postgresql function, that returns the record back to the original form (all attributes in a row form, instead of the one attribute one row form), how can I do it?
(the table design was done as an experiment for data warehousing purpose)
If I understand you right, you could write something like that
select a.value, b.value
from table1 a
inner join table1 b on a.id = b.id and a.orderNum < b.orderNum
To make it simple you could introduce field orderNum
p.s. I do not have Postgress installed, so you possibly would fix my query.