Ето решение, използващо PLV8 за вашата схема.
Първо изградете материализиран път, като използвате PLSQL функция и рекурсивни CTE.
CREATE OR REPLACE FUNCTION get_children(tag_id integer)
RETURNS json AS $$
DECLARE
result json;
BEGIN
SELECT array_to_json(array_agg(row_to_json(t))) INTO result
FROM (
WITH RECURSIVE tree AS (
SELECT id, name, ARRAY[]::INTEGER[] AS ancestors
FROM tags WHERE parent_id IS NULL
UNION ALL
SELECT tags.id, tags.name, tree.ancestors || tags.parent_id
FROM tags, tree
WHERE tags.parent_id = tree.id
) SELECT id, name, ARRAY[]::INTEGER[] AS children FROM tree WHERE $1 = tree.ancestors[array_upper(tree.ancestors,1)]
) t;
RETURN result;
END;
$$ LANGUAGE plpgsql;
След това изградете дървото от изхода на горната функция.
CREATE OR REPLACE FUNCTION get_tree(data json) RETURNS json AS $$
var root = [];
for(var i in data) {
build_tree(data[i]['id'], data[i]['name'], data[i]['children']);
}
function build_tree(id, name, children) {
var exists = getObject(root, id);
if(exists) {
exists['children'] = children;
}
else {
root.push({'id': id, 'name': name, 'children': children});
}
}
function getObject(theObject, id) {
var result = null;
if(theObject instanceof Array) {
for(var i = 0; i < theObject.length; i++) {
result = getObject(theObject[i], id);
if (result) {
break;
}
}
}
else
{
for(var prop in theObject) {
if(prop == 'id') {
if(theObject[prop] === id) {
return theObject;
}
}
if(theObject[prop] instanceof Object || theObject[prop] instanceof Array) {
result = getObject(theObject[prop], id);
if (result) {
break;
}
}
}
}
return result;
}
return JSON.stringify(root);
$$ LANGUAGE plv8 IMMUTABLE STRICT;
Това ще доведе до необходимия JSON, споменат във вашия въпрос. Надявам се, че това помага.
Написах подробна публикация/разбивка на това как работи това решение тук .