Neo4j - 3个不同查询的联合
Neo4j - UNION of 3 different queries
我有一个组合查询有问题,它包含三个部分。
- 获取直接好友
- 获取朋友的朋友
- 获取其他-只需填写space即可限制
所以它应该总是 return 有限的用户,由直接朋友、朋友的朋友和其他人订购。前两部分非常快,这里没问题,但最后一部分很慢,而且随着 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
您在限制之前的第三个查询中做了很多工作。您可能希望尽快提高排序和 LIMIT。
在单个 MATCH 模式中预匹配朋友(和朋友的朋友)也会更有效,我们可以使用 *0..1
作为潜在下一个节点的可选关系。
还有一些样式建议,我发现为 lists/collections 保留复数形式是个好主意,否则使用单数形式,因为每行只有一个节点。
尝试第三部分:
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
的类型,那么我们可以添加一个可能触发索引支持排序的修改,从而改善这一点。
我有一个组合查询有问题,它包含三个部分。
- 获取直接好友
- 获取朋友的朋友
- 获取其他-只需填写space即可限制
所以它应该总是 return 有限的用户,由直接朋友、朋友的朋友和其他人订购。前两部分非常快,这里没问题,但最后一部分很慢,而且随着 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
您在限制之前的第三个查询中做了很多工作。您可能希望尽快提高排序和 LIMIT。
在单个 MATCH 模式中预匹配朋友(和朋友的朋友)也会更有效,我们可以使用 *0..1
作为潜在下一个节点的可选关系。
还有一些样式建议,我发现为 lists/collections 保留复数形式是个好主意,否则使用单数形式,因为每行只有一个节点。
尝试第三部分:
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
的类型,那么我们可以添加一个可能触发索引支持排序的修改,从而改善这一点。