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: "fred@example.net" },
   { "_id" : 2, name: "Frank N. Stine", cell: "012-345-9999" },
   { "_id" : 3, name: "Gren Dell", home: "987-654-3210", email: "beo@example.net" }
] )

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

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

聚合结果如下:

js 复制代码
{
  _id: 1,
  name: 'Fred',
  email: 'fred@example.net',
  cell: '',
  home: ''
},
{
  _id: 2,
  name: 'Frank N. Stine',
  email: '',
  cell: '012-345-9999',
  home: ''
},
{
  _id: 3,
  name: 'Gren Dell',
  email: 'beo@example.net',
  cell: '',
  home: '987-654-3210'
}
相关推荐
数据库小学妹13 分钟前
MySQL redo刷盘实测:innodb_flush_log_at_trx_commit与故障矩阵
运维·数据库·mysql
ByteRock16 分钟前
ClickHouse 表的生老“并”死:表实例、表元数据与并发 DDL
数据库
哈__28 分钟前
破除数据库排障碎片化:一体化全链路故障根因诊断实践
数据库
严同学正在努力1 小时前
SQL Server 15.0.2000.5(2019 CU5)性能分析基线构建实战教程
数据库·ai·oracle·dba
MC丶科1 小时前
软考架构师90天冲刺|DAY44·Redis高级应用
数据库·数据仓库·redis·缓存·oracle·容器·规格说明书
Boop_wu1 小时前
[redis] redis 快速入门
数据库·redis·github
reasonsummer2 小时前
【办公类-115-01】20260906育儿知识(家园小报)批量制作(2026年9月-2027年6月)
开发语言·数据库·c#
AC赳赳老秦2 小时前
环保监测公开数据应用:OpenClaw 抓取空气与水质公开监测数据,开展区域环境质量趋势分析
大数据·数据库·人工智能·python·php·deepseek·openclaw
Zhu7583 小时前
部署安全的pg数据库,搭配pgadmin实现web界面管理
前端·数据库·安全
她说..3 小时前
Redis项目实战整理
数据库·redis·缓存