MongoDB聚合:$replaceRoot

定义

$replaceRoot使用指定的文档替换输入文档。该操作可替换输入文档的所有字段,包括_id字段。可以将内嵌文档提升到顶层,可以为提升文档创建新文档。

注意:

从MongoDB4.2开始增加了$replaceWith,执行与$replaceRoot类似的动作,但形式有所不同。

$replaceRoot阶段有下面的形式:

js 复制代码
{ $replaceRoot: { newRoot: <replacementDocument> } }

替换文档可以是任何能解析为文档的表达式,如果<replacementDocument不是一个文档,该阶段操作会出错并失败。

行为

如果<replacementDocument>不是一个文档,$replaceRoot会出错并失败。

如果<replacementDocument>解析为一个错误的文档(如:文档不存在),$replaceRoot将出错并失败,例如,创建下面的一个集合:

js 复制代码
db.collection.insertMany([
   { "_id": 1, "name" : { "first" : "John", "last" : "Backus" } },
   { "_id": 2, "name" : { "first" : "John", "last" : "McCarthy" } },
   { "_id": 3, "name": { "first" : "Grace", "last" : "Hopper" } },
   { "_id": 4, "firstname": "Ole-Johan", "lastname" : "Dahl" },
])

进行下面的$replaceRoot操作将失败,因为其中的一个文档缺少name字段。

js 复制代码
db.collection.aggregate([
   { $replaceRoot: { newRoot: "$name" } }
])

为了避免这个错误,可以使用$mergeObjectsname文档合并到缺省文档,例如:

js 复制代码
db.collection.aggregate([
   { $replaceRoot: { newRoot: { $mergeObjects: [ { _id: "$_id", first: "", last: "" }, "$name" ] } } }
])

换种方式,也可以在$replaceRoot阶段之前,先使用一个$match阶段,跳过缺少name字段的文档:

js 复制代码
db.collection.aggregate([
   { $match: { name : { $exists: true, $not: { $type: "array" }, $type: "object" } } },
   { $replaceRoot: { newRoot: "$name" } }
])

或者,也可以使用$ifNull表达式指定其它文档成为根,例如:

js 复制代码
db.collection.aggregate([
   { $replaceRoot: { newRoot: { $ifNull: [ "$name", { _id: "$_id", missingName: true} ] } } }
])

举例

$replaceRoot与内嵌文档字段

名为people的集合中有如下文档:

json 复制代码
{ "_id" : 1, "name" : "Arlene", "age" : 34, "pets" : { "dogs" : 2, "cats" : 1 } }
{ "_id" : 2, "name" : "Sam", "age" : 41, "pets" : { "cats" : 1, "fish" : 3 } }
{ "_id" : 3, "name" : "Maria", "age" : 25 }

下面的操作使用$replaceRoot阶段替换所有$mergeObjects返回的文档。$mergeObjects表达式使用`pets'合并到指定的默认文档。

js 复制代码
db.people.aggregate( [
   { $replaceRoot: { newRoot: { $mergeObjects:  [ { dogs: 0, cats: 0, birds: 0, fish: 0 }, "$pets" ] }} }
] )

操作返回下面的结果:

json 复制代码
{ "dogs" : 2, "cats" : 1, "birds" : 0, "fish" : 0 }
{ "dogs" : 0, "cats" : 1, "birds" : 0, "fish" : 3 }
{ "dogs" : 0, "cats" : 0, "birds" : 0, "fish" : 0 }

$replaceRoot与内嵌数组文档

一个名为students的集合包含下面的文档:

js 复制代码
db.students.insertMany([
   {
      "_id" : 1,
      "grades" : [
         { "test": 1, "grade" : 80, "mean" : 75, "std" : 6 },
         { "test": 2, "grade" : 85, "mean" : 90, "std" : 4 },
         { "test": 3, "grade" : 95, "mean" : 85, "std" : 6 }
      ]
   },
   {
      "_id" : 2,
      "grades" : [
         { "test": 1, "grade" : 90, "mean" : 75, "std" : 6 },
         { "test": 2, "grade" : 87, "mean" : 90, "std" : 3 },
         { "test": 3, "grade" : 91, "mean" : 85, "std" : 4 }
      ]
   }
])

下面的操作将grade大于等于90的内嵌文档提升到顶层:

js 复制代码
db.students.aggregate( [
   { $unwind: "$grades" },
   { $match: { "grades.grade" : { $gte: 90 } } },
   { $replaceRoot: { newRoot: "$grades" } }
] )

操作返回下面的结果:

json 复制代码
{ "test" : 3, "grade" : 95, "mean" : 85, "std" : 6 }
{ "test" : 1, "grade" : 90, "mean" : 75, "std" : 6 }
{ "test" : 3, "grade" : 91, "mean" : 85, "std" : 4 }

replaceRoot与新创建的文档

可以利用$replaceRoot阶段创建新文档,并替换掉其他全部字段。

一个名为contacts的集合,包含有以下文档:

json 复制代码
{ "_id" : 1, "first_name" : "Gary", "last_name" : "Sheffield", "city" : "New York" }
{ "_id" : 2, "first_name" : "Nancy", "last_name" : "Walker", "city" : "Anaheim" }
{ "_id" : 3, "first_name" : "Peter", "last_name" : "Sumner", "city" : "Toledo" }

下面的操作创建的新文档来自于first_namelast_name字段:

js 复制代码
db.contacts.aggregate( [
   {
      $replaceRoot: {
         newRoot: {
            full_name: {
               $concat : [ "$first_name", " ", "$last_name" ]
            }
         }
      }
   }
] )

操作返回的结果:

json 复制代码
{ "full_name" : "Gary Sheffield" }
{ "full_name" : "Nancy Walker" }
{ "full_name" : "Peter Sumner" }

$replaceRoot与从$$ROOT创建的新文档和缺省文档

创建一个名为contaces集合,并包含以下文档:

js 复制代码
db.contacts.insertMany( [
   { "_id" : 1, name: "Fred", email: "[email protected]" },
   { "_id" : 2, name: "Frank N. Stine", cell: "012-345-9999" },
   { "_id" : 3, name: "Gren Dell", home: "987-654-3210", email: "[email protected]" }
] )

下面的操作使用$replaceRoot$mergeObjects输出当前文档,缺失的字段使用缺省值:

js 复制代码
db.contacts.aggregate( [
   { $replaceRoot:
      { newRoot:
         { $mergeObjects:
             [
                { _id: "", name: "", email: "", cell: "", home: "" },
                "$$ROOT"
             ]
          }
      }
   }
] )

聚合结果如下:

js 复制代码
{
  _id: 1,
  name: 'Fred',
  email: '[email protected]',
  cell: '',
  home: ''
},
{
  _id: 2,
  name: 'Frank N. Stine',
  email: '',
  cell: '012-345-9999',
  home: ''
},
{
  _id: 3,
  name: 'Gren Dell',
  email: '[email protected]',
  cell: '',
  home: '987-654-3210'
}
相关推荐
深圳厨神2 分钟前
mysql对表,数据,索引的操作sql
数据库·sql·mysql
谁家有个大人3 分钟前
数据分析问题思考路径
数据库·数据分析
小陈又菜13 分钟前
MySQL-触发器
数据库·mysql·database·触发器
爱的叹息18 分钟前
详解隔离级别(4种),分别用表格展示问题出现的过程及解决办法
数据库·oracle
平凡的小y18 分钟前
MySQL内置函数
数据库·mysql
佩奇的技术笔记27 分钟前
中级:MyBatis面试题深度剖析
数据库·mybatis
阿里云大数据AI技术1 小时前
百观科技基于阿里云 EMR 的数据湖实践分享
大数据·数据库
故事与他6451 小时前
TBKDVR硬盘录像机device.rsp命令执行漏洞
服务器·网络·数据库·安全·网络安全·apache
逊嘘1 小时前
【MySQL】表的增删改查(拓展)
java·数据库·mysql
猫咪-95271 小时前
Mysql之事务(上)
数据库·mysql