Подтвердить что ты не робот

Каков самый быстрый запрос друзей ArangoDB друзей (со счетом)

Я пытаюсь использовать 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-10x).

То, что я хотел бы знать, - это... Как я могу улучшить наш запрос, чтобы он работал лучше?

4b9b3361

Ответ 1

Я один из основных разработчиков 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 и сортирует его. Это должно создать полную 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}

Какой на моем тестовом графике был выполнен в ~ 0.024 сек

Таким образом, это дало мне более быстрое время выполнения на 250 раз, и я ожидал бы, что это будет быстрее, чем ваш текущий запрос в Neo4j, но поскольку у меня нет вашего dataset, я не могу его проверить, было бы хорошо, если бы вы мог бы это сделать и сказать мне.

Последняя вещь

С edgeExamples: {name : "FRIENDS_WITH" } it is the same as with includeData`, в этом случае мы должны найти реальное ребро и изучить его. Этого можно избежать, если вы храните свои ребра в разных коллекциях на основе их имени. Затем удалите также примеры edgeExamples. Это еще больше повысит производительность (особенно если есть много краев).

Future

Оставайтесь с нами в связи с нашей следующей версией, мы сейчас добавили еще несколько функций для AQL, которые значительно упростят ваш запрос и должны повысить производительность.