Neo4j:匹配多个标签(2个或更多)

64

我想进行搜索,并且想要从2个标签(OR条件)开始遍历。例如,我需要找出所有标记为“男性”或“女性”的节点,并且其属性name =~ '.ail.'。

8个回答

70

你可以将这个条件放在WHERE子句中:

MATCH (n)
WHERE n:Male OR n:Female
RETURN n

编辑

正如@tbaum所指出的那样,这执行了一个AllNodesScan。当时标签还比较新,我期望查询规划器最终会对每个标签实现一个NodeByLabelScan,就像单个标签的情况一样。

MATCH (n)
WHERE n:Male
RETURN n

我仍然认为这是一个合理的查询表达式,并且期望查询规划器使用标签扫描来实现它,但是在Neo4j 2.2.3中,该查询仍然使用AllNodesScan和标签过滤器进行实现。因此,这里提供了一种更冗长的替代方法。由于标签分离表示集合并集,并且可以用不同的方式表示此并集,因此我们可以以一种查询规划器不需要扫描所有节点的方式来表达它,而是从每个标签开始使用NodeByLabelScan

MATCH (n:Male)
WHERE n.name =~ '.ail.'
RETURN n
UNION MATCH (n:Female)
WHERE n.name =~ '.ail.'
RETURN n
这意味着针对每个标签表达一次查询,然后使用明确的UNION将它们连接起来。 至少对于较少数量的标签来说,这并不是不合理的,但我不清楚为什么查询计划程序不能从更简单的查询中推断出相同的实现,因此我在这里打开了一个github问题here

10
有没有更短的方法来完成这个任务?例如,对于关系,您可以指定 (n)-[:rel1 | rel2]->(m),其中 | 表示 OR - Lyman Zerga
不,你不能将该模式用于标签,并且我不知道是否有任何更短或没有WHERE子句就能工作的其他模式。欢迎在Neo4j github存储库提交功能请求。 - jjaderberg
@Lyman Zerga 我也在很多地方搜索过,但没有找到类似的东西。 - Ravinder Payal
1
我遇到了同样的问题,并使用MATCH n WHERE n:Label1 OR n:Label2方法解决了它。根据运行查询的EXPLAIN和PROFILE返回的内容,似乎没有执行“AllNodesScan”。在我的情况下,我有两个标签,进行了两次节点扫描,并将结果合并。因此,第一个解决方案现在可能是最好的。 - augustearth
有人能确认@augustearth的说法吗?我是neo4j的新手,不知道如何计算扫描次数并判断性能差异。 - Daniel
作为更新,此操作不再执行AllNodesScan,而是执行两个NodeByLabelScan,以及一个Union和Distinct。 - Simon Thordal

19
MATCH n WHERE n:Label1 OR n:Label2

...会导致AllNodesScan,这不是一个好主意!

也许有更好的解决方案:

OPTIONAL MATCH (n1:Label1)
WITH collect(distinct n1) as c1

OPTIONAL MATCH (n2:Label2) 
WITH collect(distinct n2) + c1 as c2

OPTIONAL MATCH (n3:Label3) 
WITH collect(distinct n3) + c2 as c3

UNWIND c3 as nodes
RETURN count(nodes),labels(nodes) 

4
为什么每一步都需要“明显”的区分? - Jonathan Crosmer
1
感谢您指出AllNodesScan,我认为这个问题现在应该已经解决了。我已经更新了我的答案,您对我更详细的使用UNION的替代方案有什么想法?它与您的OPTIONAL MATCH/collect()/UNWIND相比如何? - jjaderberg
1
一个注意点:UNION 不方便(在某些情况下甚至无法使用),因为目前(2.2)你不能对 UNION 的结果进行任何处理。例如,你不能使用 SKIP/LIMIT 或 COUNT。 - Jonathan Crosmer

11

Neo4j 3.4.7中,当使用两个OR筛选器的WHERE查询时,查询规划器会对2个NodeByLabelScan进行UNION和DISTINCT操作。尝试在Offshore Leaks Database沙盒上执行EXPLAIN MATCH (o) WHERE o:Officer OR o:Entity RETURN o查询,得到以下规划结果:

Neo4j query planning


8
如果您想通过多个标签以OR或IN条件筛选节点,请使用以下代码:
MATCH (n)
WHERE labels(n) in [['Male'],['Female']]
AND n.name =~ '.ail.'
RETURN n

那个可以运行,但它会进行全节点扫描,可能会导致性能下降。 - Israel Varea
2
为什么要使用嵌套列表?为什么不只用['男','女'] - Ooker

8

对于v3.5,我们可以做以下事情:

MATCH (n) WHERE (n:User OR n:Admin) AND n.name CONTAINS "ail" RETURN n

并获得:
╒══════════════════╕
│"n"               │
╞══════════════════╡
│{"name":"Abigail"}│
├──────────────────┤
│{"name":"Bailee"} │
└──────────────────┘

6
现在(2023年)有一种专门的方法可以匹配多个标签。
这只适用于Neo4j 5及更高版本。
```MATCH (n:Movie|Person) RETURN n.name AS name, n.title AS title``` enter image description here 根据找到的文档here
为了解决您的特定查询: ```MATCH (n:User|Admin) WHERE n.name CONTAINS "ail" RETURN n```

0

如果你需要处理合并集或者避免使用UNION,另一个选项是:

MATCH(m:Male) WHERE m.name=~'.ail.' WITH COLLECT(m) AS male 
MATCH(f:Female) WHERE f.name=~'.ail.' WITH male, COLLECT(f) AS female
UNWIND (male + female) AS person 
RETURN person.name;

这种方法并不像使用 UNION 那样高效,但仍然避免了昂贵的 AllNodesScan 操作符。在我的用例中,查询已经包含了一个不同目的的 UNION。


-5

1
答案不完全正确,但也不是完全错误的。我认为作者只是在查询中遗漏了关系,但它可以用于匹配多个节点标签:MATCH (a:User:Admin)-[r]->(b) return a,r,b - artemisian
请注意,源URL稍作更改。这是新的网址:https://neo4j.com/docs/developer-manual/current/cypher/syntax/patterns/#_labels - Chad
28
实际上,这个说法是错误的,(a:User:Admin) 这个查询描述的是节点既是“用户”又是“管理员”,而不是“用户或管理员”。 - Carlos_Mondragon

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