How to remove element from postgres jsonb by key value? - postgresql

I have a jsonb column with this format.
{
"categoryList": [{
"category_menu_id": "51",
"is_featured_product": 0
}, {
"category_menu_id": "54",
"is_featured_product": 1
}]
}
How to remove category by category_menu_id?
This select query is working fine by category_menu_id.
select product_category
from product
where product_category->'categoryList' #> '[{"category_menu_id": "51"}]';

Example data (note that I have added a primary key id to enable updating the table):
create table product(id int primary key, product_category jsonb);
insert into product values
(1,
'{
"categoryList": [{
"category_menu_id": "51",
"is_featured_product": 0
}, {
"category_menu_id": "54",
"is_featured_product": 1
}]
}');
This query skips the element with "category_menu_id": "51" from the json array:
select jsonb_build_object('categoryList', jsonb_agg(value))
from product,
jsonb_array_elements(product_category->'categoryList')
where value->>'category_menu_id' <> '51';
jsonb_build_object
--------------------------------------------------------------------------
{"categoryList": [{"category_menu_id": "54", "is_featured_product": 1}]}
(1 row)
Use the above query to update the table:
update product p
set product_category = (
select jsonb_build_object('categoryList', jsonb_agg(value))
from product ps,
jsonb_array_elements(product_category->'categoryList')
where ps.id = p.id -- important! primary key to identify a row
and value->>'category_menu_id' <> '51')
returning *;
id | product_category
----+--------------------------------------------------------------------------
1 | {"categoryList": [{"category_menu_id": "54", "is_featured_product": 1}]}
(1 row)

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.

Postgres SQL : rows to name, value json pairs

Given a row with Col1, Col2. How to convert the row into
[
{
"name": "Col1",
"value": "Column1Value"
},
{
"name": "Col2",
"value": "Column2Value"
}
]
``
You can use a scalar subselect:
select (select jsonb_agg(jsonb_build_object('name', key, 'value', value))
from jsonb_each(to_jsonb(d)) as j)
from the_table d
Here it is. Substitute the_table with your actual table name.
with jsonrows(jsonobject, rownumber) as
(
select to_json(t), row_number() over ()
from the_table t
)
select json_agg(json_build_object('name', key, 'value', value))
from jsonrows
cross join lateral json_each_text(jsonobject)
group by rownumber;

fetch 1 0r 0 for each records passed in where clause

I have a table with jsonb column datatype
+-----+--------------------------------------------------------------------------------+
| id | value |
+-----+--------------------------------------------------------------------------------+
| 1 | {"t1": "val", "value": [{"id": "1", "name": "abc"},{"id": "2", "name": "xyz"}] |
| 2 | {"t1": "val", "value": [{"id": "2", "name": "xyz"},{"id": "3", "name": "pqr"}] |
+-----+--------------------------------------------------------------------------------*
SELECT 'True' as status
FROM table t
where t.value->'value'->-1 -> 'id' IN ('"2"','"1"')
I want to fetch Ture or false along with each id checked in where clause.
Kindly help me in this query
You need to move the condition into the SELECT list, but you have to iterate over all array elements:
select t.id,
exists(select *
from jsonb_array_elements(t.value -> 'value') as v(item)
where item ->> 'id' in ('1','2')) as status
from the_table t;
With Postgres 12 or later, you can extract all IDs into an array and then use the ?| operator:
select t.id,
jsonb_path_query_array(t.value -> 'value', '$[*].id') ?| array['1','2'] as status
from the_table t;

Postgres - jsonb : Update key in column with value taken from another table

I am using postgres 9.5. I have a profile table, which lists the names:
public.profiles:
id | first_name | last_name
--- --------------- ---------------------
1 Jason Bourne
2 Jhonny Quest
I have an invoices table:
public.invoices:
invoice_id | billing_address | profile_id
------------------ ----------------------------- ---------------------
1 { 2
"address_line1": "445 Mount
Eden Road",
"city":"Mount Eden",
"country": "Auckland"
}
I want to update the billing_address column of the invoices table with the first_name and last_name from the profile table, like :
public.invoices:
invoice_id | billing_address | profile_id
------------------ ----------------------------- ---------------------
1 {
"name" : "Jhonny Quest" 2
"address_line1": "445 Mount
Eden Road",
"city":"Mount Eden",
"country": "Auckland"
}
To do so, I have tried using jsonb_set:
UPDATE invoices AS i SET billing_address = jsonb_set(billing_address,'{name}', SELECT t::jsonb FROM (SELECT CONCAT (p.first_name,p.middle_name, p.last_name) FROM profiles p WHERE p.id = i.profile_id)t )
It throws an error at SELECT. TBH I am not even sure if any of that statement is legal. Looking for any guidance.
Click: demo:db<>fiddle
UPDATE invoices i
SET billing_address = s.new_billing_address
FROM (
SELECT
i.invoice_id,
jsonb_set(
billing_address,
'{name}'::text[],
to_jsonb(concat_ws(' ', first_name, last_name))
) AS new_billing_address
FROM
invoices i
JOIN profiles p ON i.profile_id = p.id
) s
WHERE s.invoice_id = i.invoice_id;
Creating the SELECT with joining the second table; Afterwards you are able to create the new JSON object out of the name parts using to_jsonb() and the concat operator || (or concat_ws(), of course, as mentioned in the comments).

Merging an array of JSON objects to one JSON column in Postgres

I have two tables, products and products_ext that can be reduced essentially
to this basic form:
CREATE TABLE "products" (
"product_id" TEXT PRIMARY KEY
);
CREATE TABLE "products_ext" (
"product_id" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" JSON,
PRIMARY KEY ("product_id", "key"),
FOREIGN KEY ("product_id") REFERENCES "products"("product_id")
ON DELETE CASCADE
ON UPDATE CASCADE
);
Let us assume mock data
INSERT INTO "products" ("product_id") VALUES
('test1'),
('test2'),
('test3');
INSERT INTO "products_ext" (product_id, "key", "value") VALUES
('test1', 'foo', '"Foo"'),
('test1', 'bar', '"Bar"'),
('test2', 'foo', '"Foo"');
I can use a query
SELECT
"P"."product_id",
ARRAY(
SELECT
json_build_object(
"E"."key",
"E"."value"
)
FROM "products_ext" AS "E"
WHERE "E"."product_id" = "P"."product_id"
)
FROM
"products" AS "P";
which yields
product_id | array
------------+-----------------------------------------------
test1 | {"{\"foo\" : \"Foo\"}","{\"bar\" : \"Bar\"}"}
test2 | {"{\"foo\" : \"Foo\"}"}
but I cannot make it to yield a merged JSON. Is there an easy way in Postgres 10
to merge an array of multiple JSONs as one JSON that would yield?
product_id | json
------------+----------------------------------------
test1 | {\"foo\" : \"Foo\", \"bar\" : \"Bar\"}
test2 | {\"foo\" : \"Foo\"}
test3 | {}
Primary key pair "product_id" and "key" already make sure that there are no
key collisions. There may be rows in the products that do not have any data in products_ext and in those cases an empty JSON object should be provided.
demo:db<>fiddle
Use json_object_agg():
SELECT
p.product_id AS product_id,
json_object_agg(e.key, e.value)
FROM
products AS p
JOIN
products_ext AS e ON p.product_id = e.product_id
GROUP BY p.product_id;
Edit for empty product_ext values:
demo:db<>fiddle
SELECT
p.product_id AS product_id,
COALESCE(
json_object_agg(e.key, e.value) FILTER (WHERE e.key IS NOT NULL),
'{}'
)
FROM
products AS p
LEFT JOIN
products_ext AS e ON p.product_id = e.product_id
GROUP BY p.product_id;