本章来聊聊APOC里路径查询的用法,如果APOC库还不知道是什么或者不知道怎么安装可以参照文章APOC是啥了解了解!!!不要钱。
文章中我们以查询产品光伏组件数据为例子。数据基于以下样本图:
返回结果MaterialsRelationship类型是一个关系实体,定义如下:
@Data
@RelationshipEntity(type = "Materials")
public class MaterialsRelationship {
@Id
private String uuid;
/**
* 开始节点
*/
@StartNode
private ProductEntryNode startNode;
/**
* 结束节点
*/
@EndNode
private ProductEntryNode endNode;
}
比如要查询光伏组件上游关系类型包含Materials,Manufacture,Technology的路径,已知的是光伏组件的id,那么在dao层可以这么做
@Query(" match path=(p:ProductEntry)-[r:Materials|Manufacture|Technology*1..]->(pp)" +
" where p.productEntryId={productEntryId}" +
" return path")
List<MaterialsRelationship> getProductEdgesList(String productEntryId);
这样就返回了光伏组件的上游所有路径和节点。用到了'*1..',表示查询的路径长度是无线大。很明显,这样使用在Neo4j里是不允许的。除非你能保证路径长度不超过6(官方推荐数字)。所以考虑把路径长度通过动态参数传入,像这样:
@Query(" match path=(p:ProductEntry)-[r:Materials|Manufacture|Technology*1..{level}]->(pp)" +
" where p.productEntryId={productEntryId}" +
" return path")
List<MaterialsRelationship> getProductEdgesList(String productEntryId, int level);
很遗憾的是,cypher不支持这种传参。那怎么办呢?别着急,只要你能遇到的问题,Neo4j开发团队肯定已经遇到并且给出了解决方案。那就是封装成APOC函数了。具体怎么使用,一起来看看。找到apoc.path.expand的函数。这个函数就是查询节点的路径,具体使用语法看下:
apoc.path.expand(start :: ANY?, relationshipFilter :: STRING?, labelFilter :: STRING?, minLevel :: INTEGER?, maxLevel :: INTEGER?) :: (path :: PATH?)
参数说明:
start:开始节点,可以是任意类型;
relationshipFilter:关系类型,多个用|分开(STRING)
labelFilter:标签过滤(STRING)
minLevel:最小路径长度(INTEGER)
maxLevel:最大路径长度(INTEGER)
path:返回结果路径
从参数里可以看出,maxLevel就是需要的传入参数。可以直接在dao层这样写了:
@Query("match (p:ProductEntry) where p.productEntryId={productEntryId}" +
" call apoc.path.expand(p,'<Materials|<Manufacture|<Technology','+ProductEntry',1,{level}) yield path" +
" return path")
List<MaterialsRelationship> getProductEdgesList(String productEntryId, int level);
其中'<Materials|<Manufacture|<Technology'里的'<'表示指定的方向,如果查询是双向的可以不用加'<'。+ProductEntry表示上游关系只返回每个节点都有ProductEntry标签的路径。测试一下果然可以。说明路径长度可以通过APOC的函数apoc.path.expand动态传参的。返回结果类似这样:
(p)-[r:Materials]->(pp:ProductEntry)-[r:Materials]->(ppp:ProductEntry)
其他参数讲解一下。如果要查询下游数据,方向可以这样指定
'Materials>|Manufacture>'或者没有方向'Materials|Manufacture':
@Query("match (p:ProductEntry) where p.productEntryId={productEntryId}" +
" call apoc.path.expand(p,'Materials>|Manufacture>|Technology>','',1,{level}) yield path" +
" return path")
List<MaterialsRelationship> getProductEdgesList(String productEntryId, int level);
还可以使用标签过滤器指定遍历终止条件。如果想在遍历遇到包含Engineering标签的节点时立即终止遍历,这个是包含当前Engineering节点的,则可以使用/Engineering节点过滤器:
@Query("match (p:ProductEntry) where p.productEntryId={productEntryId}" +
" call apoc.path.expand(p,'Materials>|Manufacture>|Technology>','/Engineering',1,{level}) yield path" +
" return path")
List<MaterialsRelationship> getProductEdgesList(String productEntryId, int level);
还可以使用'<Engineering'表示仅返回带有Engineering标签的节点处终止的路径,并且继续往下查找含有Engineering标签的节点,返回的结果类似这样:
(p)-[r:Materials]->(pp:Engineering)-[r:Materials]->(ppp:Engineering)
本篇讲的主要是apoc.path.expand函数的用法,包括解决了如何动态传参最大路径长度level,以及其他参数的说明。其中start还可以是id或者list,这些留给大家自己尝试。这里要说明我们不是为了用APOC而用,而是说在cypher中解决不了这样的问题,想到APOC函数有更好的解决方案。
- 本期
为方便看最新内容,记得关注Neo4j权威指南