2015-10-22 13 views
18

Ich versuche, ArangoDB zu verwenden, um eine Liste von Freunden von Freunden zu erhalten. Nicht nur eine einfache Freundesliste, ich möchte auch wissen, wie viele Freunde der Benutzer und der Freund eines Freundes gemeinsam haben und das Ergebnis sortieren. Nach mehreren Versuchen auf (Re), um die beste Ergebnis erzielt AQL-Abfrage zu schreiben, ist es das, was ich am Ende mit:Was ist die schnellste ArangoDB Freunde-von-Freunden-Abfrage (mit Anzahl)

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 

Leider Leistung ist nicht so gut, wie ich es gerne hätte. Verglichen mit den Neo4j-Versionen derselben Abfrage (und Daten) scheint AQL ziemlich langsam (5-10x) zu sein.

Was ich gerne wissen würde ... Wie kann ich unsere Abfrage verbessern, damit sie besser funktioniert?

Antwort

19

Ich bin einer der Kernentwickler von ArangoDB und versuchte, Ihre Abfrage zu optimieren. Da ich Ihre dataset nicht habe, kann ich nur über meinen Test sprechen dataset und wäre glücklich zu hören, wenn Sie meine Ergebnisse validieren können.

Zuerst, wenn alle ich auf ArangoDB 2.7 laufen, aber in diesem speziellen Fall erwarte ich keinen großen Leistungsunterschied zu 2.6.

In meinem dataset konnte ich Ihre Abfrage ausführen, wie es in ~ 7sec ist. Erste Lösung: In Ihrer Freunde-Anweisung verwenden Sie includeData: true und geben nur die _id zurück. Mit includeData: falseGRAPH_NEIGHBORS liefert direkt die _id und wir können auch hier der Subquery

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

Das hat es loszuwerden bis ~ 1,1 sec auf meinem Rechner. Also erwarte ich, dass dies der Leistung von Neo4J nahe kommt.

Warum hat dies eine hohe Auswirkung? Intern finden wir zuerst den _id Wert, ohne die Dokumente JSON tatsächlich zu laden. In Ihrer Anfrage benötigen Sie keine dieser Daten, so dass wir sicher fortfahren können, sie nicht zu öffnen.

Aber jetzt für die wirkliche Verbesserung

Ihre Abfrage des „logischen“ Weg geht und bekommt erste Nutzer Nachbarn, als ihre Nachbarn findet, zählt, wie oft ein foaf und es sortiert gefunden wird. Dies muss das komplette Foaf-Netzwerk im Speicher aufbauen und es als Ganzes sortieren.

Sie können es auch tun, in einer anderen Art und Weise: 1. Alle Benutzer suchen friends (nur _ids) 2. Hier finden Sie alle foaf (vollständige Dokument) 3. Für jeden foaf alle foaf_friends finden (nur _ids) 4. Suchen sie die Kreuzung von friends und foaf_friends und COUNT sie

diese Abfrage dies möchte:

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} 

Was in meinem Testgraph wurde in ~ 0 ausgeführt.024 sec

So gab diesen mir einen Faktor 250 schnelle Ausführungszeit, und ich würde erwarten, dass dies schneller sein als die aktuelle Abfrage in Neo4j, aber da ich nicht habe Ihre dataset ich es nicht überprüfen kann, es wäre gut, wenn du es tun könntest und es mir erzählst.

Eine letzte Sache

Mit dem edgeExamples: {name : "FRIENDS_WITH" } it is the same as with includeData`, in diesem Fall wir die wirkliche Kante zu finden, und schauen hinein. Dies könnte vermieden werden, wenn Sie Ihre Kanten in separaten Sammlungen basierend auf ihrem Namen speichern. Und entfernen Sie dann auch die edgeExamples. Dies wird die Leistung weiter erhöhen (besonders wenn es viele Kanten gibt).

Zukunft

Aufenthalt für unsere nächste Veröffentlichung abgestimmt werden wir jetzt noch mehr Funktionalität zu AQL Zugabe, die Ihren Fall machen wird viel einfacher abfragen und sollte eine weitere Leistungssteigerung geben.

+0

Danke! Ich überprüfe, bestätige und akzeptiere deine Antwort Montag! Wir schätzen es sehr, dass Sie sich die Zeit genommen haben, unsere Frage zu beantworten;) –

+1

In unserem Fall war Ihre erste Verbesserung deutlich schneller als unsere Version. Vor allem unsere langsamsten Abfragen haben von Ihren Verbesserungen profitiert. Es hat tatsächlich das AQL-Ergebnis der Neo4j-Version sehr nahe gebracht. Wie für die zweite Abfrage - es machte unsere Worst-Case Foaf-Abfragen schneller, aber der Best-Case-Abfragen ein bisschen langsamer :(. Wie auch immer, die erste Verbesserung hat uns sehr geholfen;). –