Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带Postgres的JSON图的邻接列表_Json_Postgresql_Adjacency List - Fatal编程技术网

带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);