【问题标题】:How to get spring neo4j cypher custom query to populate an array of child relationships如何获取 spring neo4j cypher 自定义查询以填充一组子关系
【发布时间】:2019-05-03 05:59:54
【问题描述】:

对 Spring Data Neo4j (SDN) 的内置查询默认返回填充深度为 1 的对象。这意味着填充查询返回的对象的“子”(相关节点)。很好 - 这些查询返回的对象的引用末尾有实际对象。

默认情况下,自定义查询的深度为 0。这很麻烦。

this answer 中,描述了如何让 springboot neo4j 将相关元素填充到自定义查询的目标 - 以从查询中获得额外的一级深度。

当相关元素在列表中时,我无法使用此方法:

@NodeEntity
public class BoardPosition {

    @Relationship(type="PARENT", direction = Relationship.INCOMING)
    public List<BoardPosition> children;

我有一个返回目标 BoardPosition 的查询,我需要填充它的子级。

@Query("MATCH (target:BoardPosition) <-[c:PARENT]- (child:BoardPosition) 
       WHERE target.play={Play} 
       RETURN target, c, child")
BoardPosition findActiveByPlay(@Param("Play") String play);

问题在于查询似乎为每个孩子返回一个单独的结果,而这些结果并未用于填充目标中的孩子数组。

而不是 Spring Neo 将子项整理到目标上的数组中,我得到“只有 1 个预期结果”错误 - 好像查询返回多个结果,每个结果都有一个子项,而不是一个包含子项的结果。

org.springframework.dao.IncorrectResultSizeDataAccessException: 结果大小不正确:预计最多为 1

如何使用自定义查询来填充该目标的子列表?

(请注意,内置的 findByPlay(play) 可以满足我的要求 - 内置查询的深度为 1 而不是 0,它返回一个包含填充子对象的目标 - 但我当然需要进行查询比“by Play”更复杂一点......这就是我需要解决这个问题的原因)


版本:

org.springframework.data:spring-data-neo4j:5.1.3.RELEASE

neo4j 3.5.0

【问题讨论】:

  • 您使用的是哪个版本。
  • 你能返回路径吗?
  • “返回路径”是什么意思?我已经read 关于如何返回节点、关系和属性,没有看到“路径”?
  • 你可以编写自己的拦截器。
  • 您的问题似乎与我遇到的有点不同,但它们仍然与 自定义查询返回的同类型节点的投影(返回值评估)有关 .因此,请查看我们的discussion 和 michael-simons 关于 Neo4J OGM 错误的信息。它可能也影响了您的用例。因此,如果您仍然遇到问题,不妨尝试一下 Neo4J OGM 的 alpha(或现在的 RC)版本。

标签: spring-boot neo4j cypher


【解决方案1】:

=== 编辑 ======

你的问题出现是因为你有自关系(相同标签的节点之间的关系)

这就是 Spring 处理单节点查询的方式:

org.springframework.data.neo4j.repository.query.GraphQueryExecution

@Override
public Object execute(Query query, Class<?> type) {
    Iterable<?> result;
    ....
    Object ret = iterator.next();
    if (iterator.hasNext()) {
        throw new IncorrectResultSizeDataAccessException("Incorrect result size: expected at most 1", 1);
    }
    return ret;
}

Spring 将您的节点类类型 Class&lt;?&gt; type 传递给 neo4j-ogm 并回读您的数据。

你知道,neo4j 服务器会为你的查询返回多行,每个匹配path

A <- PARENT - B
A <- PARENT - C
A <- PARENT - D

如果您的节点具有不同的标签,即不同的类类型,那么 ogm 只返回与您的查询返回类型相对应的单个节点,没问题。

但是你的节点是相同的标签,即相同的类类型 => Neo4j OGM 无法区分哪个是返回的节点 -> 所有节点 A、B、C、D 返回 -> 异常

关于这个问题,我认为你现在应该提交错误报告。

对于解决方法,您可以将查询更改为仅返回 distinct target.your_identity_propertyidentity_property 是节点的“主键”,它唯一地标识您的节点)

然后让您的应用程序调用加载该身份属性:

public interface BoardRepository extends CrudRepository<BoardPos, Long> {

    @Query("MATCH (target:B) <-[c:PARENT]- (child:B) WHERE target.play={Play} RETURN DISTINCT target.your_identity_property")
    Long findActiveByPlay(@Param("Play") String play);

    BoardPos findByYourIdentityProperty(xxxx);
} 

=== 旧 ======

Spring docs says that(由我突出显示):

自定义查询不支持自定义深度。此外,@Query 不支持将路径映射到域实体,因此不应从 Cypher 查询返回路径。 相反,返回节点和关系以将它们映射到域实体。

很明显,您的用例(通过自定义查询填充子节点)是支持的。 Spring 框架已经将结果映射到单个节点中。 (确实,我在当地的道岔上设置的操作是正常的)

所以你的异常可能是由几个问题引起的:

  1. 您有多个target:BoardPositiontarget.play={play}。所以例外是指不止一个target:BoardPosition,而不是一个有多个子结果的 BoardPosition

  2. 您的实体映射不正确。您的映射字段是否使用正确的 direction 属性注释 @Relationship?您可以在此处发布您的实体。

这是我的本地设置:

@NodeEntity(label = "C")
@Data
public class Child {

    @Id
    @GeneratedValue
    private long id;
    private String name;

    @Relationship(type = "PARENT", direction = "INCOMING")
    private List<Parent> parents;
}

public interface ChildRepository extends CrudRepository<Child, Long> {

    @Query("MATCH (target:C) <-[p:PARENT]- (child:P) " 
        + "WHERE target.name={name} " 
        + "RETURN target, p, child")
    Child findByName(@Param("name") String name);
}

(:C) <-[:PARENT] - (:P)

【讨论】:

  • 天哪,我的箭头可能是错误的方向。只是为了快速确认并感谢您的回复。看起来很有希望,我会尽快测试。
  • 祝你好运@GreenAsJade :)
  • 遗憾的是,箭头的方向并没有错——现在我仔细看,关系“PARENT”的意思是“这个节点是我的父节点”,并且在目标上传入,即父母。该实体代码段已发布。我想知道我的问题和你的问题是否不同,因为我的父母和孩子是同一类型,而你的问题是不同的类型。这是否让 Neo 难以弄清楚我的情况?
  • 哦,我有你的问题,让我现在解决它
【解决方案2】:

考虑替代查询

MATCH (target:BoardPosition {play:{Play}})
RETURN target, [ (target)<-[c:PARENT]-(child:BoardPosition) | [c, child] ]

它使用列表推导不仅返回目标,还返回一个结果行中标签BoardPosition的关系和相关节点。这确保了结果将是单行(只要您的属性 play 是唯一的)。

我没有在您的示例中尝试过,但在我的应用程序中,这种方法运行良好。 Neo4j OGM 会按预期对对象进行水合。重要的是包括相关节点以及指向节点的关系。

如果启用 neo4j OGM 日志,可以看到深度为 1 的内置查询使用相同的方法。

【讨论】:

  • 为了完整起见:logging.level.org.neo4j.ogm.drivers.bolt.request.BoltRequest=DEBUG 启用密码日志记录
  • 我需要阅读新列表推导。令我惊讶的是,虽然这在 neo 浏览器中只返回了一条记录(只确认了一场比赛),但它仍然生成org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result size: expected at most 1
  • 基本上,它应该可以工作。也许你可以尝试做一个 SSCCE 并在这里上传?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
相关资源
最近更新 更多