在 cypher 中,仅返回具有最近关系的节点

问题描述

this 其他问题有关,但从关系的另一边接近。

这是场景。我正在模拟一个生活或曾经生活在一个或多个不同地点的人。关系中包括他们搬入时的开始日期(表示为自纪元以来的毫秒数)。

    (:Person{name:'bill'}) -[:livesAt {since:1111000}]->(:Place{name:'apartmentA'})    
    (:Person{name:'bill'}) -[:livesAt {since:2222000}]->(:Place{name:'apartmentB'})
    (:Person{name:'john'}) -[:livesAt {since:3333000}]->(:Place{name:'apartmentA'})
    (:Person{name:'chris'}) -[:livesAt {since:1100000}]->(:Place{name:'apartmentC'})
    (:Person{name:'chris'}) -[:livesAt {since:1122000}]->(:Place{name:'apartmentA'})

我想编写一个查询,返回仍然居住在给定位置的人员节点。如果在与此人的所有关系中,livesAt 关系具有最大的“因为”,则他们仍住在一个位置。

我正在尝试这样的事情:

MATCH (:Place {name: 'apartmentA'})<-[r]-(p:Person)
WITH max(r.since) as most_recent,p.name as pname
MATCH (t:Person {name:pname}) -[e]->(l:Place)
WITH t,l
ORDER BY e.since DESC
return t,l

如果我的查询适用于上面的示例,给定地点“apartmentA”,我希望得到 john 和 chris。

解决方法

为了找到你想要的东西,你必须确保你过滤到那些没有:livesAt 关系的人,因为他们的财产(表明他们现在住在别处)到不同的地方。这很重要,因为他们可能住在那个地方,搬到别处,然后又搬回来。

我们可以使用来自 Neo4j 4.x 的存在性子查询来为我们提供更好的控制来描述我们不想存在的模式。

MATCH (loc:Place {name: 'apartmentA'})<-[r:livesAt]-(p:Person)
WITH loc,max(r.since) as most_recent,p
WHERE NOT EXISTS {
    MATCH (p) -[r:livesAt]->(other)
    WHERE r.since > most_recent AND other <> loc
}
RETURN p.name

您也可以考虑对此进行改造,保持 :currentResidence 与他们当前住所的关系,并在他们搬家时更新(删除旧的,创建新的)。这是您已经拥有的 :livesAt 关系的补充(我假设您将它们用于其他查询)。这让您可以非常快速地根据当前居住地进行检查和匹配,而无需进行任何额外的过滤。

编辑:

如果你不想使用存在性子查询,我们可以使用模式的 OPTIONAL MATCH 代替,并且只过滤到其他节点为空的结果,这意味着不存在这样的模式:

MATCH (loc:Place {name: 'apartmentA'})<-[r:livesAt]-(p:Person)
WITH loc,p
OPTIONAL MATCH (p) -[r:livesAt]->(other)
WHERE r.since > most_recent AND other <> loc
WITH p,other
WHERE other IS NULL
RETURN p.name