2015-10-22 19 views
18

Sto cercando di utilizzare ArangoDB per ottenere un elenco di amici di amici. Non solo una semplice lista di amici di amici, voglio anche sapere quanti amici l'utente e l'amico di un amico hanno in comune e ordinare il risultato. Dopo vari tentativi di (ri) scrittura della query AQL migliori risultati, questo è quello che ho finito con:Qual è la query più veloce degli amici di amici ArangoDB (con conteggio)

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 

Purtroppo, le prestazioni non è buono come mi sarebbe piaciuto. Rispetto alle versioni Neo4j della stessa query (e dati), AQL sembra un po 'più lento (5-10 volte).

Quello che mi piacerebbe sapere è ... Come posso migliorare la nostra query per farlo funzionare meglio?

risposta

19

Sono uno degli sviluppatori principali di ArangoDB e ho cercato di ottimizzare la query. Poiché non ho il tuo dataset, posso solo parlare del mio test dataset e sarei lieto di sapere se è possibile convalidare i risultati.

Innanzitutto se tutto è in esecuzione su ArangoDB 2.7 ma in questo caso particolare non mi aspetto una differenza di prestazioni importante a 2.6.

Nel mio dataset ho potuto eseguire la query così com'è in ~ 7sec. Prima correzione: Nella frase dei tuoi amici usi includeData: true e restituisci solo il _id. Con includeData: falseGRAPH_NEIGHBORS restituisce direttamente il _id e possiamo anche sbarazzarci della sottoquery qui

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

Questo trovato giù a ~ 1,1 sec sulla mia macchina. Quindi mi aspetto che questo sarà vicino alle prestazioni di Neo4J.

Perché questo ha un impatto elevato? Inizialmente troviamo il valore _id senza caricare effettivamente i documenti JSON. Nella tua query non hai bisogno di questi dati, quindi possiamo tranquillamente continuare a non aprirli.

Ma ora il vero miglioramento

Vostri criteri va il modo "logico" e il primo ottiene utenti vicini di casa, che trova i loro vicini, conta quante volte un foaf viene trovato e lo ordina. Questo deve creare l'intera rete di foaf in memoria e ordinarla nel suo complesso.

Si può anche farlo in un modo diverso: 1. Trova tutti i friends di utenza (solo _ids) 2. Trova tutti i foaf (documento completo) 3. Per ogni foaf Trova tutti i foaf_friends (solo _ids) 4. Trovare l'intersezione di friends e foaf_friends e contarli

questa interrogazione desidera un

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} 

Quale nel mio grafico di prova è stato eseguito in ~ 0.024 sec

Quindi questo mi ha dato un fattore 250 più rapido time esecuzione e mi aspetto che questo sia più veloce del tuo query corrente in Neo4j, ma come non ho il tuo dataset non posso verificarlo, sarebbe bene se potessi farlo e dimmelo

Un'ultima cosa

Con il edgeExamples: {name : "FRIENDS_WITH" } it is the same as with includeData`, in questo caso dobbiamo trovare il bordo reale e esaminare il problema. Questo potrebbe essere evitato se si archiviano i bordi in raccolte separate in base al loro nome. E quindi rimuovere anche gli edgeExamples. Ciò aumenterà ulteriormente le prestazioni (specialmente se ci sono molti bordi).

Future

Rimanete sintonizzati per la nostra prossima release, siamo ora l'aggiunta di un po 'di funzionalità per AQL che renderà il vostro caso molto più facile per eseguire query e dovrebbe dare un altro incremento delle prestazioni.

+0

Grazie! Controllerò, verificherò e accetterò la tua risposta lunedì! Apprezziamo molto il fatto che abbiate avuto il tempo di rispondere alla nostra domanda;) –

+1

Nel nostro caso, il primo miglioramento è stato significativamente più rapido della nostra versione. Soprattutto le nostre query più lente hanno beneficiato dei tuoi miglioramenti. Ha davvero portato il risultato dell'AQL molto vicino alla versione Neo4j. Per quanto riguarda la seconda query, ha reso le nostre foaf-query più difficili, ma le query migliori sono state un po 'più lente :(. In ogni caso, il primo miglioramento ci ha aiutato molto;). –