【问题标题】:Neo4J path subquery for versioned nodes版本化节点的 Neo4J 路径子查询
【发布时间】:2017-02-23 19:17:22
【问题描述】:

example graph(对不起,您需要点击图片)

我有一个包含两种节点的图:

  • 身份节点作为结构元素(蓝色)不包含任何属性
  • 版本化的值节点,包含属性/数据(橙色)

有两种关系类型:

  • version-of:将 value-nodes 连接到 identity-nodes,并包含一个窗口,在该窗口内关系应有效(图片仅显示开始日期)
  • parent-of:以分层方式连接身份节点

场景:

  • 在任何给定时间,每个身份节点最多有一个有效值节点。
  • 我想保留历史值并随时回顾,因此将当前有效的值复制到相应的身份节点将不起作用。
  • 层次结构可以是任意深度,例如 1 到 10。
  • 我不想使用值节点的属性使用从根到叶的完全限定路径字符串进行搜索。
  • 我想找到沿 ID 节点的路径以及路径末尾的正确 value-of。 (想想文件系统目录路径。)

在此示例中,我正在搜索路径“A/CD/E”,其中 A CD E 是值节点的名称属性的值。基本上我想找到正确的“E”节点,这里是与 98 相关联的节点。下面的示例省略了时间以保持简短。

我该怎么做?这甚至可能只使用 Neo4J 吗? (很容易将查询包装在另一个脚本中,该脚本生成路径匹配,包括每个路径条目的命名 id 节点。)

我可以查询路径的开始和结束来限制搜索空间,但是中间的节点数量可变呢?在某种程度上,ID 节点必须在每次查询之前与它们的值合并,以匹配关系版本的时间窗口。

MATCH p=(start:value) -[:`version-of`]-> (:ID) <-[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE start.name = 'A' AND end.name = 'E'
RETURN p

我想问什么(使用可变深度键而不是简短示例);以下是无效的密码:

MATCH p=(start:value) -[:`version-of`]-> (:ID) <-[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE p = 'A/CD/E' // or something longer, "A/CD/E/F/X"
RETURN p, end

到目前为止我所看到的

我还可以沿路径构建名称列表,并将完整路径与查询字符串进行比较。但它无法获取 start 和 end 之间的节点的名称值:

MATCH p=(start:value) -[:`version-of`]-> (:ID) <-[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE start.name = 'A' AND end.name = 'E'
RETURN EXTRACT(n in nodes(p)|n['name'])

与查询相关的成对子图,但缺少路径。我可以在这里以某种方式获得 id,val 对的路径吗?

MATCH p=(start:value) -[:`version-of`]-> (:ID) <-[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE start.name = 'A' AND end.name = 'E'
UNWIND nodes(p) as id
MATCH (id) <-[:`version-of`]- (val)
RETURN COLLECT([id,val])

这是图表

MATCH (n) DETACH DELETE n
CREATE (:value {name:'A'}) -[:`version-of` {from:'2017-01-01'}]-> (:ID {id:32})
CREATE (:value {name:'CC'}) -[:`version-of` {from:'2017-01-05'}]-> (:ID {id:21})
CREATE (:value {name:'E'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:98})
CREATE (:value {name:'E'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:48})
MATCH (n:ID {id:21}) CREATE (:value {name:'CD'}) -[:`version-of` {from:'2017-03-01'}]-> (n)
MATCH (a:ID {id:32}),(b:ID {id:21}) CREATE (a)<-[:`parent-of`]-(b)
MATCH (a:ID {id:21}),(b:ID {id:98}) CREATE (a)<-[:`parent-of`]-(b)
MATCH (a:ID {id:32}),(b:ID {id:48}) CREATE (a)<-[:`parent-of`]-(b)

【问题讨论】:

  • 什么都不清楚。显示这些测试数据的预期结果。
  • 就在题为“我想问什么”的伪代码片段上。我希望我现在也设法在文本中更清楚地说明了这一点。
  • 在您的图形创建密码中,值“E”和 :ID 48 之间有两个相同的行。我猜其中一个可以删除?
  • 确实如此。删除,复制+粘贴错误

标签: neo4j cypher


【解决方案1】:

你已经很接近了!

您的最后一个带有 id/val 对的查询缺少路径,因为您在没有任何上下文的情况下进行收集:

RETURN COLLECT([id,val])

这只是要求一个 id/val 对的集合。如果您希望为每个路径收集 id/val 对,则需要将路径变量 p 作为非聚合字段,它将充当聚合的分组键:

WITH p, COLLECT([id,val]) as pairs  // one collection per path
RETURN pairs

至于确保CD在路径中,我们需要找到CD的:ID节点,并添加谓词确保该节点在路径中:

MATCH (:value{name:'CD'})-[:`version-of`]->(cdID:ID)
MATCH p=(start:value) -[:`version-of`]-> (:ID) <-[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE start.name = 'A' AND end.name = 'E' AND cdID in nodes(p)
UNWIND nodes(p)[1..-1] as id
MATCH (id) <-[:`version-of`]- (val)
WITH p, COLLECT([id,val]) as pairs
RETURN pairs

编辑

现在明确的要求包括指定 :value 节点的名称,这些节点与路径上的任何 :ID 相关联,大概是按顺序排列的。

让我们看看这个是否可行。我将使用您现有的图表(删除重复的 E-for-id-48 行),仅在此示例中,我们将省略 :parent-of 关系的方向,因为我们没有现有方向足够的节点在中间路径中有多个元素。让我们从 E 到 E,按顺序要求以下值节点的 ID:'E'、'A'、'CD'、'E'。通过我们的图表,我们希望获得以下与值名称关联的 ID 的单一路径:48、32、21、98

这将是一个复杂的查询,因为在 Cypher 中处理索引并不是那么简单。这也是一个我们过滤所有可能路径的查询,因为我看不到在扩展期间过滤的方法:

WITH ['E', 'A', 'CD', 'E'] as names
UNWIND RANGE(0, SIZE(names)-1) as index
WITH index, names[index] as name
// each name with its expected index in the path

MATCH (:value{name:name})-[:`version-of`]->(id:ID)
WITH index, name, COLLECT(id) as ids
ORDER BY index ASC
WITH COLLECT({name:name, ids:ids}) as pathdef
// index-order collection of expected name and possible IDs for the name

MATCH p=(start:value) -[:`version-of`]-> (:ID) -[:`parent-of`*]- (:ID) <-[:`version-of`]- (end:value)
WHERE start.name = 'E' AND end.name = 'E' AND LENGTH(p)-1 = SIZE(pathdef)
// we have all paths from E to E of the desired length

// only interested in :ID nodes in match, leave off :value nodes at end
WITH pathdef, NODES(p)[1..-1] as nodes, RANGE(0, SIZE(pathdef)-1) as indices
// due to our match from start and end values, don't need to check start/end :ID nodes
WHERE ALL(index in indices[1..-1] WHERE nodes[index] in pathdef[index].ids)
// we've filtered to only path nodes associated with values in expected order
UNWIND indices as index
RETURN COLLECT([nodes[index], pathdef[index].name]) as pairs

【讨论】:

  • 谢谢。这确实是一个我没有看过的方法。但这仅适用于在 A 和 E 之间具有单个节点的示例,但不适用于介于两者之间的任意数量的节点,因为它只检查节点 (p) 中的 X。它既不按顺序检查其间的节点,也可以多次找到相同的 id。
  • 所以你需要支持对路径中多个节点的检查,并且需要保证这些节点在路径中的顺序,同一个节点id不能出现多次?请将这些新要求添加到您的描述中,在不了解它们的情况下很难给出好的答案。
  • 添加了一个可以满足您要求的查询。不过,可能仍有一些优化方法。
  • 哇,你是 cyper 的巫师!我想他们必须在 neo4j 中添加类似 join 的东西才能以“漂亮”且简短的方式做到这一点。
  • 这是一个相当具体的案例,但我认为可能有一些空间可以缩短索引和展开操作。如果要使用查询,请确保添加回 parent-of 关系方向!
【解决方案2】:
MATCH (n) DETACH DELETE n;
CREATE (:value {name:'A'}) -[:`version-of` {from:'2017-01-01'}]-> (:ID {id:32});
CREATE (:value {name:'CC'}) -[:`version-of` {from:'2017-01-05'}]-> (:ID {id:21});
CREATE (:value {name:'E'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:98});
CREATE (:value {name:'N'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:87});
CREATE (:value {name:'E'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:48});
CREATE (:value {name:'W'}) -[:`version-of` {from:'2017-02-01'}]-> (:ID {id:74});
CREATE (:value {name:'E'}) -[:`version-of` {from:'2017-01-01'}]-> (:ID {id:75});
MATCH (n:ID {id:21}) CREATE (:value {name:'CD'}) -[:`version-of` {from:'2017-03-01'}]-> (n);
MATCH (a:ID {id:32}),(b:ID {id:21}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:32}),(b:ID {id:23}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:32}),(b:ID {id:48}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:48}),(b:ID {id:87}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:87}),(b:ID {id:98}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:21}),(b:ID {id:74}) CREATE (a)<-[:`parent-of`]-(b);
MATCH (a:ID {id:74}),(b:ID {id:75}) CREATE (a)<-[:`parent-of`]-(b);

a more exiting example-graph to test on (上面的解决方案在这里也有效)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多