什么是最快的 ArangoDB 朋友的朋友查询(带计数)
What is the fastest ArangoDB friends-of-friends query (with count)
我正在尝试使用 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
并且仅使用 return 而 _id
。用includeData: false
GRAPH_NEIGHBORS
直接returns _id
我们也可以去掉这里的子查询
LET friends = GRAPH_NEIGHBORS('graph',
@user,
{"direction": "any",
"edgeExamples": {
name: "FRIENDS_WITH"
}})
这在我的机器上减少了大约 1.1 秒。所以我预计这将接近 Neo4J 的性能。
为什么这会产生很大的影响?
在内部,我们首先找到 _id
值而不实际加载文档 JSON。在您的查询中,您不需要任何这些数据,因此我们可以安全地继续而不打开它。
但现在真正的进步
您的查询采用 "logical" 方式,首先获取用户的邻居,然后找到他们的邻居,计算找到 foaf
的频率并对其进行排序。
这就得在内存中建立完整的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
相同,在这种情况下我们必须找到真正的边缘并研究它。如果您根据边缘的名称将边缘存储在单独的集合中,则可以避免这种情况。然后也删除 edgeExamples。这将进一步提高性能(尤其是在有很多边的情况下)。
未来
敬请关注我们的下一个版本,我们现在正在向 AQL 添加更多功能,这将使您的案例更易于查询,并且应该会进一步提升性能。
我正在尝试使用 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
并且仅使用 return 而 _id
。用includeData: false
GRAPH_NEIGHBORS
直接returns _id
我们也可以去掉这里的子查询
LET friends = GRAPH_NEIGHBORS('graph',
@user,
{"direction": "any",
"edgeExamples": {
name: "FRIENDS_WITH"
}})
这在我的机器上减少了大约 1.1 秒。所以我预计这将接近 Neo4J 的性能。
为什么这会产生很大的影响?
在内部,我们首先找到 _id
值而不实际加载文档 JSON。在您的查询中,您不需要任何这些数据,因此我们可以安全地继续而不打开它。
但现在真正的进步
您的查询采用 "logical" 方式,首先获取用户的邻居,然后找到他们的邻居,计算找到 foaf
的频率并对其进行排序。
这就得在内存中建立完整的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
相同,在这种情况下我们必须找到真正的边缘并研究它。如果您根据边缘的名称将边缘存储在单独的集合中,则可以避免这种情况。然后也删除 edgeExamples。这将进一步提高性能(尤其是在有很多边的情况下)。
未来
敬请关注我们的下一个版本,我们现在正在向 AQL 添加更多功能,这将使您的案例更易于查询,并且应该会进一步提升性能。