我试图使用ArangoDB来获取朋友的朋友列表。不仅仅是一个基本的朋友的朋友列表,我还想知道用户和朋友的朋友有多少共同的朋友,并对结果进行排序。在多次尝试(重新)编写性能最好的AQL查询后,我最终得到了以下结果:
LET friends = (
FOR f IN GRAPH_NEIGHBORS('graph', @user, {"direction": "any", "includeData": true, "edgeExamples": { name: "FRIENDS_WITH"}})
RETURN f._id
)
LET foafs = (FOR friend IN friends
FOR foaf in GRAPH_NEIGHBORS('graph', friend, {"direction": "any", "includeData": true, "edgeExamples": { name: "FRIENDS_WITH"}})
FILTER foaf._id != @user AND foaf._id NOT IN friends
COLLECT foaf_result = foaf WITH COUNT INTO common_friend_count
RETURN {
user: foaf_result,
common_friend_count: common_friend_count
}
)
FOR foaf IN foafs
SORT foaf.common_friend_count DESC
RETURN foaf
不幸的是,性能没有我希望的那么好。与相同查询(和数据)的Neo4j版本相比,AQL似乎要慢一些(5-10倍)。
我想知道的是……我如何改进我们的查询,使其执行得更好?我是ArangoDB
的核心开发人员之一,并试图优化您的查询。由于我没有你的dataset
,我只能谈论我的测试dataset
,如果你能验证我的结果,我将很高兴听到。
首先,如果我在ArangoDB
2.7上运行,但在这种特殊情况下,我不期望与2.6有重大的性能差异。
在我的dataset
我可以执行你的查询,因为它是在~7秒。第一个解决办法:在你的朋友声明中,你使用includeData: true
,只返回_id
。对于includeData: false
, GRAPH_NEIGHBORS
直接返回_id
,我们也可以摆脱这里的子查询
LET friends = GRAPH_NEIGHBORS('graph',
@user,
{"direction": "any",
"edgeExamples": {
name: "FRIENDS_WITH"
}})
这使它在我的机器上下降到~ 1.1秒。所以我希望这将接近Neo4J的性能。
为什么这有很大的影响?在内部,我们首先找到_id
值,而不实际加载文档JSON。在您的查询中,您不需要任何这些数据,因此我们可以安全地继续不打开它。
但现在真正的改进
您的查询以"逻辑"方式进行,首先获得用户邻居,然后找到他们的邻居,计算找到foaf
的频率并对其进行排序。这必须在内存中建立完整的泡沫网络,并将其作为一个整体进行排序。
你也可以用另一种方法:1. 查找用户的所有friends
(仅_ids
)2. 找到所有foaf
(完整文档)3.对于每个foaf
,找到所有foaf_friends
(仅_ids
)4. 找出friends
和foaf_friends
的交点并计算它们
这个查询像这样:
LET fids = GRAPH_NEIGHBORS("graph",
@user,
{
"direction":"any",
"edgeExamples": {
"name": "FRIENDS_WITH"
}
}
)
FOR foaf IN GRAPH_NEIGHBORS("graph",
@user,
{
"minDepth": 2,
"maxDepth": 2,
"direction": "any",
"includeData": true,
"edgeExamples": {
"name": "FRIENDS_WITH"
}
}
)
LET commonIds = GRAPH_NEIGHBORS("graph",
foaf._id, {
"direction": "any",
"edgeExamples": {
"name": "FRIENDS_WITH"
}
}
)
LET common_friend_count = LENGTH(INTERSECTION(fids, commonIds))
SORT common_friend_count DESC
RETURN {user: foaf, common_friend_count: common_friend_count}
在我的测试图中,在~ 0.024秒内执行
所以这给了我一个因子250快执行时间,我希望这比你当前在Neo4j中的查询更快,但是因为我没有你的dataset
我无法验证它,如果你能做到这一点并告诉我,那将是很好的。
最后一件事
对于edgeExamples: {name : "FRIENDS_WITH" }
,它与includeData
相同,在这种情况下,我们必须找到真正的边缘并查看它。如果根据边的名称将边存储在单独的集合中,则可以避免这种情况。然后删除边的例子。这将进一步提高性能(特别是如果有很多边)。
请继续关注我们的下一个版本,我们现在正在为AQL添加一些更多的功能,这将使您的案例更容易查询,并且应该会带来另一个性能提升。