这是基于来自Neo4j文档的Cypher示例:
MATCH (user:User)-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User)-[:ORDERS]->(recommended:Product)
WHERE NOT (user)-[:ORDERS]->(recommended)
AND user.id = 171
RETURN distinct recommended.id, count(distinct otherUser.id) as frequency
ORDER BY frequency DESC
LIMIT 200
以下是我所做的改进:
MATCH (user:User)-[:ORDERS]->(p:Product)
WHERE user.id = 171
WITH DISTINCT p, user
MATCH (p)<-[:ORDERS]-(otherUser:User)
WITH DISTINCT otherUser, user
MATCH (otherUser)-[:ORDERS]->(recommended:Product)
WHERE NOT (user)-[:ORDERS]->(recommended)
RETURN distinct recommended.id, count(distinct otherUser.id) as frequency
ORDER BY frequency DESC
LIMIT 200
两者都返回相同的结果,但第二个运行速度提高了6倍。 (但我的Macbook上还有3秒钟)
答案 0 :(得分:2)
您的查询获取p
个产品(您不想推荐),但最终会删除它们。这些p
节点可用于与recommended
节点进行比较,而不是丢弃它们,从而避免了处理WHERE NOT (user)-[:ORDERS]->(recommended)
所需的额外数据库命中(必须重新扫描{{1}的每个订单}} 每次)。这应该可以显着加快您的查询速度。
试试这个:
user
此外,我假设MATCH (user:User)-[:ORDERS]->(p:Product)<-[:ORDERS]-(otherUser:User)
WHERE user.id = 171
WITH COLLECT(DISTINCT otherUser) AS others, COLLECT(DISTINCT p) AS sharedProds
UNWIND others AS other
MATCH (other)-[:ORDERS]->(recommended:Product)
WHERE NOT recommended IN sharedProds
RETURN DISTINCT recommended.id, count(DISTINCT other) as frequency
ORDER BY frequency DESC
LIMIT 200;
个节点具有唯一的User
值,因此我使用id
代替count(DISTINCT otherUser)
,这应该更快。
答案 1 :(得分:1)
第一个查询可能比必要的慢,因为它将DISTINCT
应用于属性而不是节点,这会强制数据库在可能丢弃它们之前查找每个节点的属性。如果id
属性对于单个User
节点是唯一的(对于Product
节点是相同的),则效率会降低。这可能是同一查询的更快版本,如果您可以假设没有User
与另一个id
共享User
,并且没有Product
共享id
与另一个Product
:
MATCH (user:User)-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User)-[:ORDERS]->(recommended:Product)
WHERE NOT (user)-[:ORDERS]->(recommended)
AND user.id = 171
WITH recommended, COUNT(DISTINCT otherUser) AS frequency
RETURN recommended.id, frequency
ORDER BY frequency DESC
LIMIT 200
您的查询在每一步都将DISTINCT
应用于节点,因此它避免了此属性查找问题(这通常是查询中最昂贵的部分,尤其是大型查询)。但是,手动切断这样的长路径可能会降低查询速度,因为规划器可能无法看到它可以使用整个路径执行的优化。在几乎每个实例中,单个长路径优于多个短路径。
至于如何更快,更好,请记住图表可能是资源密集型的,因此如果您在笔记本电脑或小型云实例上并行运行,可以查看performance tuning看看你是否有点窒息你的数据库。
编辑:如果你有一个密集连接的图形(:Product
之间的许多共享:User
个节点),那么你可能也会使用第一个查询生成不必要的结果行。如果PROFILE
在查询中间显示大量结果行,请尝试使用此替代方法:
MATCH (user:User {id:171})-[:ORDERS]->(:Product)<-[:ORDERS]-(otherUser:User)
WITH DISTINCT user, otherUser
MATCH (otherUser)-[:ORDERS]->(recommended:Product)
WHERE NOT (user)-[:ORDERS]->(recommended)
WITH recommended, COUNT(DISTINCT otherUser) AS frequency
RETURN recommended.id, frequency
ORDER BY frequency DESC
LIMIT 200
这确保每个otherUser
节点仅检查一次以获取推荐,而第一个查询将针对与用户共享的每个otherUser
处理每个:Product
一次。这是PROFILE
可以帮助您出现的有关图表的信息;找到结果行计数爆炸的步骤,看看是否有办法将其分解以减少总行数。