Neo4j:如何在UNION Cypher上实现SKIP和LIMIT?有什么替代方案?

3

我正试图在各种关系中创建活动源。目前唯一的方法是使用UNION运行三个语句。然而,你不能在所有联合的结果上LIMITORDER - 目前看来这是Neo的一个限制。

有人知道我如何重写下面的内容,以便我可以对整个结果进行排序和限制吗?

match (me:User {username:'bob'})-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(u:User)
return m as message, u as user, lower(type(r)) as activity, r.created_on as date  
order by date skip 0 limit 25

union match (me:User {username:'bob'})<-[r:Mentions]-(m:Message)-[:Owner]-(u:User) 
return m as message, u as user, lower(type(r)) as activity, r.created_on as date
order by date skip 0 limit 25

union match (me:User {username:'bob'})<-[r:Follows]-(u:User)
return NULL as message, u as user, lower(type(r)) as activity, r.created_on as date
order by date skip 0 limit 25

我找到了以下代码,它返回一个包含所需数据嵌套属性的单列,但我无法想出如何对最终集合进行排序.....
match (me:User {username:'bob'})-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(u:User)
with collect({activity:lower(type(r)), user:u, message:m, date:r.created_on}) as a1

optional match (me:User {username:'bob'})<-[r:Mentions]-(m:Message)-[:Owner]-(u:User) 
with collect({activity:lower(type(r)), user:u, message:m, date:r.created_on }) as a2, a1 

optional match (me:User {username:'bob'})<-[r:Follows]-(u:User) 
with collect({activity:lower(type(r)), user:u, date:r.created_on }) as  a3, a2, a1

with a3 + a2 +a1 as all
unwind all as activity
return activity
skip 0 limit 25

任何帮助都非常感激!
更新
现在我有了这个……
MATCH (me:User { username:'bob' })--(u:User)
OPTIONAL MATCH (me)-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(u)
WITH me, collect({ type:lower(type(r)), user:u, message:m, date:r.created_on }) AS a1
OPTIONAL MATCH (me)<-[r:Mentions]-(m:Message)<-[:Owner]-(u)
WITH me, collect({ type:lower(type(r)), user:u, message:m, date:r.created_on }) AS a2, a1
OPTIONAL MATCH (me)<-[r:Follows]-(u)
WITH collect({ type:lower(type(r)), user:u, date:r.created_on })+ a2 + a1 AS all
UNWIND all AS activity
WITH activity
WHERE  activity.type is not null
RETURN activity
ORDER BY activity.date
LIMIT 25;

你能看到这个代码有任何性能问题吗?

我在开头匹配了me--user,以确保只查找我与之存在某种关系的用户。然后在结尾过滤掉可选匹配中的NULL匹配。因为我手动收集了一个文字对象,如果没有匹配到任何内容,它会得到一个具有NULL条目的对象,所以我最后将其移除....

所有这些都是因为不能对POST UNION进行过滤!

3个回答

1
目前还不支持“后联合处理”的功能,但承诺会“尽快”支持(请参见neo4j issue 2725)。如果您希望尽快实现此功能,请在该问题下添加评论。
您尝试的解决方案接近正确。这个查询应该更有效:
MATCH (me:User { username:'bob' })-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(u:User)
WITH me, collect({ type:lower(type(r)), user:u, message:m, date:r.created_on }) AS a1
OPTIONAL MATCH (me)<-[r:Mentions]-(m:Message)<-[:Owner]-(u:User)
WITH me, collect({ type:lower(type(r)), user:u, message:m, date:r.created_on }) AS a2, a1
OPTIONAL MATCH (me)<-[r:Follows]-(u:User)
WITH collect({ type:lower(type(r)), user:u, date:r.created_on })+ a2 + a1 AS all
UNWIND all AS activity
RETURN activity
ORDER BY activity.date
LIMIT 25;

这个查询:

  • ORDERS BY 活动日期的组合,以返回最早的 25 个活动。这是您查询所需的主要更改。
  • 消除了不必要的 SKIP 0 子句。
  • 通过前两个 WITH 子句传递了 me,使得 OPTIONAL MATCH 子句不必重新查找 me

啊,没错,集合和展开!不知道为什么我没想到那个 ;) - Brian Underwood
我在思考我之前所说的关于随着用户数量增加而减速的问题,我认为这可能会更快地减速,因为你在WITH之间有三个不同的OPTIONAL MATCH子句,所以我认为在这种情况下它将会扫描三次User标签。 - Brian Underwood
太好了!看了@BrianUnderwood下面的评论,我应该先匹配meuser之间的任何关系,然后再选择性地匹配LikesReplyShare关系,因为可能没有喜欢、回复或分享链接... MATCH (me:User { username:'bob' })--(u:User) OPTIONAL MATCH (me:User { username:'bob' })-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(u) - Matt Bryson
1
你不能使用MATCH子句来定义一组可供选择的备选路径。你可以使用WHERE (path) OR (path) OR (path),但我认为这可能会有相同的性能问题,并且你仍然需要在OPTIONAL MATCH中重复路径以定义变量,以便返回。 - Brian Underwood
如果您有一个路径,您知道它始终会连接到您要查找的用户(比如您想通过“关注”关系限制查询),那么您可以首先在该关系上进行匹配,这将更加高效,因为它只需要遍历“关注”关系,这将大大缩小您的结果范围。但我可以理解为什么这可能不是一个好的解决方案。 - Brian Underwood
是的,说得好,实际上它忽略了一些活动。然而,如果我只匹配最终用户,MATCH (me:User { username:'bob' }), (user:User),我会得到与 me 没有关系的结果。那么如何使用可选路径呢?所以我想匹配我和一个用户,但只有他们通过示例中的一个或多个路径相关联时才匹配。 - Matt Bryson

0
你可以这样做:
MATCH (me:User {username:'bob'}), (user:User)
OPTIONAL MATCH (me)-[:Owner]->(m1:Message)<-[message_type:Likes|:Reply|:Share]-(user)
OPTIONAL MATCH (me)<-[mentions:Mentions]-(m2:Message)-[:Owner]-(user) 
OPTIONAL MATCH (me)<-[follows:Follows]-(user)
WITH *
WHERE message_type IS NOT NULL OR mentions IS NOT NULL OR follows IS NOT NULL
RETURN
  COALESCE(m1, m2) AS message,
  user,
  lower(COALESCE(type(message_type), type(mentions), type(follows))),
  COALESCE(message_type.date, mentions.date, follows.date) AS date
ORDER BY date
SKIP 0
LIMIT 25

这里的主要缺点(除了所有的COALESCE之外;)是由于从me到user只有可选匹配路径,所以你最终会将me与每个用户进行匹配,这意味着随着用户数量的增加,你的查询可能会变得更慢。这就是为什么我在WITH之后放置了WHERE,因为你需要过滤掉两个用户之间没有路径的所有情况。

编辑:

我刚刚意识到这是一个有问题的解决方案。如果两个或更多的OPTIONAL MATCH子句匹配,则只会得到一个结果。你可以像这样做:

MATCH (me:User {username:'bob'}), (user:User)
OPTIONAL MATCH (me)-[:Owner]->(message:Message)<-[message_type:Likes|:Reply|:Share]-(user)
OPTIONAL MATCH (me)<-[mentions:Mentions]-(mention_message:Message)-[:Owner]-(user) 
OPTIONAL MATCH (me)<-[follows:Follows]-(user)
WITH *
WHERE message_type IS NOT NULL OR mentions IS NOT NULL OR follows IS NOT NULL
RETURN
  message,
  mention_message
  user,
  type(message_type) AS message_type_type,
  type(mentions) AS mentions_type,
  type(follows) AS follows_type,
  message_type.date AS message_type_date,
  mentions.date AS mentions_date,
  follows.date AS follows_date
ORDER BY date
SKIP 0
LIMIT 25

但是我认为,根据你的匹配方式,你可能会得到重复的值。


0

我想用另一个建议来窃取你/ cybersam 的解决方案。我将以cybersam为基础进行工作。首先,我认为您不希望仅返回始终存在从user-[:Owner]->(:Message)-:Likes|:Reply|:Share]-(:User)的路径的结果,因此需要进行可选匹配。但是第二,我认为如果您只匹配最终用户变量一次,速度会更快。像这样:

MATCH (me:User { username:'bob' }), (user:User)
OPTIONAL MATCH (me)-[:Owner]->(m:Message)<-[r:Likes|:Reply|:Share]-(user)
WITH me, collect({ type:lower(type(r)), user:user, message:m, date:r.created_on }) AS a1
OPTIONAL MATCH (me)<-[r:Mentions]-(m:Message)<-[:Owner]-(user)
WITH me, collect({ type:lower(type(r)), user:user, message:m, date:r.created_on }) AS a2, a1
OPTIONAL MATCH (me)<-[r:Follows]-(user)
WITH collect({ type:lower(type(r)), user:user, date:r.created_on }) + a2 + a1 AS all
UNWIND all AS activity
RETURN activity
ORDER BY activity.date
LIMIT 25;

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