neo4j - Neo4j - 3 个不同查询的 UNION
问题描述
我对一个包含三个部分的组合查询有疑问。
- 结交直接朋友
- 结交朋友的朋友
- 获取其他人 - 只需填满空间以限制
所以它应该总是返回有限的用户,由直接朋友、朋友的朋友和其他人订购。前两部分非常快,在这里没问题,但最后一部分很慢,而且随着 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
解决方案
您在限制之前的第三个查询中做了很多工作。您可能希望尽快将 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
那么我们可以添加一个可能触发索引支持排序的修改,这可以改善这一点。
推荐阅读
- php - 如何在 Symfony 中的选择中添加第一个空行
- python - 我们是否应该将结果保存在我们可以随时使用数据计算的数据库模型中?
- solr - 在 Solr 和 DB 之间切换以进行 alfresco cmis 查询执行
- python - 如何将csv文件行拆分为python中的列?
- javascript - JavaScript 语言在后台加载,Vs 2015 滞后,有时重新启动
- spring-boot - 将 Spring Boot 版本从 2.0.3.RELEASE 更改为 2.1.0.M4 时出现问题
- ios - 在 iOS 中选择部署目标
- mdriven - PropertyChanged 事件是如何使用的
- reactjs - 将 CSS 编写为 JS 对象与普通 CSS 相比的优势?
- ubuntu - ubuntu 18.10 上的 .Net 核心 sdk