提问人:Podbrushkin 提问时间:11/8/2023 最后编辑:Podbrushkin 更新时间:11/20/2023 访问量:56
在 Spring Data Neo4j 中将域对象作为参数传递给自定义 Cypher 查询
Passing domain object as argument to custom Cypher query in Spring Data Neo4j
问:
我正在编写Spring Data Neo4j webapp。 我有这个域对象:
@Node("Person")
public class Person {
@Id
@GeneratedValue
private String id;
和一段关系。(:Image) - [:DEPICTS] -> (:Person)
我需要计算描绘特定人物的图像。 这是我的尝试:
public interface ImageRepositoryNeo4j extends Neo4jRepository<Image, String> {
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE p = $person
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") Person person);
但它总是返回 0。
其他失败的尝试
@Query("""
MATCH (i:Image)-[:DEPICTS]->(p:Person)
WHERE p IN $people
RETURN count(i)
""")
// DOESN'T WORK, 0 RESULTS
public Integer countByPeopleContains(@Param("people") Collection<Person> people);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE ID(p) = $person.__id__
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") Person person);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE id(p)=$person
RETURN count(i)
""")
// DSNT WORK
public Integer countByPeopleContains(@Param("person") String personId);
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person {id: $person.__id__})
RETURN count(i)
""")
// DSNT WORK, ALWAYS 0
public Integer countByPeopleContains(@Param("person") Person person);
它们都返回 0。
这个有效,但由于不是唯一的属性,它不适合域逻辑:fullName
@Query("""
MATCH (i:Image) -[:DEPICTS] -> (p:Person {fullName: $person})
RETURN count(i)
""")
// TODO: personFullname is not unique, use smth else
public Integer countByPeopleContains(@Param("person") String personFullName);
如何在Spring Data Neo4j存储库的自定义Cypher查询中使用域对象?
线索
在Spring Data Neo4j文档中,我发现最接近我需要的是:
@Node
public final class Movie {
@Id
private final String title;
...
interface MovieRepository extends Neo4jRepository<Movie, String> {
@Query("MATCH (m:Movie {title: $movie.__id__})\n"
+ "MATCH (m) <- [r:DIRECTED|REVIEWED|ACTED_IN] - (p:Person)\n"
+ "return m, collect(r), collect(p)")
Movie findByMovie(@Param("movie") Movie movie);
}
但出于某种原因,它在我的情况下不起作用。也许那是因为我的 id 是 .@GeneratedValue
另一条线索
在 Neo4j 嵌入式文档中,有一个关于如何在没有 Spring Data Neo4j 的情况下将对象传递给 Cypher 的示例:
Map<String,Object> params = new HashMap<>();
params.put( "node", bobNode );
String query =
"MATCH (n:Person)" + "\n" +
"WHERE n = $node" + "\n" +
"RETURN n.name";
Result result = transaction.execute( query, params );
它的工作原理是$node:org.neo4j.graphdb.Node
@Bean
CommandLineRunner countForGregWithoutSDN(GraphDatabaseService graphDatabaseService) {
return args -> {
try (var tx = graphDatabaseService.beginTx()) {
String cypherGetGreg = "MATCH (n:Person {birthday: date('1993-03-03')}) RETURN n LIMIT 5;";
Result result = tx.execute(cypherGetGreg);
var greg1993 = (Node) result.next().get("n");
if (!greg1993.getProperty("name").equals("Greg"))
throw new RuntimeException("Should be Greg1993 but actually "+greg1993);
result.close();
Map<String,Object> params = new HashMap<>();
params.put( "node", greg1993 );
String cypherCountForGreg = """
MATCH (i:Image) -[:DEPICTS] -> (p:Person)
WHERE p = $node
RETURN count(i)
""";
result = tx.execute( cypherCountForGreg, params );
var count = result.next().values().iterator().next();
System.out.println("Count for Greg1993 is "+count); // 1
result.close();
}
};
}
但如果 $node 是 SDN 实体,则结果为 0。
答:
0赞
Vincent Rupp
11/15/2023
#1
我怀疑问题出在评估节点相等性上。
而不是尝试WHERE p = $person
WHERE id(p) = id($person)
更新:根据这篇文章,我可能会弄错:应该直接比较节点还是必须首先通过 ID()?
评论
0赞
Podbrushkin
11/15/2023
我试过了,当调用此方法时,会抛出此运行时异常:org.neo4j.driver.exceptions.ClientException: Invalid input for function 'id()': Expected Map{__labels__ -> List{String("Person")}, __id__ -> String("4:21f6f6f2-ea32-4b8c-a8a7-e953f6f436c2:1559"), __properties__ -> Map{birthday -> NO_VALUE, dotId -> String("somedotid"), fullName -> String("vasya pupkin")}} to be a node or relationship, but it was 'Map'
0赞
Vincent Rupp
11/15/2023
$person
那么就不是一个节点,不是 Neo4j 需要它的方式。是否必须传入 Person 节点对象,或者是否可以将 PersonID 作为节点上的索引属性?
0赞
Podbrushkin
11/15/2023
它是一个节点。它是用 注释的类的对象,它是从 neo4jRepository 返回的,正如您在异常 SDN added 和 properties 中看到的那样,后者是 neo4j 生成的。如果需要,我可以提供 MRE。我还没有找到任何关于我在任何地方尝试做什么的工作示例。我可以更改此方法的签名并传递字符串,但它也不起作用(请参阅“失败的尝试”),并且 func 已被弃用,neo4j 团队不鼓励使用内部节点 ID。@Node
__labels__
__id__
person.id
id()
0赞
Vincent Rupp
11/15/2023
你是对的.至于类,错误说整个东西是一个映射,而不是一个节点或关系,我不确定这是如何发生的。你可以试试或.否则,MRE将非常有用。id()
p = apoc.convert.toNode($person)
apoc.create.vNode($person)
0赞
Podbrushkin
11/15/2023
给你,MRE。我希望重新创建目录结构不会花费太多时间,通常我会像这样在 pwsh 中这样做:等等。使用 运行它。感谢您的关注。如果我们可以在不依赖 id 的情况下在自定义密码中引用我们的实体对象,那就太好了。但是现在我甚至无法通过id做到这一点。gcb -Raw | New-Item -Force '.\src\main\java\myapp\repo\PersonRepositoryNeo4j.java'
mvn clean spring-boot:run -e
评论
$person