我还需要知道这个问题的答案,所以这里有一个可行的解决方案。
我确信代码需要为您定制,并且可以做一些改进,如果适用于此示例答案,请相应地发表评论。
解决方案是使用支持递归并构建树的 Foxx 微服务。我遇到的问题是循环路径,但我实现了一个最大深度限制来阻止这种情况,在下面的示例中硬编码为 10。
创建 Foxx 微服务:
- 创建一个新文件夹(例如递归树)
- 创建目录脚本
- 将文件
manifest.json和index.js放到根目录
- 将文件
setup.js放到脚本目录中
- 然后创建一个包含这三个文件的新 zip 文件(例如
Foxx.zip)
- 导航到 ArangoDB 管理控制台
- 点击服务 |添加服务
- 输入适当的挂载点,例如/我的/树
- 点击 Zip 标签
- 拖入您创建的
Foxx.zip 文件,它应该可以毫无问题地创建
- 如果出现错误,请确保集合
myItems 和 myConnections 不存在,并且名为 myGraph 的图表不存在,因为它将尝试使用示例数据创建它们。
- 然后导航到 ArangoDB 管理控制台,服务 | /我的/树
- 点击API
- 展开 /tree/{rootId}
- 提供 ItemA 的 rootId 参数并点击“试用”
- 您应该会从提供的根 ID 中看到结果。
如果 rootId 不存在,则不返回任何内容
如果 rootId 没有孩子,它会为“包含”返回一个空数组
如果 rootId 有循环的“包含”值,它会返回嵌套深度限制,我希望有一种更简洁的方法来阻止它。
以下是三个文件:
setup.js(位于脚本文件夹中):
'use strict';
const db = require('@arangodb').db;
const graph_module = require("org/arangodb/general-graph");
const itemCollectionName = 'myItems';
const edgeCollectionName = 'myConnections';
const graphName = 'myGraph';
if (!db._collection(itemCollectionName)) {
const itemCollection = db._createDocumentCollection(itemCollectionName);
itemCollection.save({_key: "ItemA" });
itemCollection.save({_key: "ItemB" });
itemCollection.save({_key: "ItemC" });
itemCollection.save({_key: "ItemD" });
itemCollection.save({_key: "ItemE" });
if (!db._collection(edgeCollectionName)) {
const edgeCollection = db._createEdgeCollection(edgeCollectionName);
edgeCollection.save({_from: itemCollectionName + '/ItemA', _to: itemCollectionName + '/ItemB'});
edgeCollection.save({_from: itemCollectionName + '/ItemB', _to: itemCollectionName + '/ItemC'});
edgeCollection.save({_from: itemCollectionName + '/ItemB', _to: itemCollectionName + '/ItemD'});
edgeCollection.save({_from: itemCollectionName + '/ItemD', _to: itemCollectionName + '/ItemE'});
}
const graphDefinition = [
{
"collection": edgeCollectionName,
"from":[itemCollectionName],
"to":[itemCollectionName]
}
];
const graph = graph_module._create(graphName, graphDefinition);
}
mainfest.json(位于根文件夹中):
{
"engines": {
"arangodb": "^3.0.0"
},
"main": "index.js",
"scripts": {
"setup": "scripts/setup.js"
}
}
index.js(位于根文件夹中):
'use strict';
const createRouter = require('@arangodb/foxx/router');
const router = createRouter();
const joi = require('joi');
const db = require('@arangodb').db;
const aql = require('@arangodb').aql;
const recursionQuery = function(itemId, tree, depth) {
const result = db._query(aql`
FOR d IN myItems
FILTER d._id == ${itemId}
LET contains = (
FOR c IN 1..1 OUTBOUND ${itemId} GRAPH 'myGraph' RETURN { "_id": c._id }
)
RETURN MERGE({"_id": d._id}, {"contains": contains})
`);
tree = result._documents[0];
if (depth < 10) {
if ((result._documents[0]) && (result._documents[0].contains) && (result._documents[0].contains.length > 0)) {
for (var i = 0; i < result._documents[0].contains.length; i++) {
tree.contains[i] = recursionQuery(result._documents[0].contains[i]._id, tree.contains[i], depth + 1);
}
}
}
return tree;
}
router.get('/tree/:rootId', function(req, res) {
let myResult = recursionQuery('myItems/' + req.pathParams.rootId, {}, 0);
res.send(myResult);
})
.response(joi.object().required(), 'Tree of child nodes.')
.summary('Tree of child nodes')
.description('Tree of child nodes underneath the provided node.');
module.context.use(router);
现在您可以调用 Foxx 微服务 API 端点,提供 rootId 它将返回完整的树。非常快。
ItemA 的示例输出是:
{
"_id": "myItems/ItemA",
"contains": [
{
"_id": "myItems/ItemB",
"contains": [
{
"_id": "myItems/ItemC",
"contains": []
},
{
"_id": "myItems/ItemD",
"contains": [
{
"_id": "myItems/ItemE",
"contains": []
}
]
}
]
}
]
}
可以看到Item B包含了两个子ItemC和ItemD,然后ItemD也包含了ItemE。
我等不及 ArangoDB AQL 改进了FOR v, e, p IN 1..100 OUTBOUND 'abc/def' GRAPH 'someGraph' 样式查询中可变深度路径的处理。不建议在 3.x 中使用自定义访问者,但并没有真正替换为处理路径中顶点深度的通配符查询或处理路径上的 prune 或 exclude 样式命令的强大功能遍历。
如果可以简化,希望得到 cmets/feedback。