带Postgres的JSON图的邻接列表
我对标记表有以下模式:带Postgres的JSON图的邻接列表,json,postgresql,adjacency-list,Json,Postgresql,Adjacency List,我对标记表有以下模式: CREATE TABLE tags ( id integer NOT NULL, name character varying(255) NOT NULL, parent_id integer ); 我需要构建一个查询来返回以下结构(为了可读性,这里用yaml表示): 经过反复试验,并在SO中寻找类似问题后,我提出了以下问题: WITH RECURSIVE tagtree AS ( SELECT tags.name, tags
CREATE TABLE tags (
id integer NOT NULL,
name character varying(255) NOT NULL,
parent_id integer
);
我需要构建一个查询来返回以下结构(为了可读性,这里用yaml表示):
经过反复试验,并在SO中寻找类似问题后,我提出了以下问题:
WITH RECURSIVE tagtree AS (
SELECT tags.name, tags.parent_id, tags.id, json '[]' children
FROM tags
WHERE NOT EXISTS (SELECT 1 FROM tags tt WHERE tt.parent_id = tags.id)
UNION ALL
SELECT (tags).name, (tags).parent_id, (tags).id, array_to_json(array_agg(tagtree)) children FROM (
SELECT tags, tagtree
FROM tagtree
JOIN tags ON tagtree.parent_id = tags.id
) v
GROUP BY v.tags
)
SELECT array_to_json(array_agg(tagtree)) json
FROM tagtree
WHERE parent_id IS NULL
但转换为yaml时会返回以下结果:
- name: Ciencia
parent_id:
id: 7
children:
- name: Química
parent_id: 7
id: 9
children: []
- name: Ciencia
parent_id:
id: 7
children:
- name: Biología
parent_id: 7
id: 8
children:
- name: Botánica
parent_id: 8
id: 19
children: []
- name: Etología
parent_id: 8
id: 18
children: []
根节点是重复的。
我可以将结果合并到我的应用程序代码中的预期结果,但我觉得我很接近了,这可以从PG完成
下面是一个使用SQL FIDLE的示例:
预期产出:
实际产量:
试试PL/Python和networkx 诚然,使用下面的代码并不能生成完全符合请求格式的JSON,但是信息似乎都在那里,如果PL/Python是可以接受的,那么这可能会被改编成一个完整的答案
CREATE OR REPLACE FUNCTION get_adjacency_data(
names text[],
ids integer[],
parent_ids integer[])
RETURNS jsonb AS
$BODY$
pairs = zip(ids, parent_ids)
import networkx as nx
import json
from networkx.readwrite import json_graph
name_dict = dict(zip(ids, names))
G=nx.DiGraph()
G.add_nodes_from(ids)
nx.set_node_attributes(G, 'name', name_dict)
G.add_edges_from(pairs)
return json.dumps(json_graph.adjacency_data(G))
$BODY$ LANGUAGE plpythonu;
WITH raw_data AS (
SELECT array_agg(name) AS names,
array_agg(parent_id) AS parent_ids,
array_agg(id) AS ids
FROM tags
WHERE parent_id IS NOT NULL)
SELECT get_adjacency_data(names, parent_ids, ids)
FROM raw_data;
这里有一个使用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;
CREATE或REPLACE函数get_tree(数据json)将json返回为$$
var根=[];
用于(数据中的var i){
构建_树(数据[i]['id'],数据[i]['name'],数据[i]['children']);
}
函数生成树(id、名称、子级){
var exists=getObject(根,id);
如果(存在){
存在['children']=子对象;
}
否则{
push({'id':id,'name':name,'children':children});
}
}
函数getObject(对象,id){
var结果=null;
if(数组的对象实例){
对于(变量i=0;i
这将产生您问题中提到的所需JSON。希望有帮助
我已经写了一篇关于这个解决方案工作原理的详细文章。我正在寻找同样的解决方案,也许这个例子对任何人都有用 在具有相同结构的表格的10级Postgres上测试 列为:id、name和pid作为父项的表
create or replace function get_c_tree(p_parent int8) returns setof jsonb as $$
select
case
when count(x) > 0 then jsonb_build_object('id', c.id, 'name', c.name, 'children', jsonb_agg(f.x))
else jsonb_build_object('id', c.id, 'name', c.name, 'children', null)
end
from company c left join get_c_tree(c.id) as f(x) on true
where c.pid = p_parent or (p_parent is null and c.pid is null)
group by c.id, c.name;
$$ language sql;
select jsonb_agg(get_c_tree) from get_c_tree(null::int8);
你能发布一些示例记录吗?我发布了一个SQL Fiddle示例,并给出了预期和实际输出。感谢数据结构良好的问题。。。我试过了,但无法解决:-(不过,这里有一个链接可能会进一步帮助你…谢谢你的链接!我会检查一下。似乎使用postgres为复杂的图形生成json不是一个好办法。在应用程序端更容易维护。我仍然渴望看到如何解决这个问题。这是史诗般的!
create or replace function get_c_tree(p_parent int8) returns setof jsonb as $$
select
case
when count(x) > 0 then jsonb_build_object('id', c.id, 'name', c.name, 'children', jsonb_agg(f.x))
else jsonb_build_object('id', c.id, 'name', c.name, 'children', null)
end
from company c left join get_c_tree(c.id) as f(x) on true
where c.pid = p_parent or (p_parent is null and c.pid is null)
group by c.id, c.name;
$$ language sql;
select jsonb_agg(get_c_tree) from get_c_tree(null::int8);