19

我正在尝试使用 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 倍)。

我想知道的是......我怎样才能改进我们的查询以使其表现更好?

4

1 回答 1

22

我是核心开发人员之一,ArangoDB并试图优化您的查询。由于我没有你的dataset,我只能谈论我的测试dataset,如果你能验证我的结果,我很乐意听到。

首先,如果我在ArangoDB2.7 上运行,但在这种特殊情况下,我不希望与 2.6 有重大的性能差异。

在我的dataset情况下,我可以在约 7 秒内执行您的查询。第一个修复:在您的朋友声明中,您使用includeData: true并且只返回_id. withincludeData: false GRAPH_NEIGHBORS直接返回_id,我们也可以去掉这里的子查询

LET friends = GRAPH_NEIGHBORS('graph', 
                              @user,
                              {"direction": "any",
                               "edgeExamples": { 
                                   name: "FRIENDS_WITH"
               }})

这在我的机器上降低了 ~ 1.1 秒。所以我预计这将接近 Neo4J 的性能。

为什么这会产生很大的影响? 在内部,我们首先在_id没有实际加载文档 JSON 的情况下找到值。在您的查询中,您不需要任何此类数据,因此我们可以安全地继续不打开它。

但现在为了真正的改进

您的查询采用“逻辑”方式,首先获取用户邻居,然后找到他们的邻居,计算foaf找到 a 的频率并对其进行排序。这必须在内存中建立完整的 foaf 网络并将其作为一个整体进行排序。

你也可以用不同的方式来做: 1. 查找所有friends用户(仅_ids) 2. 查找所有foaf(完整文档) 3. 为每个foaf查找所有foaf_friends(仅_ids) 4. 查找和的交集friendsfoaf_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 添加更多功能,这将使您的案例更易于查询,并且应该会再次提升性能。

于 2015-10-23T13:55:06.633 回答