首页 > 解决方案 > Neo4j - 3 个不同查询的 UNION

问题描述

我对一个包含三个部分的组合查询有疑问。

  1. 结交直接朋友
  2. 结交朋友的朋友
  3. 获取其他人 - 只需填满空间以限制

所以它应该总是返回有限的用户,由直接朋友、朋友的朋友和其他人订购。前两部分非常快,在这里没问题,但最后一部分很慢,而且随着 db 的大小不断增长,它变得越来越慢。Person.number 和 Person.createdAt 上有索引。

有谁知道如何改进或重写此查询以提高性能?

MATCH (me:Person { number: $number })-[r:KNOWS]-(contact:Person { registered: "true" }) WHERE contact.number <> $number AND (r.state = "contact" OR r.state = "declined")
    MATCH (contact)-[:HAS_AVATAR]-(avatar:Avatar { primary: true })
    WITH contact, avatar
    RETURN contact AS friend, avatar, contact.createdAt AS rank
    ORDER BY contact.createdAt DESC
  UNION
    MATCH (me:Person { number: $number })-[:KNOWS]-(friend)-[:KNOWS { state: "accepted" }]-(friend_of_friend:Person { registered: "true" }) WHERE NOT friend.username = 'default' AND NOT (me)-[:KNOWS]-(friend_of_friend)
    MATCH (friend_of_friend)-[:HAS_AVATAR]-(avatar:Avatar { primary: true })
    OPTIONAL MATCH (friend_of_friend)-[rel:KNOWS]-(friend)
    RETURN friend_of_friend AS friend, avatar, COUNT(rel) AS rank
    ORDER BY rank DESC
  UNION
    MATCH (me:Person { number: $number })
    MATCH (others:Person { registered: "true" }) WHERE others.number <> $number AND NOT (me)-[:KNOWS]-(others) AND NOT (me)-[:KNOWS]-()-[:KNOWS { state: "accepted" }]-(others:Person { registered: "true" })
    MATCH (others)-[:HAS_AVATAR]->(avatar:Avatar { primary: true })
    OPTIONAL MATCH (others)-[rel:KNOWS { state: "accepted" }]-()
    WITH others, rel, avatar
    RETURN others AS friend, avatar, COUNT(rel) AS rank
    ORDER BY others.createdAt DESC
  SKIP $skip
  LIMIT $limit

以下是一些简介:

https://i.stack.imgur.com/LfNww.png

https://i.stack.imgur.com/0EO0r.png

最终的解决方案是将整个查询分解为三个并分别调用它们,在我们的例子中,它不会达到 99% 的第三个查询,前两个查询速度非常快。而且似乎即使达到了第三阶段,它仍然很快,所以也许 UNION 最慢的是整个事情。

const contacts = await this.neo4j.readQuery(`...
if (contacts.records.length < limit){
  const friendOfFriend = await this.neo4j.readQuery(`...
  if (contacts.records.length + friendOfFriend.records.length < limit){
    const others = await this.neo4j.readQuery(`...

merge all results

标签: neo4jgraph-databases

解决方案


您在限制之前的第三个查询中做了很多工作。您可能希望尽快将 ordering 和 LIMIT 向上移动。

在单个 MATCH 模式中与朋友(和朋友的朋友)进行预匹配也会更有效,我们可以将*0..1其用作与潜在下一个节点的可选关系。

只是一些风格建议,我发现为列表/集合保留复数并使用单数是个好主意,因为每行只有一个节点。

在第三部分试试这个:

  MATCH (me:Person { number: $number })
  OPTIONAL MATCH (me)-[:KNOWS]-()-[:KNOWS*0..1 { state: "accepted" }]-(other:Person {registered:"true"})
  WITH collect(DISTINCT other) as excluded
  MATCH (other:Person { registered: "true" }) WHERE other.createdAt < dateTime() AND other.number <> $number AND NOT other IN excluded
  WITH other
  ORDER BY other.createdAt DESC
  SKIP $skip
  LIMIT $limit
  MATCH (other)-[:HAS_AVATAR]->(avatar:Avatar { primary: true })
  WITH other, avatar, size((other)-[:KNOWS { state: "accepted" }]-()) AS rank
  RETURN other AS friend, avatar, rank

如果我们知道它的类型,createdAt那么我们可以添加一个可能触发索引支持排序的修改,这可以改善这一点。


推荐阅读