how to get result as 'ARRAY of objects' in postgresql query? - postgresql

I am selecting rows as
`SELECT
jsonb_build_object('platform_id',pl.id,'platform_Name',pl."name") as "platform" from
"platforms" as pl ON pl.id = up."platform_id"
WHERE usrs.id = ${userid}`
I am getting result as JSON
"platform": {
"platform_id": 5,
"platform_Name": "Snapchat"
}
but I want the platform result as "array of object " like below
DESIRED RESULT :
"platform": [{
"platform_id": 5,
"platform_Name": "Snapchat"
}]
How to make write query to get it as array ?

I recreated a similar example with:
create table platforms (id int, name text, user_id int);
insert into platforms values (1, 'snapchat',100),(2, 'fb', 100),(3, 'instagram', 200), (4, 'twitter',100);
The platforms table contains:
id | name | user_id
----+-----------+---------
1 | snapchat | 100
2 | fb | 100
3 | instagram | 200
4 | twitter | 100
(4 rows)
You can solve the problem in 2 steps:
create the json object with id and name using jsonb_build_object
aggregate over user_id using jsonb_agg
the query is
with create_doc as (
SELECT
user_id,
jsonb_build_object('platform_id',id,'platform_Name',name) as platform
from platforms
)
select user_id, jsonb_agg(platform) from create_doc group by user_id;
result
user_id | json_agg
---------+----------------------------------------------------------------------------------------------------------------------------------------------
200 | [{"platform_id": 3, "platform_Name": "instagram"}]
100 | [{"platform_id": 1, "platform_Name": "snapchat"}, {"platform_id": 2, "platform_Name": "fb"}, {"platform_id": 4, "platform_Name": "twitter"}]
(2 rows)

Related

Postgres JSONB, query first appearance of inner field

Trying to query product_id and inner JSONB value "quantity_on_hand" with "unit:one". Below is example table
Products table
| product_id | data |
| -------- | -------------|
| 00445 | {"storage"...}| - rest of data specified right below
{
"storage": [
{
"unit": "one",
"quantity": 3,
},
{
"unit": "two",
"quantity": 2,
}
}
I found a query:
SELECT product_id, e -> 'quantity' as quant
FROM Products t,
jsonb_array_elements(t.value->'storage') e
WHERE (product_id) IN (('00445'));
The query returns following output:
product_id | quant
00445 | 3
00445 | 2
Please advise how to set rule: "quantity_on_hand" with "unit:one" to return only:
product_id | quant
00445 | 3
Thanks
You can add a clause for filtering the result of the jsonb_array_elements to only include elements where the JSON key "unit"'s value is "one":
SELECT product_id,
e -> 'quantity' AS quant
FROM Products t,
JSONB_ARRAY_ELEMENTS(t.value -> 'storage') e
WHERE (product_id) IN (('00445'))
AND e ->> 'unit' = 'one';
This should give:
product_id | quant
------------+-------
1 | 3
(1 row)
See https://www.postgresql.org/docs/14/functions-json.html for more information on JSONB operators and functions in Postgres.

PostgreSQL - transform varchar set to json

One of the columns from my table contains varchar data in the form:
{"item1", "item2", "item3"}. The number of elements may vary, the column is also nullable.
I would like to change the type of this column to json. After the change, the data should be in the format:
[{"name": "SOME_NAME", "item": "item1"}, {"name": "SOME_NAME", "item": "item2"}, {"name": "SOME_NAME", "item": "item3"}]
Value SOME_NAME is fixed and will be the same for all elements.
So having:
+-----------------+
| items (varchar) |
+-----------------+
| {item1,item2} |
| null |
| {item3,item4} |
+-----------------+
I'd like to have a new column with:
+----------------------------------------------------------------------------------+
| new_items (json) |
+----------------------------------------------------------------------------------+
| [{"name": "SOME_NAME", "item": "item1"}, {"name": "SOME_NAME", "item": "item2"}] |
| null |
| [{"name": "SOME_NAME", "item": "item3"}, {"name": "SOME_NAME", "item": "item4"}] |
+----------------------------------------------------------------------------------+
I tried with json_build_object() and similar json functions, but I didn't get the intended effect.
I believe you can get your data back as array of text with
with first_sel as (
select '{"item1", "item2", "item3"}' col_text
)
select col_text::text[] from first_sel;
result
col_text
---------------------
{item1,item2,item3}
Get them in different rows with the unnest
with first_sel as (
select '{"item1", "item2", "item3"}' col_text
)
select unnest(col_text::text[]) from first_sel;
Result
unnest
--------
item1
item2
item3
Then you can build the single JSON entries with json_build_object
with first_sel as (
select '{"item1", "item2", "item3"}' col_text
),
list_of_rows as(
select unnest(col_text::text[]) items from first_sel
)
select
json_build_object('SOME_NAME',items) from list_of_rows
Result
json_build_object
-------------------------
{"SOME_NAME" : "item1"}
{"SOME_NAME" : "item2"}
{"SOME_NAME" : "item3"}
(3 rows)
and then aggregate them with json_agg
with first_sel as (
select '{"item1", "item2", "item3"}' col_text
),
list_of_rows as(
select unnest(col_text::text[]) items from first_sel
),
single_entries as (
select json_build_object('SOME_NAME',items) json_obj from list_of_rows)
select
json_agg(json_obj)
from single_entries;
Result
json_agg
-----------------------------------------------------------------------------
[{"SOME_NAME" : "item1"}, {"SOME_NAME" : "item2"}, {"SOME_NAME" : "item3"}]
Edit: since the subject of the original question now spans more rows. If you have a column id for which you can group by, then use that, otherwise you can:
Create a similar table with the following
create table test (col_text text);
insert into test values ('{"item1", "item2", "item3"}');
insert into test values (NULL);
insert into test values ('{"item4", "item5", "item6"}');
Use the ctid as group by information
with list_of_rows as(
select ctid id, unnest(col_text::text[]) items from test
),
single_entries as (
select id, json_build_object('SOME_NAME',items) json_obj from list_of_rows)
select
id,
json_agg(json_obj)
from single_entries
group by id;
result
id | json_agg
-------+-----------------------------------------------------------------------------
(0,1) | [{"SOME_NAME" : "item1"}, {"SOME_NAME" : "item2"}, {"SOME_NAME" : "item3"}]
(0,3) | [{"SOME_NAME" : "item4"}, {"SOME_NAME" : "item5"}, {"SOME_NAME" : "item6"}]
(2 rows)

Postgres aggregate json function to return array of objects with subquery

I have 3 tables: patient, patient_form (join table), form that look as follows:
patient
| id | name |
| --- | ------------------- |
| 1 | Bob |
| 2 | Matilda |
patient_form (join table)
| id | patient_id | form_id |
| --- | ---------- | -------
| 1 | 1 |1
| 2 | 2 |1
form
| id | label
| --- | ------------------- |
| 1 | intake |
| 1 | follow up |
I'd like to create a query that selects a patient and chains the relational data to result in the following output:
{
id: 1,
name: "Bob",
forms: [ {label: "intake"}, {label:"followup"} ]
}
I understand that there a way using json_agg to create the desired array of objects unfortunatly I've not been able to get this to work.
I figured this out as follows using json_build_object:
SELECT
ARRAY(
SELECT json_build_object(
'link', link,
'label', (SELECT label FROM form WHERE id = form_id))
FROM patient_form
WHERE patient_form.patient_id = patient.id
) AS "forms"
WHERE patient.id = 1;
Instead of the subquery for forms, you can use JOIN instead. The full implementation is as follows:
Schema (PostgreSQL v15)
CREATE TABLE IF NOT EXISTS patient (
id INT NOT NULL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS form (
id INT NOT NULL PRIMARY KEY,
label TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS patient_form(
id INT NOT NULL PRIMARY KEY,
patient_id INT,
form_id INT,
CONSTRAINT patient_id_fk FOREIGN KEY(patient_id)
REFERENCES patient(id)
ON DELETE CASCADE,
CONSTRAINT form_id_fk FOREIGN KEY(form_id)
REFERENCES form(id)
ON DELETE CASCADE
);
INSERT INTO form(id, label) VALUES (1, 'intake'), (2, 'follow up');
INSERT INTO patient(id, name) VALUES (1, 'Bob'), (2, 'Matilda');
INSERT INTO patient_form(id, patient_id, form_id) VALUES (1, 1, 1), (2, 2, 1);
Query #1
SELECT json_build_object(
'id', p.id,
'name', p.name,
'forms', json_agg(f)
) AS data
FROM patient AS p
JOIN patient_form AS p_f ON p_f.patient_id = p.id
JOIN form AS f ON f.id = p_f.form_id
WHERE p.id = 1
GROUP BY p.id;
data
{"id":1,"name":"Bob","forms":[{"id":1,"label":"intake"}]}
Put simply, the output is:
{
"id": 1,
"name": "Bob",
"forms": [
{
"id": 1,
"label": "intake"
}
]
}
View on DB Fiddle

extract all values of postgresql jsonb object

i have a postgresql table t1 , id integer , data jsonb
id | data
--------------------
1 | {"1":{"11":11},"2":{"12":12}}
and i need a function to extract all key/value in separate rows
like this
key | values
----------------------
1 | {"11":11}
2 | {"12":12}
in "hstore" dataType , there was "hvals" function , do this
but in jsonb i dont find similar function
You are looking for jsonb_each
with t1 (id, data) as (
values (1, '{"1":{"11":11},"2":{"12":12}}'::jsonb)
)
select t.*
from t1, jsonb_each(data) as t(k,v)
returns:
k | v
--+-----------
1 | {"11": 11}
2 | {"12": 12}

Migrate flat jsonb to hstore

I run postgres 9.4, and want to migrate column in my database table to hstore just to be able to make performance comparison.
My current column is key-value pair in jsonb, w/o nested structure.
Any tips how to approach this problem?
Example data:
create table jsons (id int, val jsonb);
insert into jsons values
(1, '{"age":22}'),
(2, '{"height":182}'),
(3, '{"age":30, "height":177}');
Split json objects to key, value pairs:
select id, (jsonb_each_text(val)).key, (jsonb_each_text(val)).value
from jsons
id | key | value
----+--------+-------
1 | age | 22
2 | height | 182
3 | age | 30
3 | height | 177
(4 rows)
Aggregate the pairs and convert them to hstore:
select id, hstore(array_agg(key), array_agg(value))
from (
select id, (jsonb_each_text(val)).key, (jsonb_each_text(val)).value
from jsons
) sub
group by 1
order by 1
id | hstore
----+------------------------------
1 | "age"=>"22"
2 | "height"=>"182"
3 | "age"=>"30", "height"=>"177"
(3 rows)
The same can be accomplished in a more elegant way using lateral join:
select id, hstore(array_agg(key), array_agg(value))
from jsons
cross join jsonb_each_text(val)
group by 1
order by 1;