问题 使用AQL(或arangojs)从ArangoDB获取d3的数据
我正在构建一个基于d3强制导向图的应用程序,后端有ArangoDB,我希望能够尽可能高效地从Arango动态加载节点和链接数据。
我不是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}
结束输出如下所示:
http://pastebin.com/raw.php?i=B7uzaWxs
...几乎可以直接读到d3(我只需要重复删除一点)。
我的图形节点具有大量链接,因此性能很重要(无论是在服务器和客户端上的负载,还是在两者之间进行通信的文件大小)。除了简单地扩展相邻节点之外,我还计划创建各种与图形交互的命令。有没有办法更好地构建此AQL查询(例如,通过避免四个单独的图形查询)或完全避免AQL使用arangojs函数或FOXX应用程序,同时仍然以我需要的d3格式构建响应(包括每个节点的链接数据) )?
5272
2017-11-22 14:19
起源
答案:
抱歉迟到的回复,我们正忙着建设v2.8;)
我建议在数据库端做尽可能多的事情,因为通过网络复制和序列化/反序列化JSON通常很昂贵,因此尽可能少地传输数据应该是一个很好的目标。
首先,我使用了您的查询并在我创建的样本数据集上执行它(在我的数据集中命中了~800个顶点和800个边)
作为基线,我使用了查询的执行时间,在我的例子中 〜5.0S
所以我试图在AQL中创建完全相同的结果。
我在您的查询中发现了一些改进:
1。 GRAPH_NEIGHBORS
比...快一点 GRAPH_EDGES
。
2.如果可能的话避免 {includeData: true}
如果你不需要数据
特别是如果你需要/来自vertices._id GRAPH_NEIGHBORS
同 {includeData: false}
性能优于 GRAPH_EDGES
一个数量级。
3. GRAPH_NEIGHBORS是重复数据删除,GRAPH_EDGES不是。在你的情况下似乎是需要的。
你可以在那里摆脱几个子查询。
所以这里是我可以提出的纯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中,所有连接的顶点也只是一次,我不知道你是否需要知道这个列表中顶点之间是否还有多条边。
此查询使用 〜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];
})
};
这种遍历的想法是访问从起始顶点到最多两个边缘的所有顶点。
0 - 1深度中的所有顶点将与顶点对象中的数据相加。
源自起始顶点的所有边将与边数列表中的数据相加。
深度为2的所有顶点仅在结果中设置outEdges / inEdges。
这样做的优点是, vertices
重复数据删除。和outEdges / inEdges包含所有连接的顶点,如果它们之间有多个边。
此遍历在我的数据集中执行 〜0.025s 所以它的速度是AQL唯一解决方案的两倍。
希望这仍然有帮助;)
14
2017-12-04 07:43
答案:
抱歉迟到的回复,我们正忙着建设v2.8;)
我建议在数据库端做尽可能多的事情,因为通过网络复制和序列化/反序列化JSON通常很昂贵,因此尽可能少地传输数据应该是一个很好的目标。
首先,我使用了您的查询并在我创建的样本数据集上执行它(在我的数据集中命中了~800个顶点和800个边)
作为基线,我使用了查询的执行时间,在我的例子中 〜5.0S
所以我试图在AQL中创建完全相同的结果。
我在您的查询中发现了一些改进:
1。 GRAPH_NEIGHBORS
比...快一点 GRAPH_EDGES
。
2.如果可能的话避免 {includeData: true}
如果你不需要数据
特别是如果你需要/来自vertices._id GRAPH_NEIGHBORS
同 {includeData: false}
性能优于 GRAPH_EDGES
一个数量级。
3. GRAPH_NEIGHBORS是重复数据删除,GRAPH_EDGES不是。在你的情况下似乎是需要的。
你可以在那里摆脱几个子查询。
所以这里是我可以提出的纯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中,所有连接的顶点也只是一次,我不知道你是否需要知道这个列表中顶点之间是否还有多条边。
此查询使用 〜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];
})
};
这种遍历的想法是访问从起始顶点到最多两个边缘的所有顶点。
0 - 1深度中的所有顶点将与顶点对象中的数据相加。
源自起始顶点的所有边将与边数列表中的数据相加。
深度为2的所有顶点仅在结果中设置outEdges / inEdges。
这样做的优点是, vertices
重复数据删除。和outEdges / inEdges包含所有连接的顶点,如果它们之间有多个边。
此遍历在我的数据集中执行 〜0.025s 所以它的速度是AQL唯一解决方案的两倍。
希望这仍然有帮助;)
14
2017-12-04 07:43