I'm trying to create a JSON representing an arbitrarily deep and wide hierarchy, for example of creatures:
CREATE TABLE creatures (
name text PRIMARY KEY,
parent text REFERENCES creatures(name)
);
INSERT INTO creatures(name,parent)
VALUES
('amoeba',NULL),
('beetle','amoeba'),
('coelacanth','amoeba'),
('salmon','coelacanth'),
('tuna','coelacanth'),
('lizard','coelacanth'),
('t-rex','lizard'),
('plant',NULL);
I want to turn this into a JSON like this:
[{"name":"amoeba",
"children": [{"name": "beetle",
"children": []},
{"name": "coelacanth",
"children": [{"name": "tuna",
"children": []},
{"name": "salmon",
"children": []}
{"name": "lizard",
"children": [{"name": "t-rex",
"children": []}]}
]}]},
{"name": "plant",
"children": []}]
Is this possible to do in Postgres?
So far I've tried
WITH RECURSIVE r AS
-- Get all the leaf nodes, group by parent.
(SELECT parent,
json_build_object('name', parent,
'children', array_agg(name)) AS json
FROM creatures c
WHERE parent NOTNULL
AND NOT EXISTS
(SELECT 1
FROM creatures c2
WHERE c.name = c2.parent)
GROUP BY parent
UNION
-- Recursive term - go one step up towards the top.
SELECT c.parent,
json_build_object('name', c.parent,
'children', array_agg(c.name)) AS json
FROM r
JOIN creatures c ON r.parent = c.name
GROUP BY c.parent)
SELECT *
FROM r;
But it fails with
ERROR: aggregate functions are not allowed in a recursive query's recursive term
LINE 19: 'children', array_agg(c.name)) AS...
Is there any way to work around this, or another solution that can make me my nice tree?
creaturesdo not contain circular paths…