什么是最快的ArangoDB朋友的朋友查询(有计数)

Ter*_*ler 18 graph-databases arangodb 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
Run Code Online (Sandbox Code Playgroud)

不幸的是,性能并不像我想的那么好.与同一查询(和数据)的Neo4j版本相比,AQL似乎相当慢(5-10倍).

我想知道的是......我如何改进查询以使其表现更好?

mch*_*cki 21

我是其核心开发人员之一,ArangoDB并尝试优化您的查询.因为我没有你,dataset我只能谈论我的测试dataset,并且很高兴听到你是否可以验证我的结果.

首先,如果我在ArangoDB2.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"
               }})
Run Code Online (Sandbox Code Playgroud)

这使我的机器下降到~1.1秒.所以我希望这将接近Neo4J的性能.

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

但现在真正改善了

您的查询采用"逻辑"方式,首先获取用户邻居,而不是查找其邻居,计算找到的频率foaf并对其进行排序.这必须在内存中构建完整的foaf网络并将其整体排序.

您也可以以不同的方式执行此操作:1.查找所有friends用户(仅限_ids)2.查找全部foaf(完整文档)3.对于每个foaf查找全部foaf_friends(仅限_ids)4.找到friends&foaf_friends和COUNT 的交集

这个查询是这样的:

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}
Run Code Online (Sandbox Code Playgroud)

我的测试图中的哪个在~0.024秒内执行

所以这给了我250倍的执行时间,我希望这比你在Neo4j中的当前查询更快,但是由于我没有你的dataset我无法验证它,如果你能做到并告诉我它会很好.

最后一件事

edgeExamples: {name : "FRIENDS_WITH" }它相同includeData,在这种情况下,我们必须找到真正的优势并进行调查.如果根据名称将边存储在单独的集合中,则可以避免这种情况.然后删除edgeExamples.这将进一步提高性能(特别是如果有很多边缘).

未来

请继续关注我们的下一个版本,我们现在正在为AQL添加更多功能,这将使您的案例更容易查询,并应该提供另一个性能提升.

  • 在我们的例子中,您的第一次改进明显快于我们的版本.特别是我们最慢的查询受益于您的改进.它确实使AQL结果非常接近Neo4j版本.至于第二个查询 - 它使我们的最坏情况foaf查询更快,但最好的情况查询有点慢:(.无论哪种方式,第一次改进帮助了我们很多;). (2认同)