Neo4j有序树

3
我们正在使用一个层级树结构,其中父节点有零个或多个子节点,而子节点只能有零个或一个父节点。当我们查询给定父节点的直接子节点列表时,查询结果会以随机顺序返回子节点。我们需要按照创建或更新子节点时定义的顺序返回子节点。
我已经在子节点之间添加了关系-[:Sibling]->,因此“顶部”节点只有一个传入的:Sibling关系,而“底部”节点只有一个传出的关系。
鉴于此,是否有Cypher查询可以按兄弟节点的顺序返回子节点?
我有一个查询,返回每个子节点及其兄弟节点,但现在必须编写一些代码以正确顺序返回列表。
另一种方法可能是为每个子节点添加排序号。如果其中一个子节点更改顺序,则需要为所有子节点更新它。这种方法似乎与图形数据库概念略有不同。
如果此问题以前遇到过,是否有标准算法可以通过编程解决?
更新1
sample data如Bruno所请求
(parent1) (child1)-[:ChildOf]->(parent1) (child2)-[:ChildOf]->(parent1) (child2)-[:Sibling]->(child1) (child3)-[:ChildOf]->(parent1) (child3)-[:Sibling]->(child2)
有没有Cypher查询可以按child1、child2、child3的顺序返回?
如果没有,则可以通过程序进行排序。
使用属性而不是关系
(parent1) (child1)-[:ChildOf]->(parent1) (child1:{order:1}) (child2)-[:ChildOf]->(parent1) (child2:{order:2}) (child3)-[:ChildOf]->(parent1) (child3:{order:3})
`match (c)-[:ChildOf]->(parent1) return c ordered by c:order`

我不认为有一种密码查询可以更新子元素的顺序。


更新2

我现在已经得到了以下查询,它以正确的顺序返回子元素

`match (firstChild)-[:FirstChildOf]->(parent) match (sibling)-[:Sibling*]->(firstChild) return firstChild,sibling`

该查询依赖于添加一个 -[:FirstChildOf]->(parent) 关系。

如果没有其他通知,我会将其设置为答案。

我应该假设没有用于将节点插入有序列表的 Cypher 查询吗?


你好,马丁!我认为在你的问题中加入一个样本数据集和期望的结果是一个好主意。谢谢! - Bruno Peres
3个回答

2
以下是我处理Neo4J节点有序列表的一些技巧:
  • 将关系方向反转为(child1)<-[:HasChild]-(parent1)。(这主要是逻辑上的加强,因为“父母拥有孩子列表”)

  • 在HasChild中添加属性index。这将使您可以使用WITH child, hasChild.index as sid ORDER BY sid ASC进行兄弟姐妹排序。(这是我在儿童列表上维护任意顺序信息的方法)这是在关系上的,因为它假定一个节点可以是多个有序列表的一部分。

  • 您可以使用SIZE(shortestpath((root)-[*]->(child1)) as depth,然后按根据深度对它们进行排序。

  • 由于这是任意顺序,您必须更新所有索引以更新顺序(您可以执行类似于WITH COLLECT(child) as children, FILTER(c IN COLLECT(child) WHERE c.index >=3) as subset FOREACH (c IN subset| SET c.index+=1)的基本插入,否则您将不得不重新编写它们全部以任意更改顺序。

  • 如果您实际上并不关心顺序,只是想要保持一致,您可以使用WITH child, ID(child) as sid ORDER BY sid ASC。这基本上是“按节点年龄排序”

  • 另一个选择是使用元关系。因此,:HasChild将充当节点列表,然后类似于:NextMember的东西将告诉您从这个节点开始的下一个项目。这更加灵活,但在我看来难以处理(您需要检查是否缺少下一个,要获得正确的顺序,您必须对节点进行“跟踪”,如果您稍后想要将此节点添加到另一个有序列表中则无法工作等)。

当然,如果顺序不是任意的(基于名称或年龄等),那么最好只是根据非任意逻辑进行排序。


目前我认为我们将继续使用-[:Sibling]->关系,因为当顺序改变时更容易更改。但是我们可能会将顺序存储在节点或关系上。 - Martin Flower
我们应该选择(parent)<-[:isChildOf]-(child)还是(parent)-[:HasChild]->(child)?目前我们使用的是前者 - 一个子节点必须有且仅有一个父节点(除了根节点)。即使父节点没有任何子节点,它也可以继续存在。这让我想到关系更强烈地依附于子节点。在neo4j中,查询性能是否取决于关系方向? - Martin Flower
@MartinFlower Cypher 只关心方向,就像你一样(只是在必要时进行验证)。我认为方向对人类而言更是一个问题。你有一棵树形结构,所以从根节点扩展,“(parent)- [] ->(child)”比“(parent)<- [] -(child)”更直观,可以获取所有子节点。(它在那个方向上暗示了一对多的关系。)当然,大多数时候,让你的模型更易于阅读对人类而言更有好处,当然,这完全取决于你用数据做什么。因此,这真的只是考虑个人偏好。 - Tezra
只需使用最合乎逻辑的方向,以及最不容易引起混淆的方向,适合你和你的团队。只要确保你的逻辑是一致的就可以了。 - Tezra

2
当您创建图形模型时,应该专注于想要通过数据模型回答的问题。如果您想按“创建或更新”属性排序获取父项的子项,则应存储它,因为通常关系不代表顺序。这与图形数据库概念并不矛盾,因为图形数据库使用属性。决定将某些内容存储为关系还是属性并不总是容易的任务,这完全取决于适当的建模。如果您有“[:NEXT_SIBLING]”或类似的概念,那么当您必须删除节点时,它会令人感到非常麻烦。因此,当此状态是恒定的时候,应该使用它。例如,在时间树中,一天跟着一天,而且不会改变。

一般来说,如果需要使用创建顺序,则应该使用此类时间戳:

    create (n:Node {created:timestamp()});

然后您可以使用时间戳进行排序。
    match (p:Parent)-[:HAS_CHILDREN]->(n:Child) where p.name='xy' return n order by n.created;

您也可以使用时间戳来建立关系。 类似的问题在这里: 使用neo4j建立有序树模型


孩子们的初始顺序是在创建它们时定义的——appendSibling()将新的孩子添加到最后一个孩子之后,insertSibling()在另一个孩子之前插入。然后,顺序可以在以后改变——将childXXX移动到childYYY之前。我猜图形数据库可以像链表一样运行。也许我的猜测是错误的。 - Martin Flower
它肯定可以像那样表现,因为它们是节点和关系。上面的链接包含一个时间树解决方案,其方法与您提到的类似。 - szenyo
我已经根据相关问题的输入找到了一个查询,并更新了我的问题。 - Martin Flower

0

返回正确顺序的子元素查询为:

match (firstChild)-[:FirstChildOf]->(parent) match (sibling)-[:Sibling*]->(firstChild) return firstChild,sibling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接