如何在MongoDB中将子文档展平到根级别?

如何在MongoDB中将子文档展平到根级别?,mongodb,aggregation-framework,Mongodb,Aggregation Framework,例如,如果我有这样一个文档 { a: 1, subdoc: { b: 2, c: 3 } } 我怎样才能把它转换成这样的格式(不使用项目) 您可以使用和来完成此操作 db.collection.update({},{$set:{“b”:2,“c”:3},$unset:{“subdoc”:1}) 当然,如果集合中有很多文档,那么如上所述,您需要循环集合文档以获取值。MongoDB无法仅在更新操作中引用字段值: db.collection.find({“subdoc”

例如,如果我有这样一个文档

{
  a: 1,
  subdoc: {
    b: 2,
    c: 3
  }
}
我怎样才能把它转换成这样的格式(不使用
项目


您可以使用和来完成此操作

db.collection.update({},{$set:{“b”:2,“c”:3},$unset:{“subdoc”:1})
当然,如果集合中有很多文档,那么如上所述,您需要循环集合文档以获取值。MongoDB无法仅在更新操作中引用字段值:

db.collection.find({“subdoc”:{“$exists”:1}).forEach(函数(doc){
var setDoc={};
for(doc.subdoc中的变量k){
setDoc[k]=单据子单据[k];
}
db.collection.update(
{u id:doc.\u id},
{“$set”:setDoc,“$unset”:“subdoc”}
);
})
这还采用了一些安全的用法,以确保您选择的文档中实际存在“subdoc”键

您也可以使用。(推荐方式)。如果不想使用
项目
请选中此项

聚合([{$project{..}}])

下面是您案例的示例:

db.collectionName.aggregate
([
    { $project: { a: 1, 'b': '$subdoc.b', 'c': '$subdoc.c'} }
]);
为您提供您期望的输出,即

    {
        "a" : 1,
        "b" : 2,
        "c" : 3
    }
您可以将阶段一起使用,如下所示:

db.collection.aggregate([
    { "$addFields": { "subdoc.a": "$a" } },
    { "$replaceRoot": { "newRoot": "$subdoc" }  }
])

我想最简单的方法是在返回后使用
map
更改结果

collection.mapFunction = function(el) {
  el.b = el.subdoc.b;
  el.c = el.subdoc.c
  delete(el.subdoc);
  return el;
}

...

var result = await collection.aggregate(...).toArray();
result = result.map(collection.mapFunction);
我们可以使用别名for和运算符来执行此操作

let pipeline = [
   {
      "$replaceWith": {
         "$mergeObjects": [ { "a": "$a" }, "$subdoc" ]
      }
   }
];


Mongo 4.2
开始,聚合操作符可用于将一个文档替换为另一个文档(在本例中为子文档),作为@chridam提到的
$replaceRoot
的合成糖

因此,我们可以首先在子文档中包含根字段,以继续使用操作符(也在
Mongo 4.2
中介绍,作为
$addFields
的别名),然后使用
$replacebay
将整个文档替换为子文档:

// { a: 1, subdoc: { b: 2, c: 3 } }
db.collection.aggregate([
  { $set: { "subdoc.a": "$a" } },
  { $replaceWith: "$subdoc" }
])
// { b: 2, c: 3, a: 1 }

如果要查询嵌套数组元素并在根级别显示它 如果有两个嵌套数组,则应使用$unwind两次

db.mortgageRequests.aggregate( [
       { $unwind: "$arrayLevel1s" },
       { $unwind: "$arrayLevel1s.arrayLevel2s" },
       { $match: { "arrayLevel1s.arrayLevel2s.documentid" : { $eq: "6034bceead4ed2ce3951f38e" } } },
       { $replaceRoot: { newRoot: "$arrayLevel1s.arrayLevel2s" } }
    ] )

您还可以在
$mergeObjects
中使用
$$ROOT

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

所以mongoDB中没有内置的操作符/函数来实现这一点,对吗?@svg操作符就是我提到的。你不能仅仅在一次更新中得到一个字段的值,这也是我说的。好的,知道了,谢谢。我实际上是指一个接线员,在一次拍摄中。对不起,我说得不太清楚。顺便说一句,创造性地使用
$set
&
$unset
。恐怕循环的性能不如
$project
,对吗?如果是这样的话,我将坚持使用
$project
,但要多打字(子文档中有30多个元素)。@svg这两件事完全不同。您要么操纵实际文档,要么进行投影以重新塑造特定结果,而这只能通过聚合来完成。决定你想做哪一个。以不同的方式更改文档或显示结果。为什么在没有
项目的情况下坚持这样做?请回答这个问题,因为您的理由可能也排除了其他可能的答案。@Philipp子文档中有30多个元素,因此
project
意味着大量的键入。一个很好的理由可能是子文档会随时间而变化,因此,如果以后,其中会有一个“d”,您希望所有子文档都进入根目录。从MongoDB 3.4开始,有一个名为的聚合管道操作符,它允许您将
子文档
设置为新的
$$root
。但是,它完全取代了root。实际上,我不知道如何将
子文档
与原始根目录中已有的字段合并。也许这暗示了某人…任何$replaceRoot的替代品,在版本3.2中…mongodbI不认为你应该这样做。这是不好的,因为它运行在应用程序内存上,而不是数据库级别,但我不必重写$project子句中数据库的每个字段如果你在根目录中有很多字段,您可以将
mergeObjects
与特殊变量
$$ROOT
一起使用,例如:
db.collection.aggregate([{$replaceRoot:{newRoot:{$mergeObjects:[“$$ROOT”,“$subdoc”]}}},{$project:{subdoc:0}},]])
db.mortgageRequests.aggregate( [
       { $unwind: "$arrayLevel1s" },
       { $unwind: "$arrayLevel1s.arrayLevel2s" },
       { $match: { "arrayLevel1s.arrayLevel2s.documentid" : { $eq: "6034bceead4ed2ce3951f38e" } } },
       { $replaceRoot: { newRoot: "$arrayLevel1s.arrayLevel2s" } }
    ] )
{ $replaceWith: {
        $mergeObjects: [ "$$ROOT", "$subdoc" ]
} }