2015-11-22 17 views
10

私は、ArangoDBをバックエンドに持つd3 force-directedグラフをベースにしたアプリケーションを構築しています.Arangoからノードとリンクのデータを動的にロードしたい可能な限り効率的にArangoDBを使ってArangoDBからd3のデータを取得する

私はd3の専門家ではありませんが、一般に、フォースレイアウトでは、データをノードの配列として、実際のノードオブジェクトをソースとターゲットとして持つリンクの配列が必要です。

var nodes = [ 
     {id: 0, reflexive: false}, 
     {id: 1, reflexive: true }, 
     {id: 2, reflexive: false} 
    ], 
    links = [ 
     {source: nodes[0], target: nodes[1], left: false, right: true }, 
     {source: nodes[1], target: nodes[2], left: false, right: true } 
    ]; 

現在、次のAQLクエリを使用して隣接ノードを取得していますが、非常に面倒です。難しいのは、それらのエッジが横切られていなくても(ノードがそれらのリンクをデータベースからロードする前に持っているリンクの数を表示するために)、ノードのエッジ情報を含めることです。

LET docId = "ExampleDocClass/1234567" 

// get data for all the edges 
LET es = GRAPH_EDGES('EdgeClass',docId,{direction:'any',maxDepth:1,includeData:true}) 

// create an array of all the neighbor nodes 
LET vArray = ( 
    FOR v IN GRAPH_TRAVERSAL('EdgeClass',docId[0],'any',{ maxDepth:1}) 
     FOR v1 IN v RETURN v1.vertex 
    ) 

// using node array, return inbound and outbound for each node 
LET vs = (
    FOR v IN vArray 
     // inbound and outbound are separate queries because I couldn't figure out 
     // how to get Arango to differentiate inbout and outbound in the query results 
     LET oe = (FOR oe1 IN GRAPH_EDGES('EdgeClass',v,{direction:'outbound',maxDepth:1,includeData:true}) RETURN oe1._to) 
     LET ie = (FOR ie1 IN GRAPH_EDGES('EdgeClass',v,{direction:'inbound',maxDepth:1,includeData:true}) RETURN ie1._from) 
     RETURN {'vertexData': v, 'outEdges': oe, 'inEdges': ie} 
    ) 
RETURN {'edges':es,'vertices':vs} 

エンド出力は次のようになります。D3にほとんど直接読み取ることができ http://pastebin.com/raw.php?i=B7uzaWxs が...(私は少しだけを重複排除しなければなりません)。

グラフノードにはリンクが多いため、パフォーマンスは重要です(サーバーとクライアントの負荷、およびそれらの間の通信のファイルサイズの両方)。また、隣接ノードを単純に拡張する以外に、グラフとやりとりするためのさまざまなコマンドを作成する予定です。このAQLクエリをより構造化する方法(4つの別々のグラフクエリを避けるなど)や、arangojs関数やFOXXアプリケーションを使用してAQLを回避する方法がありますが、d3に必要な形式)?

答えて

14

遅れて申し訳ありませんが、私たちはv2.8をビルドしていました;) ネットワーク上でJSONのコピーとシリアル化/逆シリアル化が一般的に高価なので、できるだけ多くのことをデータベース側で行うことをお勧めします。できるだけ少ないデータを転送することが良い目標になるはずです。

まず初めに私は作成したサンプルデータセット(800個の頂点と800個のエッジがデータセットにヒットしました)でクエリを使用して実行しました ベースラインとして私の場合はクエリの実行時間を使用しました〜5.0s

私はAQLだけで必要なものと全く同じ結果を作り出そうとしました。 私はあなたのクエリのいくつかの改善点を見つけました: 1. GRAPH_NEIGHBORSGRAPH_EDGESより少し速いです。 2.可能であれば、 のデータを必要としない場合は{includeData: true}を避けてください。特に、頂点に/から頂点が必要な場合は、GRAPH_NEIGHBORS{includeData: false}の間では、桁違いに大きくなります。 3. GRAPH_NEIGHBORSは重複排除され、GRAPH_EDGESは重複除外されません。あなたの場合はどちらが望ましいと思われる。 3.いくつかのサブクエリを取り除くことができます。だからここ

は、私が思い付くことができ、純粋なAQLクエリです:

LET docId = "ExampleDocClass/1234567" 
LET edges = GRAPH_EDGES('EdgeClass',docId,{direction:'any',maxDepth:1,includeData:true}) 
LET verticesTmp = (FOR v IN GRAPH_NEIGHBORS('EdgeClass', docId, {direction: 'any', maxDepth: 1, includeData: true}) 
    RETURN { 
    vertexData: v, 
    outEdges: GRAPH_NEIGHBORS('EdgeClass', v, {direction: 'outbound', maxDepth: 1, includeData: false}), 
    inEdges: GRAPH_NEIGHBORS('EdgeClass', v, {direction: 'inbound', maxDepth: 1, includeData: false}) 
    }) 
LET vertices = PUSH(verticesTmp, { 
    vertexData: DOCUMENT(docId), 
    outEdges: GRAPH_NEIGHBORS('EdgeClass', docId, {direction: 'outbound', maxDepth: 1, includeData: false}), 
    inEdges: GRAPH_NEIGHBORS('EdgeClass', docId, {direction: 'inbound', maxDepth: 1, includeData: false}) 
}) 
RETURN { edges, vertices } 

これはあなたのクエリと同じ結果フォーマットを生成し、DOCIDに接続されたすべての頂点が頂点に正確に一度保存されるという利点を有しています。また、docId自体は頂点に一度だけ格納されます。 クライアント側で重複排除は必要ありません。 しかし、各頂点のoutEdges/inEdgesでは、接続されたすべての頂点も正確に1回です。このリストの頂点間に複数の辺があるかどうかを知る必要があるかどうかはわかりません。

このクエリでは、私のデータセットに〜0.06sが使用されています。

しかし、もっと努力すれば、Foxxアプリケーションの中で手作業によるトラバーサルを使うことも考えられます。 これはもう少し複雑ですが、サブクエリを少なくすると、処理が高速になる場合があります。 このためのコードは次のようになります。

var traversal = require("org/arangodb/graph/traversal"); 
var result = { 
    edges: [], 
    vertices: {} 
} 
var myVisitor = function (config, result, vertex, path, connected) { 
    switch (path.edges.length) { 
    case 0: 
     if (! result.vertices.hasOwnProperty(vertex._id)) { 
     // If we visit a vertex, we store it's data and prepare out/in 
     result.vertices[vertex._id] = { 
      vertexData: vertex, 
      outEdges: [], 
      inEdges: [] 
     }; 
     } 

     // No further action 
     break; 
    case 1: 
     if (! result.vertices.hasOwnProperty(vertex._id)) { 
     // If we visit a vertex, we store it's data and prepare out/in 
     result.vertices[vertex._id] = { 
      vertexData: vertex, 
      outEdges: [], 
      inEdges: [] 
     }; 
     } 
     // First Depth, we need EdgeData 
     var e = path.edges[0]; 
     result.edges.push(e); 
     // We fill from/to for both vertices 
     result.vertices[e._from].outEdges.push(e._to); 
     result.vertices[e._to].inEdges.push(e._from); 
     break; 
    case 2: 
     // Second Depth, we do not need EdgeData 
     var e = path.edges[1]; 
     // We fill from/to for all vertices that exist 
     if (result.vertices.hasOwnProperty(e._from)) { 
     result.vertices[e._from].outEdges.push(e._to); 
     } 
     if (result.vertices.hasOwnProperty(e._to)) { 
     result.vertices[e._to].inEdges.push(e._from); 
     } 
     break; 
    } 
}; 
var config = { 
    datasource: traversal.generalGraphDatasourceFactory("EdgeClass"), 
    strategy: "depthfirst", 
    order: "preorder", 
    visitor: myVisitor, 
    expander: traversal.anyExpander, 
    minDepth: 0, 
    maxDepth: 2 
}; 
var traverser = new traversal.Traverser(config); 
traverser.traverse(result, {_id: "ExampleDocClass/1234567"}); 
return { 
    edges: result.edges, 
    vertices: Object.keys(result.vertices).map(function (key) { 
       return result.vertices[key]; 
      }) 
}; 

このトラバーサルのアイデアは離れて、最大2つのエッジに開始頂点から全ての頂点を訪問することです。 0〜1の深さのすべての頂点がデータとともに頂点オブジェクトに追加されます。 開始頂点に由来するすべてのエッジがエッジリストにデータとともに追加されます。 深度2のすべての頂点は、結果にoutEdges/inEdgesのみを設定します。

これは、verticesが重複排除されるという利点があります。 outEdges/inEdgesには、その間に複数の辺がある場合、接続されているすべての頂点が複数回含まれます。

このトラバーサルは、〜0.025sのデータセットで実行されるため、AQL専用ソリューションの2倍の速さです。

これはまだ助かります。

+0

これは非常に役に立ちます - ありがとう!最終的な頂点ドキュメントのプロパティが長さの配列に含まれていたので、 'vertexData:DOCUMENT(docId) 'を' vertexData:DOCUMENT(docId)[0] 'に変更しなければなりませんでした。頂点に直接取り付けられているのではなく1)。それ以外の場合は、魅力のように動作します。私はFOXXを学ぶのにもう少し時間があるときにFOXXコードに戻ってきますが、それは非常に役立ちます。 – ropeladder

+0

歓迎です;) – mchacki

関連する問題