MongoDB - $项目嵌套文档到根级别

时间:2015-12-23 08:09:12

标签: mongodb

是否可以而不编写所有子文档的字段?

我们说我有以下文档结构:

{
  field1: a,
  subdoc: {
          field2: b,
          field3: c
  }
}

我想使用$project以便在根级别获取子文件:

{
  field1: a,
  field2: b,
  field3: c              
}

这只是在subdoc中有2个字段的示例,我的真实文档有很多字段,将来可能会添加或删除更多字段,所以我希望$project更加动态而不是分别指定所有字段。

1 个答案:

答案 0 :(得分:4)

对于MongoDB 3.6及更高版本,请使用聚合框架和 $replaceRoot 管道,该管道可与 $mergeObjects 运算符一起应用newRoot表达。

此表达式

{ "$mergeObjects": ["$subdoc", "$$ROOT"] }

会将文档中的顶级字段与subdoc嵌入字段中的顶级字段合并,因此最后您的聚合操作将如下所示:

db.collection.aggregate([
    { "$replaceRoot": { 
        "newRoot": { 
            "$mergeObjects": [ "$subdoc", "$$ROOT" ] 
        } 
    } },
    { "$project": { "subdoc": 0 } }  
])

否则,您需要一种机制来获取组合动态 $project 文档所需的所有动态键。这可以通过 Map-Reduce 来实现。以下mapreduce操作将使用所有键作为_id值填充单独的集合:

mr = db.runCommand({
    "mapreduce": "my_collection",
    "map" : function() {
        for (var key in this.subdoc) { emit(key, null); }
    },
    "reduce" : function(key, stuff) { return null; }, 
    "out": "my_collection" + "_keys"
})

要获取所有动态键的列表,请在生成的集合上运行distinct:

db[mr.result].distinct("_id")
["field2", "field3", ...]

现在给出上面的列表,您可以通过创建一个将在循环中设置其属性的对象来汇编 $project 聚合管道文档。通常,您的 $project 文档将具有以下结构:

var project = {
    "$project": {
        "field1": 1,
        "field2": "$subdoc.field2",
        "field3": "$subdoc.field3"
    }
};

因此,使用上面的子文档键列表,您可以使用JavaScript的 reduce() 方法动态构建上述内容:

var subdocKeys = db[mr.result].distinct("_id"),
    obj = subdocKeys.reduce(function (o, v){
      o[v] = "$subdoc." + v;
      return o;
    }, { "field1": 1 }),
    project = { "$project": obj };

db.collection.aggregate([project]);