Cypher获得"独特最长的简单(非循环)"来自给定源节点

时间:2017-06-29 12:10:31

标签: neo4j cypher

我有这个简单的图表:

create (a:ent {id:'a'})-[:rel]->(:ent {id:'b'})-[:rel]->(c:ent {id:'c'})-[:rel]->(d:ent {id:'d'})-[:rel]->(:ent {id:'e'})-[:rel]->(a),
   (d)-[:rel]->(c),
   (c)-[:rel]->(f:ent {id:'f'})-[:rel]->(g:ent {id:'g'})-[:rel]->(a),
   (g)-[:rel]->(f)

看起来像这样:
enter image description here

鉴于是' a',即节点(:ent {id:'a'}),我想编写一个返回"正好两个唯一最长" 路径的查询:

a->b->c->d->e
a->b->c->f->g

正如你在这里看到的,我应该考虑图中的周期。似乎查询建议here应该没问题,我改写如下:

MATCH path=(:ent{id:'a'})-[:rel*]->(:ent)
WHERE ALL(n in nodes(path)
      WHERE 1=size(filter(m in nodes(path)WHERE m.id=n.id))
     )
RETURN path

我知道查询没有给出我想要得到的确切结果,但是如果我正确理解逻辑,它至少可以避免循环路径。我觉得这个查询可以是一个很好的起点。但它给了我一个奇怪的错误:

key not found:   UNNAMED26

这里有什么不对?我无法使用这种不明确的错误描述来查明密码中的错误。

更新

我尝试了一个新的更简单的查询:

MATCH path=(s:ent{id:'a'})-[:rel*]->(d:ent)
WHERE not (d)-[:rel]->() OR (d)-[:rel]->(s)
RETURN extract(x IN nodes(path)| x.id) as result

它返回:

╒═════════════════════╕
│result               │
╞═════════════════════╡
│[a, b, c, d, e]      │
├─────────────────────┤
│[a, b, c, d, c, f, g]│
├─────────────────────┤
│[a, b, c, f, g]      │
└─────────────────────┘

正如您所看到的,由于周期[a, b, c, d, c, f, g]周期,它会导致一条冗余路径(d)<->(c)。老实说,我觉得这篇文章中的原始/第一个查询应该消除它。有人可以告诉我如何才能让它发挥作用......?

1 个答案:

答案 0 :(得分:3)

您可以使用apoc.path.expandConfig查找路径,然后过滤掉最短的路径,只留下最长的路径。

// start with the a node
MATCH (a:ent {id :"a"})

// use expandConfig with the relationship type and direction
// use uniqueness of NODE_PATH to ensure that you don't backtrack
// optionally include a minlevel, maxlevel
CALL apoc.path.expandConfig(a, 
{
  relationshipFilter: 'rel>',
  uniqueness: 'NODE_PATH',
  minLevel: 2,
  maxLevel: 10
} ) yield path

// collect the paths and the get the max length
WITH COLLECT(path) AS paths, MAX(length(path)) AS longest_length

// remove any of the paths that are not the max length
WITH FILTER(path IN paths WHERE length(path)= longest_length) AS longest_paths

// return each path matching the max length
UNWIND longest_paths AS path
RETURN path

已更新:根据OP的说明进行替代回答。

// alternate set of test data based on OP's comments
MERGE (a:ent {id:'a'})-[:rel]->(b:ent {id:'b'})-[:rel]->(c:ent {id:'c'})
MERGE (b)-[:rel]->(d:ent {id:'d'})
MERGE (b)-[:rel]->(e:ent {id:'e'})-[:rel]->(f:ent {id:'f'})
RETURN *

更新的查询

// start with the a node
MATCH (a:ent {id :"a"})

// use expandConfig with the relationship type and direction
// use uniqueness of NODE_PATH to ensure that you don't backtrack
// optionally include a minlevel, maxlevel
CALL apoc.path.expandConfig(a, 
{
  relationshipFilter: 'rel>',
  uniqueness: 'NODE_PATH',
  minLevel: 1,
  maxLevel: 10
} ) yield path

// create two collections: one of nodes of full paths and the other nodes of paths shorted by one
WITH COLLECT(path) AS paths,
     COLLECT(DISTINCT nodes(path)[0..size(nodes(path))-1]) AS all_but_last_nodes_of_paths

// filter out the nodes of paths that match nodes in the shorted list
// i.e. the paths that are already included in another list
WITH [p IN paths WHERE NOT nodes(p) IN all_but_last_nodes_of_paths] AS paths_to_keep

// return each path
UNWIND paths_to_keep AS path
RETURN path