如何在一个查询中检索树中每个节点的前n个子节点

时间:2018-07-15 22:07:30

标签: database neo4j orientdb arangodb

我最近正在根据一项特定要求评估图形数据库或任何其他数据库:

在一个查询中,可以通过树中节点的直接子代及其所有直接子和间接子代的聚合属性来检索每个节点的前n个子代。结果应返回正确的层次结构。

示例

root + 11
          ++ 111
             ++ 1111
          ++ 112
             ++ 1121
             ++ 1122
             ++ 1123
          ++ 113
     + 12
          ++ 121
          ++ 122
             ++ 1221
             ++ 1222
             ++ 1223
          ++ 123
     + 13
         ++ 131
         ++ 132
         ++ 133
         ++ 134
     + 14

Each node has a property of how many direct children it has. And the tree has no more than 8 levels。假设我要按每个级别的所有节点运行整个树的查询,该节点的前2个子节点具有最直接和最间接的子节点。它会给我们以下内容:

root + 11
          ++ 111
             ++ 1111
          ++ 112
             ++ 1121
             ++ 1122
     + 12
          ++ 121
          ++ 122
             ++ 1221
             ++ 1222

我想知道是否有任何图形数据库或任何其他有效支持这种查询的数据库,如果可以,怎么办?

1 个答案:

答案 0 :(得分:1)

使用Neo4j

您可以使用Neo4j进行此操作,但是您需要确保使用APOC Procedures插件来访问某些地图和收集功能以及过程。

首先要注意的一件事。当子节点的子节点数之间有联系时,您没有定义在子节点之间进行选择时要使用的任何条件。因此,以下结果可能与您的结果不完全匹配,因为可能已选择了备用节点(具有绑定计数)。如果确实需要其他标准来进行订购和选择,则必须将其添加到描述中,以便我可以相应地修改查询。

创建测试图

首先,让我们创建测试数据集。我们可以通过Neo4j浏览器来做到这一点。

首先让我们设置创建图形所需的参数:

:param data => [{id:11, children:[111, 112, 113]}, {id:12, children:[121, 122, 123]}, {id:13, children:[131,132,133,134]}, {id:14, children:[]}, {id:111, children:[1111]}, {id:112, children:[1121, 1122, 1123]}, {id:122, children:[1221,1222,1223]}]

现在,我们可以使用此查询来使用这些参数来创建图形:

UNWIND $data as row
MERGE (n:Node{id:row.id})
FOREACH (x in row.children |
 MERGE (c:Node{id:x})
 MERGE (n)-[:CHILD]->(c))

我们正在处理类型为:Node的节点,这些节点通过:CHILD关系相互连接,并向叶节点传出。

让我们还在顶层添加一个:Root:Node,以使我们以后的一些查询更加容易:

MERGE (r:Node:Root{id:0})
WITH r
MATCH (n:Node)
WHERE NOT ()-[:CHILD]->(n)
MERGE (r)-[:CHILD]->(n)

:Root节点现在已连接到顶部节点(11、12、13、14),我们的测试图已准备就绪。

实际查询

因为您要的聚合需要一个节点的所有后代的计数,而不仅仅是其直接子节点的计数,所以我们不能使用节点具有多少个直接子节点的子计数属性。或者更确切地说,我们可以对节点所有后代的计数求和,但是由于这要求我们无论如何都要遍历所有后代,因此更容易获得所有后代的计数并完全避免属性访问。

下面是完整的查询,您应该可以在测试图上运行完整的查询。我将其分为换行符和注释,以更好地显示每个部分的功能。

// for each node and its direct children, 
// order by the child's descendant count
MATCH (n:Node)-[:CHILD]->(child)
WITH n, child, size((child)-[:CHILD*]->()) as childDescCount
ORDER BY childDescCount DESC
// now collect the ordered children and take the top 2 per node
WITH n, collect(child)[..2] as topChildren

// from the above, per row, we have a node and a list of its top 2 children.
// we want to gather all of these children into a single list, not nested
// so we collect the lists (to get a list of lists of nodes), then flatten it with APOC
WITH apoc.coll.flatten(collect(topChildren)) as topChildren

// we now have a list of the nodes that can possibly be in our path
// although some will not be in the path, as their parents (or ancestors) are not in the list
// to get the full tree we need to match down from the :Root node and ensure
// that for each path, the only nodes in the path are the :Root node or one of the topChildren
MATCH path=(:Root)-[:CHILD*]->()
WHERE all(node in nodes(path) WHERE node:Root OR node in topChildren)

RETURN path

没有注释,这只是8行查询。

现在,这实际上返回了多条路径,每行一条路径,如果查看图形结果,所有路径的全部共同创建您想要的视觉树。

enter image description here

以JSON格式获取结果

但是,如果不使用可视化工具以图形方式查看结果,则可能需要树的JSON表示形式。我们可以通过收集所有结果路径并使用APOC中的过程来生成JSON树结构来实现这一点。这是一个稍作修改的查询,其中包含以下更改:

MATCH (n:Node)-[:CHILD]->(child)
WITH n, child, size((child)-[:CHILD*]->()) as childDescCount
ORDER BY childDescCount DESC
WITH n, collect(child)[..2] as topChildren
WITH apoc.coll.flatten(collect(topChildren)) as topChildren
MATCH path=(:Root)-[:CHILD*]->()
WHERE all(node in nodes(path) WHERE node:Root OR node in topChildren)
// below is the new stuff to get the JSON tree
WITH collect(path) as paths
CALL apoc.convert.toTree(paths) YIELD value as map
RETURN map

结果将类似于:

{
  "_type": "Node:Root",
  "_id": 52,
  "id": 0,
  "child": [
    {
      "_type": "Node",
      "_id": 1,
      "id": 12,
      "child": [
        {
          "_type": "Node",
          "_id": 6,
          "id": 122,
          "child": [
            {
              "_type": "Node",
              "_id": 32,
              "id": 1223
            },
            {
              "_type": "Node",
              "_id": 31,
              "id": 1222
            }
          ]
        },
        {
          "_type": "Node",
          "_id": 21,
          "id": 123
        }
      ]
    },
    {
      "_type": "Node",
      "_id": 0,
      "id": 11,
      "child": [
        {
          "_type": "Node",
          "_id": 4,
          "id": 111,
          "child": [
            {
              "_type": "Node",
              "_id": 26,
              "id": 1111
            }
          ]
        },
        {
          "_type": "Node",
          "_id": 5,
          "id": 112,
          "child": [
            {
              "_type": "Node",
              "_id": 27,
              "id": 1121
            },
            {
              "_type": "Node",
              "_id": 29,
              "id": 1123
            }
          ]
        }
      ]
    }
  ]
}