对 MongoDB 中的集合进行递归搜索
Recursive search on a collection in MongoDB
我在 MongoDB 中有一个树结构的文档列表,其中使用了 Model Tree Structures with Parent References 模式。我想要一个 returns 祖先列表(直到根)的单个聚合查询,给定 'name' 属性.
结构:
{
'_id': '1',
'name': 'A',
'parent': '',
},
{
'_id': '2',
'name': 'B',
'parent': 'A',
},
{
'_id': '3',
'name': 'C',
'parent': 'B',
},
{
'_id': '4',
'name': 'D',
'parent': 'C',
}
聚合结果:(给定,名称='D')
{
'_id': '4',
'name': 'D',
'ancestors': [{name:'C'}, {name:'B'}, {name:'A'}]
}
Note:
我现在无法更改文档结构。会造成很多问题。我看到许多建议使用 Model Tree Structures with an Array of Ancestors 的解决方案。但我现在不能使用它。有什么方法可以使用单一聚合查询通过上述模式实现吗?谢谢
从MongoDB 3.4开始,我们可以使用聚合框架来做到这一点。
我们管道中的第一个也是最重要的阶段是 $graphLookup
阶段。 $graphLookup
允许我们在 "parent" 和 "name" 字段上递归匹配。结果,我们得到了每个 "name".
的祖先
管道的下一个阶段是 $match
阶段,我们只是 select 我们感兴趣的 "name"。
最后阶段是 $addFields
or $project
stage where we apply an expression to the "ancestors" array using the $map
数组运算符。
当然要配合$reverseArray
operator we reverse our array才能得到预期的结果。
db.collection.aggregate(
[
{ "$graphLookup": {
"from": "collection",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "name",
"as": "ancestors"
}},
{ "$match": { "name": "D" } },
{ "$addFields": {
"ancestors": {
"$reverseArray": {
"$map": {
"input": "$ancestors",
"as": "t",
"in": { "name": "$$t.name" }
}
}
}
}}
]
)
如果您愿意使用客户端 javascript,您可以在 mongo shell 上使用递归来实现此目的:
var pushAncesstors = function (name, doc) {
if(doc.parent) {
db.collection.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
pushAncesstors(name, db.collection.findOne({name : doc.parent}))
}
}
db.collection.find().forEach(function (doc){
pushAncesstors(doc.name, doc);
})
这将为您提供所有产品的完整层次结构。示例输出:
{ "_id" : "1", "name" : "A", "parent" : "" }
{ "_id" : "2", "name" : "B", "parent" : "A", "ancesstors" : [ { "name" : "A" } ] }
{ "_id" : "3", "name" : "C", "parent" : "B", "ancesstors" : [ { "name" : "B" }, { "name" : "A" } ] }
{ "_id" : "4", "name" : "D", "parent" : "C", "ancesstors" : [ { "name" : "C" }, { "name" : "B" }, { "name" : "A" } ] }
如果您的要求不是更新正确的集合,请将数据插入不同的集合并在那里更新。 pushAncesstors
函数将更改为:
var pushAncesstors = function (name, doc) {
if(doc.parent) {
db.outputColl.save(doc)
db.outputColl.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
pushAncesstors(name, db.collection.findOne({name : doc.parent}))
}
}
我在 MongoDB 中有一个树结构的文档列表,其中使用了 Model Tree Structures with Parent References 模式。我想要一个 returns 祖先列表(直到根)的单个聚合查询,给定 'name' 属性.
结构:
{
'_id': '1',
'name': 'A',
'parent': '',
},
{
'_id': '2',
'name': 'B',
'parent': 'A',
},
{
'_id': '3',
'name': 'C',
'parent': 'B',
},
{
'_id': '4',
'name': 'D',
'parent': 'C',
}
聚合结果:(给定,名称='D')
{
'_id': '4',
'name': 'D',
'ancestors': [{name:'C'}, {name:'B'}, {name:'A'}]
}
Note:
我现在无法更改文档结构。会造成很多问题。我看到许多建议使用 Model Tree Structures with an Array of Ancestors 的解决方案。但我现在不能使用它。有什么方法可以使用单一聚合查询通过上述模式实现吗?谢谢
从MongoDB 3.4开始,我们可以使用聚合框架来做到这一点。
我们管道中的第一个也是最重要的阶段是 $graphLookup
阶段。 $graphLookup
允许我们在 "parent" 和 "name" 字段上递归匹配。结果,我们得到了每个 "name".
管道的下一个阶段是 $match
阶段,我们只是 select 我们感兴趣的 "name"。
最后阶段是 $addFields
or $project
stage where we apply an expression to the "ancestors" array using the $map
数组运算符。
当然要配合$reverseArray
operator we reverse our array才能得到预期的结果。
db.collection.aggregate(
[
{ "$graphLookup": {
"from": "collection",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "name",
"as": "ancestors"
}},
{ "$match": { "name": "D" } },
{ "$addFields": {
"ancestors": {
"$reverseArray": {
"$map": {
"input": "$ancestors",
"as": "t",
"in": { "name": "$$t.name" }
}
}
}
}}
]
)
如果您愿意使用客户端 javascript,您可以在 mongo shell 上使用递归来实现此目的:
var pushAncesstors = function (name, doc) {
if(doc.parent) {
db.collection.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
pushAncesstors(name, db.collection.findOne({name : doc.parent}))
}
}
db.collection.find().forEach(function (doc){
pushAncesstors(doc.name, doc);
})
这将为您提供所有产品的完整层次结构。示例输出:
{ "_id" : "1", "name" : "A", "parent" : "" }
{ "_id" : "2", "name" : "B", "parent" : "A", "ancesstors" : [ { "name" : "A" } ] }
{ "_id" : "3", "name" : "C", "parent" : "B", "ancesstors" : [ { "name" : "B" }, { "name" : "A" } ] }
{ "_id" : "4", "name" : "D", "parent" : "C", "ancesstors" : [ { "name" : "C" }, { "name" : "B" }, { "name" : "A" } ] }
如果您的要求不是更新正确的集合,请将数据插入不同的集合并在那里更新。 pushAncesstors
函数将更改为:
var pushAncesstors = function (name, doc) {
if(doc.parent) {
db.outputColl.save(doc)
db.outputColl.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
pushAncesstors(name, db.collection.findOne({name : doc.parent}))
}
}