如何加速Neo4j Cypher中的协同过滤?

时间:2016-11-05 14:10:04

标签: neo4j cypher collaborative-filtering

这是基于来自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秒钟)

  1. 为什么第二个跑得更快?
  2. 如何进一步加快速度?

2 个答案:

答案 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可以帮助您出现的有关图表的信息;找到结果行计数爆炸的步骤,看看是否有办法将其分解以减少总行数。