How to reverse query for a record, returning all of its parent and their parents? - postgresql

Suppose the following,
create schema if not exists my_schema;
CREATE TABLE IF NOT EXISTS my_schema.category (
id serial PRIMARY KEY,
category_name VARCHAR (255) NOT NULL,
subcategories BIGINT[] DEFAULT ARRAY[]::BIGINT[]
);
INSERT INTO my_schema.category VALUES
(
1,
'Pickup/dropoff',
'{}'
),
(
2,
'Electrician',
'{}'
),
(
3,
'Around the house',
'{2}'
),
(
4,
'Personal',
'{3}'
);
I'm trying to create breadcrumbs on the frontend. For instance, if Electrician is the selected category, I'd like to render something like Personal > Around the house > Electrician.
Ideally, I'd like the query to return something like,
{
"id": 2,
"breadcrumbs": "Personal.Around the house.Electrician"
}
This way, I could just do breadcrumbs.split('.').map(. . .).
Another result that would work is something like,
{
"id": 2,
"category_name": "Electrician"
"breadcrumbs": [
{
"id": 4,
"category_name": "Personal"
},
{
"id": 3,
"category_name": "Around the house"
},
]
}
Or even,
{
"breadcrumbs": [
{
"id": 4,
"category_name": "Personal"
},
{
"id": 3,
"category_name": "Around the house"
},
{
"id": 2,
"category_name": "Electrician"
}
]
}
How can I attain such a result?

You can do it using WITH RECURSIVE as follows :
to get any breadcrumbs just put its id on the first select where id = 2
WITH RECURSIVE cte(n, id, selectedCat) AS
(
SELECT 1, id, id::text
from category
where id = 2
UNION ALL
SELECT n+1, e.id, ep.selectedCat
FROM cte AS ep JOIN category AS e
ON ep.id = ANY (e.subcategories)
)
SELECT selectedCat, string_agg(category_name, ',') as breadcrumbs FROM (
SELECT selectedCat, category_name
FROM cte
inner join category c on c.id = cte.id
order by n desc
) AS s
GROUP BY selectedCat
demo here

Related

DB2 Avoid Duplicates in JSON_ARRAY

I am using DB2LUW 11.5.
I build a json and gets an output like this
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 10
}, {
"ID": 1,
"SIZE": 20
}
]
}
But I want the id from B_OBJECTS listed only once.
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 10
}
]
}
Here is my query...
WITH TABLE_A(ID,NAME) AS (
VALUES (1, 'a')),
TABLE_B(ID, ID_A, SIZE) AS (
VALUES (1, 1, 10), (1, 1, 20)),
JSON_STEP_1 AS (
SELECT A.ID AS A_ID, A.NAME AS A_NAME, B.ID AS B_ID,
JSON_OBJECT('ID' VALUE B.ID, 'SIZE' VALUE B.SIZE) B_JSON
FROM TABLE_A A
JOIN TABLE_B B ON B.ID_A = A.ID
GROUP BY A.ID, A.NAME, B.ID, B.SIZE),
JSON_STEP_2 AS (
SELECT JSON_OBJECT ('ID' VALUE A_ID,
'NAME' VALUE A_NAME,
'B_OBJECTS' VALUE JSON_ARRAY (LISTAGG(B_JSON, ', ') WITHIN GROUP (ORDER BY B_ID) FORMAT JSON) FORMAT JSON
) JSON_OBJS
FROM JSON_STEP_1
GROUP BY A_ID, A_NAME
)
SELECT JSON_ARRAY (SELECT JSON_OBJS FROM JSON_STEP_2 FORMAT JSON) FROM SYSIBM.SYSDUMMY1;
I just updated the query with additional table TABLE_C
WITH
TABLE_A(ID,NAME) AS
(
VALUES (1, 'a')
)
, TABLE_B(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 10), (1, 1, 20), (2, 1, 10), (2, 1, 20)
), TABLE_C(ID, ID_A, SIZE) AS
(
VALUES (1, 1, 5), (2,1,10), (3,1,15)
)
, JSON_STEP_1 AS
(
SELECT A_ID, A_NAME, B_ID
, JSON_OBJECT('ID' VALUE B_ID, 'SIZE' VALUE B_SIZE) B_JSON
, JSON_OBJECT('ID' VALUE C_ID, 'SIZE' VALUE C_SIZE) C_JSON
FROM
(
SELECT
A.ID AS A_ID, A.NAME AS A_NAME, B.ID AS B_ID, B.SIZE AS B_SIZE, C.ID AS C_ID, C.SIZE AS C_SIZE
, ROW_NUMBER () OVER (PARTITION BY B.ID, B.ID_A, B.SIZE) AS RN_
, ROW_NUMBER () OVER (PARTITION BY C.ID, C.ID_A, C.SIZE) AS RN1_
FROM TABLE_A A
JOIN TABLE_B B ON B.ID_A = A.ID
JOIN TABLE_C C ON C.ID_A = A.ID
)
WHERE RN_ = 1 AND RN1_ = 1
GROUP BY A_ID, A_NAME, B_ID, B_SIZE, B_ID, B_SIZE, C_ID, C_SIZE
)
, JSON_STEP_2 AS
(
SELECT
JSON_OBJECT
(
'ID' VALUE A_ID,
'NAME' VALUE A_NAME,
'B_OBJECTS' VALUE JSON_ARRAY (LISTAGG(B_JSON, ', ') WITHIN GROUP (ORDER BY B_ID) FORMAT JSON) FORMAT JSON,
'C_OBJECTS' VALUE JSON_ARRAY (LISTAGG(C_JSON, ', ') WITHIN GROUP (ORDER BY B_ID) FORMAT JSON) FORMAT JSON
) JSON_OBJS
FROM JSON_STEP_1
GROUP BY A_ID, A_NAME
)
SELECT JSON_ARRAY (SELECT JSON_OBJS FROM JSON_STEP_2 FORMAT JSON) FROM SYSIBM.SYSDUMMY1
The output should be like
{
"ID": 1,
"NAME": "a",
"B_OBJECTS": [{
"ID": 1,
"SIZE": 10
},
{
"ID": 1,
"SIZE": 20
},
{
"ID": 2,
"SIZE": 10
},
{
"ID": 2,
"SIZE": 20
}
],
"C_OBJECTS": [{
"ID": 1,
"SIZE": 5
},
{
"ID": 2,
"SIZE": 10
},
{
"ID": 3,
"SIZE": 15
}
]
}
WITH
TABLE_A (ID,NAME) AS
(
VALUES (1, 'a')
)
, TABLE_B (ID, ID_A, SIZE) AS
(
VALUES (1, 1, 10), (1, 1, 10), (2, 1, 10), (2, 1, 20)
)
, TABLE_C (ID, ID_A, SIZE) AS
(
VALUES (1, 1, 5), (2, 1, 10), (3, 1, 15)
)
SELECT
JSON_OBJECT
(
'ID' VALUE A.ID
, 'NAME' VALUE A.NAME
, 'B_OBJECTS' VALUE
JSON_ARRAY
(
(
SELECT JSON_OBJECT ('ID' VALUE ID, 'SIZE' VALUE SIZE FORMAT JSON)
FROM TABLE (SELECT DISTINCT ID, SIZE FROM TABLE_B B WHERE B.ID_A = A.ID ORDER BY ID)
)
FORMAT JSON
) FORMAT JSON
, 'C_OBJECTS' VALUE
JSON_ARRAY
(
(
SELECT JSON_OBJECT ('ID' VALUE ID, 'SIZE' VALUE SIZE FORMAT JSON)
FROM TABLE (SELECT DISTINCT ID, SIZE FROM TABLE_C C WHERE C.ID_A = A.ID ORDER BY ID)
)
FORMAT JSON
) FORMAT JSON
) JSON_OBJ
FROM TABLE_A A
{
"ID":1,"NAME":"a"
,"B_OBJECTS":
[
{"ID":1,"SIZE":10},
{"ID":2,"SIZE":10},
{"ID":2,"SIZE":20}
]
,"C_OBJECTS":
[
{"ID":1,"SIZE":5},
{"ID":2,"SIZE":10},
{"ID":3,"SIZE":15}
]
}

PostgreSQL how to merge jsonb keys and count values

I have a table that looks somthing like the following:
id
name
category
specs (jsonb)
1
product1
phones
{ "brand": "brand1", "color": "red", "size": 5, "memory": "8GB"}
2
product2
phones
{ "brand": "brand1", "color": "white", "size": 7, "memory": "8GB"}
3
product3
laptops
{ "brand": "brand20", "storage": "SSD", "os": "os1" , "memory": "32GB"}
My desired output given a specific category
{
"brand": {
"brand1": 1,
"brand2": 1
},
"color": {
"red": 1,
"white": 5,
},
"memory": {
"8gb": 2,
}
}
Blow out the specs column with jsonb_each_text(), calculate the counts, and then reassemble with jsonb_object_agg() (the first two CTEs could be combined into one, but I left them verbose for illustration):
with blowout as (
select s.category, j.key, j.value
from somthing s
cross join lateral jsonb_each_text(s.specs) as j(key, value)
), counts as (
select category, key, value, count(1) as cnt
from blowout
group by category, key, value
), agg_specs as (
select category, key, jsonb_object_agg(value, cnt) as counts
from counts
group by category, key
)
select category, jsonb_object_agg(key, counts) as output
from agg_specs
group by category
;
db<>fiddle here

Copy JSONB property data within a collection

I have a JSONB column with data as follows:
{
"foo": {
"bars": [
{ "propA": "abc" },
{ "propA": "def" }
]
}
}
For each element in bars, I'm trying to copy the value from propA into a new propB property. Basically the resulting JSON needs to look like this:
{
"foo": {
"bars": [
{ "propA": "abc", "propB": "abc" },
{ "propA": "def", "propB": "def" }
]
}
}
Is this possible to do via an UPDATE statement?
Thank you.
dbfiddle link for demonstration.
SQL that will change the jsonb object:
SELECT id,
Jsonb_set(final_inline_view.contentx, '{foo,bars}',
final_inline_view.synthesized_jsonb_agg) changed_jsonb
FROM (SELECT id,
contentx,
Jsonb_agg (Jsonb_build_object('PropA', z.x, 'PropB', z.x))
synthesized_jsonb_agg
FROM (SELECT jsonsettest.id,
jsonsettest.content contentx,
Jsonb_array_elements(Jsonb_path_query_array(content,
'$.foo.bars[0,1]'))
->>
'propA' x
FROM jsonsettest) z
GROUP BY id,
contentx) final_inline_view;
Update (final):
UPDATE jsonsettest a
SET content = b.changed_jsonb
FROM (SELECT id,
Jsonb_set(final_inline_view.contentx, '{foo,bars}',
final_inline_view.synthesized_jsonb_agg) changed_jsonb
FROM (SELECT id,
contentx,
Jsonb_agg (Jsonb_build_object('PropA', z.x, 'PropB',
z.x))
synthesized_jsonb_agg
FROM (SELECT jsonsettest.id,
jsonsettest.content contentx,
Jsonb_array_elements(Jsonb_path_query_array(
content,
'$.foo.bars[0,1]'))
->>
'propA' x
FROM jsonsettest) z
GROUP BY id,
contentx) final_inline_view) b
WHERE a.id = b.id;

How to return data in JSON hierarchy?

For the following data tables and function in pg12
create table orders
(
orderid integer, grandtotal numeric(10, 2)
)
create table odetails
(
orderid integer, detailid integer, description text
)
create function jorder() returns json as
begin
return query select od.orderid, od.grandtotal, ds.detailid, ds.description
from orders od
join odetails ds on od.orderid = ds.orderid;
end;
How do I get return data in a JSON hierarchy like below?
[{
"orderid": 1,
"grandtotal": 100.00,
"details": [{
"detailid": 11,
"description": "pen"
},
{
"detailid": 12,
"description": "orange"
}
]
}, {
"orderid": 2,
"grandtotal": 200.00,
"details": [{
"detailid": 21,
"description": "book"
},
{
"detailid": 22,
"description": "coffee"
},
{
"detailid": 23,
"description": "tea"
}
]
}]
You should look into json functions . You need json_build_object to form the objects and json_agg to aggregate them into json array:
CREATE FUNCTION jorder()
RETURNS json
LANGUAGE sql
AS $$
SELECT
json_agg(orders.order)
FROM (
SELECT
json_build_object(
'orderid', od.orderid,
'grandtotal', od.grandtotal,
'details', array_agg(
json_build_object(
'detailid', ds.detailid,
'description', ds.description
)
)
) as order
FROM
orders od
JOIN odetails ds on od.orderid = ds.orderid
GROUP BY
od.orderid, od.grandtotal
) as orders
$$;
Example at db<>fiddle

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