如果关系不存在,则返回节点

108

我正在尝试使用Cypher创建一个查询,以“查找”厨师可能缺少的成分,我的图形设置如下:

(ingredient_value)-[:is_part_of]->(ingredient)

(ingredient)的键/值将为name="染色颜料"。(ingredient_value) 可以具有value="红色"的键/值,并且"is part of" (ingredient, name="dye colors")

(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)

我使用这个查询语句获取所有食谱所需的成分,但不是它们的实际值,但我只想返回厨师没有的成分,而不是每个菜谱所需的所有成分。我尝试过

(chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)<-[:has_ingredient*0..0]-chef

但是这样没有返回任何结果。

这个问题能否通过cypher/neo4j实现,或者最好通过返回所有配料并自己排序来处理?

奖励问题:是否有一种方法可以使用cypher将厨师拥有的所有值与食谱所需的所有值匹配。到目前为止,我只返回了chef-[:has_value]->ingredient_value<-[:requires_value]-recipe返回的所有部分匹配,并自己聚合结果。


请查看以下与v3相关的信息:https://dev59.com/VF8e5IYBdhLWcg3whqk4 - Maciej
对于未来的用户,可以在WHERE子句中使用exists(也可以否定它),有关更多信息,请参见https://neo4j.com/developer/subqueries/#existential-subqueries。 - ozanmuyes
7个回答

179

更新 2013年1月10日:

在 Neo4j 2.0 参考手册中看到了这个:

尽量不要使用可选关系。 最重要的是,

不要像这样使用它们:

MATCH a-[r?:LOVES]->() WHERE r IS NULL,你只需确保它们不存在即可。

相反,应该像这样做:

MATCH (a) WHERE NOT (a)-[:LOVES]->()

使用Cypher查询语言检查关系是否不存在:

...
MATCH source-[r?:someType]-target
WHERE r is null
RETURN source

问号标记表示关系是可选的。

在 neo4j 2 中执行:

...
OPTIONAL MATCH source-[r:someType]-target
WHERE r is null
RETURN source

现在您可以检查不存在的(null)关系。


3
在Neo4j 2.0中,使用OPTIONAL MATCH来匹配可选关系,例如第一个示例将如下所示:OPTIONAL MATCH (source)-[r:someType]-(target) RETURN source, r - boggle
我正在尝试在WHERE NOT中使用带标签的节点,但它不起作用。例如:MATCH a WHERE NOT (a)-[:LOVES]->(Stranger),其中'Stranger'是一个节点标签。我正在使用neo4j版本2.1.2。 - Krishna Shetty
1
没关系,我明白为什么你想展示达到这个答案的过程:MATCH a WHERE NOT (a)-[:LOVES]->()。 - NumenorForLife
你好,也祝你有美好的一天! - Roee Gavirel
5
MATCH a...的示例现在应该是MATCH (a) WHERE NOT (a)-[:LOVES]->() - Liam
1
@gil-stal 为什么我不能在查询中使用节点名称,像这样:MATCH a WHERE NOT (a)-[:LOVES]->(b:SomeLabel)。如果不使用节点名称,则可以正常工作。 - iit2011081

27

获取没有关联关系的节点是通过以下方法实现的

这是检查节点是否存在关系的好方法

MATCH (player)
    WHERE NOT(player)-[:played]->()
    RETURN player

您还可以检查多个条件

它将返回所有没有“played”或“notPlayed”关系的节点。

MATCH (player) 
 WHERE NOT (player)-[:played|notPlayed]->()
 RETURN player

获取没有关系的节点

MATCH (player) 
WHERE NOT (player)-[r]-()
RETURN player

它将检查节点是否没有任何传入/传出关系。


4
“MATCH (player) WHERE NOT (player)-[r]-() RETURN player”会出现“Variable r not defined”的错误。如何定义r?将语句改为“MATCH (player) WHERE NOT exists((player)-[r]-()) RETURN player”,即可定义r并避免错误。 - Chathura Wijeweera
要解决这个问题,可以指定一个关系(例如 (player -[:rel]- ()),或者留空以匹配任何关系 (player -[]- () - Archemar
MATCH (player) WHERE NOT (player)-[]-() RETURN player - 它运行良好。 - Prashant
你的第一个查询实际上是错误的。MATCH模式本身总是只返回存在的关系,没有其中任何一个为NULL。因此,你的WHERE语句没有任何过滤作用。 - Cristian Scutaru
1
@CristiS。谢谢你让我知道。我已经更新了查询,现在应该可以工作了。 - Satish Shinde

8
如果你需要“条件排除”语义,可以通过以下方式实现。
自neo4j 2.2.1版本以来,您可以使用“OPTIONAL MATCH”子句并过滤未匹配(NULL)的节点。
同样重要的是,在“OPTIONAL MATCH”和“WHERE”子句之间使用“WITH”子句,以便第一个“WHERE”为可选匹配定义条件,第二个“WHERE”则像过滤器一样运行。
假设我们有两种类型的节点:“Person”和“Communication”。 如果我想获取所有从未通过电话进行过通信但可能有其他通信方式的人员,则会执行此查询:
MATCH (p: Person) 
OPTIONAL MATCH p--(c: Communication) 
WHERE c.way = 'telephone'
WITH p, c 
WHERE c IS NULL 
RETURN p

这个匹配模式将匹配所有带有通信方式的人,其中非电话通信的c将为NULL。然后,过滤器(在WITH之后的WHERE)将过滤掉电话通信,留下其他所有通信。

参考资料:

http://neo4j.com/docs/stable/query-optional-match.html#_introduction_3 http://java.dzone.com/articles/new-neo4j-optional


第二行的p前面应该加上括号,像这样:OPTIONAL MATCH (p)--(c: Communication)。 - undefined
@GuidoInLFP 我不清楚当前版本,但在指定的版本中,你是不需要的。 - undefined

2
我写了一篇简要说明,展示如何使用Cypher 2.0自然地完成这个过程。

http://gist.neo4j.org/?9171581

关键点是使用可选匹配来匹配现有的原料,然后比较以过滤缺失(空值)的原料或值不正确的原料。
请注意,这个概念是声明性的,不需要描述算法,你只需要写下你需要的内容。

2
最后一个查询应该是:
START chef = node(..)
MATCH (chef)-[:has_value]->(ingredient_value)<-[:requires_value]-(recipe)-[:requires_ingredient]->(ingredient)
WHERE (ingredient)<-[:has_ingredient]-chef
RETURN ingredient

这个模式:(ingredient)<-[:has_ingredient*0..0]-chef 是没有返回任何结果的原因。*0..0 表示关系的长度必须为零,这意味着 ingredient 和 chef 必须是相同的节点,但它们并不相同。

是的,但它并没有返回所需的配料。它返回了厨师已经与食谱共有的内容,我想找出它们之间的差异。 - Nicholas

1

对于新版本的Neo4j,你可能会遇到以下错误:

MATCH (ingredient:Ingredient)
WHERE NOT (:Chef)-[:HAS_INGREDIENT]->(ingredient)
RETURN * LIMIT 100;

该特性已被弃用,将在未来的版本中移除。 列表强制转换为布尔值已被弃用。请考虑使用NOT isEmpty(...)代替。

解决方法:

MATCH (ingredient:Ingredient)
WHERE NOT EXISTS((:Chef)-[:HAS_INGREDIENT]->(ingredient))
RETURN * LIMIT 100;

1

我使用 Gremlin 完成了这个任务。我做到了。

x=[]

g.idx('Chef')[[name:'chef1']].as('chef')
.out('has_ingredient').as('alreadyHas').aggregate(x).back('chef')
.out('has_value').as('values')
.in('requires_value').as('recipes')
.out('requires_ingredient').as('ingredients').except(x).path()

这返回了所有缺失成分的路径。我无法用cypher语言来表达它,至少对于1.7版本而言。


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