How to convert json to string by concatenating certain keys in postgres - postgresql

Input:
[{"name": "X", "strength": "10"}, {"name": "Y", "strength": "30"}]
Desired output:
X-Y 10-30

First aggregate into arrays and then convert the arrays to strings.
select array_to_string(array_agg(j ->> 'name'), ','),
array_to_string(array_agg(j ->> 'strength'), ',')
from jsonb_array_elements
('[
{"name": "X", "strength": "10"},
{"name": "Y", "strength": "30"},
{"name": "Z", "strength": "20"}
]') j;
When doing on a table column:
select col1,
(select array_to_string(array_agg(j ->> 'name'), ',')
from json_array_elements(cast(col1 as json)) j),
(select array_to_string(array_agg(j ->> 'strength'), ',')
from json_array_elements(cast(col1 as json)) j)
from table1;

Related

How do I create a Postgres table with a column that holds an array of objects?

The documentation shows that arrays can be created as either text or integer. I did not find an example for creating an object array. An example is the items column:
CREATE TABLE cart (
_id serial PRIMARY KEY,
user_id Integer UNIQUE NOT NULL,
items varchar [],
FOREIGN KEY (user_id)
REFERENCES users (_id)
Object stored in the items array have quotes around them making them strings instead of objects. A code segment creating objects in items is shown below:
update cart
set items =
array_append(items,
'{product_id: ${cartItem.productId}, size: ${cartItem.size}, quantity: ${cartItem.quantity}}')
where _id = ${cart._id}
and user_id = ${userId}
RETURNING *
I am compelled to put quotes around the object value. As a result it is stored as a string in the column with quotes around it.
cart {
_id: 1,
user_id: 1,
items: [
'{product_id: 1, size: Small, quantity: 1}',
'{product_id: 1, size: Small, quantity: 1}',
'{product_id: 1, size: Small, quantity: 1}'
]
}
Because the items in the items column are stored as string instead of objects, I cannot correctly iterate over them in my program. For example,
items.product_id does not exist.
How do I fix this?
I wrote some examples for you
Example 1. (Insert into JSONB type using converting String to JSONB)
INSERT INTO cart
(
product_id,
quantity,
"size",
user_id,
items
)
values
(
1,
1,
'Small',
1,
'[
{"size": "Small", "quantity": 1, "product_id": 1},
{"size": "Small", "quantity": 1, "product_id": 1},
{"size": "Small", "quantity": 1, "product_id": 1}
]'::jsonb
);
Example 2. (Convert fields of selected table to JSONB using row_to_json)
select row_to_json(t1) as your_json from (
select product_id, "size", quantity from cart
) t1
Result:
your_json
--------------------------------------------
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
Example 3. (Concat all rows of type JSONB)
select jsonb_agg(row_to_json(t1)) as your_json from (
select product_id, "size", quantity from cart
) t1
Result:
your_json
---------------------------------------------------------------------------------------------------------------------------------------------------------
[{"size": "Small", "quantity": 1, "product_id": 1}, {"size": "Small", "quantity": 1, "product_id": 1}, {"size": "Small", "quantity": 1, "product_id": 1}]
Example 4. (Similar of row_to_json)
select
json_build_object('product_id', product_id, 'size', size, 'quantity', quantity) as your_json
from cart;
Result:
your_json
--------------------------------------------
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
{"product_id":1,"size":"Small","quantity":1}
Example 5. (Concatenate many JSONB objects using ||)
select jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb) || jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb) as your_json
-- or
select jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb, '{"product_id":1,"size":"Small","quantity":1}'::jsonb) as your_json
Result:
your_json
------------------------------------------------------------------------------------------------------
[{"product_id": 1, "size": "Small", "quantity": 1}, {"product_id": 1, "size": "Small", "quantity": 1}]
Example 6. (Similar example to your update query (using concatenating JSONB types)
update cart
set
items = jsonb_build_array(items) || jsonb_build_array('{"product_id":1,"size":"Small","quantity":1}'::jsonb)
where id = 1
Result:
items
------------------------------------------------------------------------------------------------------
[{"product_id": 1, "size": "Small", "quantity": 1}, {"product_id": 1, "size": "Small", "quantity": 1}]
Recommended using JSON or JSONB types. In JSON types you can perform any operations on keys or values, such as filtering, selecting, joining. If you need you can even show JSON keys and values as table record view.
Examples:
items: '[
{"product_id": 1, "size": "Small", "quantity": 1},
{"product_id": 1, "size": "Small", "quantity": 1},
{"product_id": 1, "size": "Small", "quantity": 1}
]'
select
json_array_elements(items)->'product_id' as product_id,
json_array_elements(items)->'size' as "size",
json_array_elements(items)->'quantity' as quantity
from cart
where user_id = 1;
Result:
product_id
size
quantity
1
"Small"
1
1
"Small"
1
1
"Small"
1

not understand json_agg in this context

Reference: Query jsonb column containing array of JSON objects
begin;
CREATE TEMP TABLE segments (segments_id serial PRIMARY KEY, payload jsonb);
INSERT INTO segments (payload)
VALUES ('[{"kind": "person", "limit": "1"}, {"kind": "B", "filter_term": "fin"}]');
INSERT INTO segments (payload)
VALUES ('[{"kind": "person", "limit": "3"}, {"kind": "A", "filter_term": "abc"}]');
INSERT INTO segments (payload)
VALUES ('[{"kind": "person", "limit": "2"}, {"kind": "C", "filter_term": "def"}]');
commit;
CTE query:
with a as (select jsonb_array_elements(s.payload) j from segments s)
SELECT json_agg(a.j) AS filtered_payload from a
where j #> '{"kind":"person"}';
Return: [{"kind": "person", "limit": "1"}, {"kind": "person", "limit": "3"}, {"kind": "person", "limit": "2"}]
This QueryA:
SELECT a.filtered_payload,
a.ct_elem_row
, sum(ct_elem_row) OVER () AS ct_elem_total
, count(*) OVER () AS ct_rows
FROM segments s
JOIN LATERAL (
SELECT json_agg(j.elem) AS filtered_payload, count(*) AS ct_elem_row
FROM jsonb_array_elements(s.payload) j(elem)
WHERE j.elem #> '{"kind":"person"}'
) a ON ct_elem_row > 0
WHERE s.payload #> '[{"kind":"person"}]';
return :
In QueryA, the structure is like: select ... from segments s join lateral filtered_payload.... segments is 3 rows lateral join with one row (filtered_payload). filtered_payload return only row as per CTE query, as an a consolidate JSON array. So overall I am very confused with json_agg in the QueryA.
Edit at 2021-10-05 16:36 +5:30:
Even following code, a.filtered_payload return 3 jsonb array, instead of one arrgregate json array. I don't know when already aggregated jsonb array (using json_agg function) unnested to serveal jsonb arrays.
SELECT a.filtered_payload, s.*
FROM segments s
cross JOIN LATERAL (
SELECT json_agg(j.elem) AS filtered_payload
FROM jsonb_array_elements(s.payload) j(elem)
WHERE j.elem #> '{"kind":"person"}') a;
I believe the LATERAL JOIN is doing the trick there.
In your original query, you're using json_agg against the whole table dataset (filtered by '{"kind":"person"}')
with a as
(
select jsonb_array_elements(s.payload) j
from segments s
)
SELECT json_agg(a.j) AS filtered_payload
from a
where j #> '{"kind":"person"}';
Meanwhile in the second instance, you are playing with one row at the time using the LATERAL. That's why you end up having 3 rows with single "kind":"person" values instead of an unique row with 3 values.
Not sure about what you're trying to achieve but the following could put you in the right direction
SELECT a.filtered_payload,
a.ct_elem_row,
sum(ct_elem_row) OVER () AS ct_elem_total,
count(*) OVER () AS ct_rows
FROM segments s
JOIN LATERAL (
SELECT json_agg(j.elem) AS filtered_payload,
count(*) AS ct_elem_row
FROM segments d, lateral jsonb_array_elements(d.payload) j(elem)
WHERE j.elem #> '{"kind":"person"}'
) a ON ct_elem_row > 0
WHERE s.payload #> '[{"kind":"person"}]';
Results
filtered_payload | ct_elem_row | ct_elem_total | ct_rows
--------------------------------------------------------------------------------------------------------+-------------+---------------+---------
[{"kind": "person", "limit": "1"}, {"kind": "person", "limit": "3"}, {"kind": "person", "limit": "2"}] | 3 | 9 | 3
[{"kind": "person", "limit": "1"}, {"kind": "person", "limit": "3"}, {"kind": "person", "limit": "2"}] | 3 | 9 | 3
[{"kind": "person", "limit": "1"}, {"kind": "person", "limit": "3"}, {"kind": "person", "limit": "2"}] | 3 | 9 | 3
(3 rows)

postgreSQL json query and update/insert

I have json like this that based on the value of the enabled variable I want to insert into a table.
{
"Name": "Shop1",
"Id": "1",
"enabled": false
},
{
"Name": "Shop2",
"Id": "2",
"enabled": true
}
]
In a query I would like to select any items that are true and insert into a table. The table schema I want to insert into looks like
id,enabled
how to query the json and push into the rows of the other table?
You can use jsonb_array_elements() for that:
insert into the_table (id, enabled)
select (e ->> 'Id')::int,
(e ->> 'enabled')::boolean
from jsonb_array_elements('[
{"Name": "Shop1","Id": "1","enabled": false},
{"Name": "Shop2","Id": "2","enabled": true}
]') as t(e)
where e ->> 'enabled' = 'true';

Postgresql query array of objects in JSONB field filter Specific Object

CREATE TABLE company (id SERIAL, companyJson JSONB);
CREATE INDEX comapny_gin_idx ON company USING gin (companyJson);
INSERT INTO company (id, companyJson)
VALUES (1, '[{"name": "t", "company": "company1"}]');
INSERT INTO company (id, companyJson)
VALUES (2, '[{"name": "b", "company":"company2"}, {"name": "b", "company":"company3"}]');
SELECT * FROM company WHERE companyJson #> '[{"company": "company2" , "name": "b"}]';
The output of the above program is
2 [{"name": "b", "company": "company2"}, {"name": "b", "company": "company3"}]
Is there anyway to return {"name": "b", "company": "company2"} instead whole row.
I can only think of unnesting the array and the return the element from that:
SELECT x.j
FROM company c
cross join jsonb_array_elements(c.companyjson) as x(j)
where x.j = '{"company": "company2" , "name": "b"}'
You can directly return the first component through companyJson -> 0 which contains -> operand returning the first component by argument zero :
SELECT companyJson -> 0 as companyJson
FROM company
WHERE companyJson #> '[{"company": "company2" , "name": "b"}]';
Demo

How do I just select the "occupation"?

I have the following:
SELECT *
FROM (
SELECT '{"people": [{"name": "Bob", "occupation": "janitor"}, {"name": "Susan", "occupation": "CEO"}]}'::jsonb as data
) as b
WHERE data->'people' #> '[{"name":"Bob"}]'::jsonb;
I am filtering for the object '{"name": "Bob", "occupation": "janitor"}'
How do I return Bob's occupation ("janitor")?
SELECT data->'people'->>'occupation'
FROM (
SELECT '{"people": [{"name": "Bob", "occupation": "janitor"}, {"name": "Susan", "occupation": "CEO"}]}'::jsonb as data
) as b
WHERE data->'people' #> '[{"name":"Bob"}]'::jsonb;
returns
?column?
--------
NULL
Looking for:
occupation
----------
janitor
If you don't care about anything else on the row where the jsonb is, you can take all elements out of the jsonb and then use them as separate elements to select from
SELECT data->>'occupation' as occupation
FROM (
SELECT jsonb_array_elements(
'{"people":
[
{"name": "Bob", "occupation": "janitor"},
{"name": "Susan", "occupation": "CEO"}
]
}'::jsonb->'people') as data) as b
WHERE data #> '{"name":"Bob"}';
Results
occupation
-----------
janitor
(1 row)
Your "people" element is an array. You can get at the elements of an array with the jsonb_array_elements function. After that, you can just filter on person->>'name':
SELECT person->>'occupation' as occupation
FROM (
SELECT person.value as person
FROM (
SELECT
'{"people":
[
{"name": "Bob", "occupation": "janitor"},
{"name": "Susan", "occupation": "CEO"}
]
}'::jsonb as data
) a
CROSS JOIN
jsonb_array_elements(data->'people') as person
) b
WHERE person->>'name' = 'Bob';
Note that ->> returns text, while -> returns jsonb.