我有一个文档如下:
{
"contents": [
{
"translationId": "MENU",
},
{
"translationId": "PAGETITLE"
}
],
"slides": [
{
"translationId": "SLIDE1",
"imageUrl": "assets/img/room/1.jpg",
"desc": {
"translationId": "DESC",
}
},
{
"translationId": "SLIDE2",
"imageUrl": "assets/img/aa/2.jpg"
}
]}
我想根据translationId进行聚合,无论数据在哪个子文档中。我当前的查询如下所示,没有给我预期的结果。
db.cursor.find({"contents.translationId": { $exists: true }},
{"contents.translationId":1,'slides.translationId':1,"slides.desc.translationId":1,'_id':0})
我期望的结果如下。是否有一个很好的方法来检索这样的结果直接从mongodb查询?
[
{
"translationId": "MENU"
},
{
"translationId": "PAGETITLE"
},
{
"translationId": "SLIDE1"
},
{
"translationId": "SLIDE2"
},
{
"translationId": "DESC"
}
]
另外,我可能不知道translationId可能存在于哪个元素中。在本例中,它位于contents、slides和slides.desc中,但也可能位于其他元素下。这可能吗?
谢谢!
只要条目是唯一的,您可以使用 $setUnion
操作符在现代MongoDB版本2.6及以上,以及 $map
操作符从其他数组中转换所需的元素:
db.cursor.aggregate([
{ "$project": {
"joined": {
"$setDifference": [
{ "$setUnion": [
"$contents",
{ "$map": {
"input": "$slides",
"as": "slide",
"in": {
"translationId": "$$slide.translationId"
}
}},
{ "$map": {
"input": "$slides",
"as": "slide",
"in": {
"$cond": [
{ "$ifNull": [ "$$slide.desc.translationId", false] },
{ "translationId": "$$slide.desc.translationId" },
false
]
}
}}
]},
[false]
]
}
}}
])
您还需要$setDifference
过滤掉返回的任何false
值,其中"desc"字段不存在。
它产生:
{
"_id" : ObjectId("55f13f444db9bc30de351c84"),
"joined" : [
{
"translationId" : "DESC"
},
{
"translationId" : "SLIDE2"
},
{
"translationId" : "SLIDE1"
},
{
"translationId" : "PAGETITLE"
},
{
"translationId" : "MENU"
}
]
}
当然,如果你完全不知道这个结构,那么你需要一个递归函数来代替mapReduce:
db.cursor.mapReduce(
function() {
var tags = [];
function walkObj(obj) {
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) == "object" ) {
walkObj(obj[key]);
} else if ( key == "translationId" ) {
tags.push({ "translationId": obj[key] })
}
});
}
walkObj(this);
emit(this._id,{ "joined": tags})
},
function(){},
{ "out": { "inline": 1 } }
)
与之前的输出基本相同,但当然不需要注意