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'
}
相关推荐
Σίσυφος190021 分钟前
PCL法向量估计 之 方向约束法向量(Orientation Guided Normal)
数据库
老毛肚24 分钟前
手写mybatis
java·数据库·mybatis
海山数据库30 分钟前
移动云大云海山数据库(He3DB)postgresql_anonymizer插件原理介绍与安装
数据库·he3db·大云海山数据库·移动云数据库
云飞云共享云桌面33 分钟前
高性能图形工作站的资源如何共享给10个SolidWorks研发设计用
linux·运维·服务器·前端·网络·数据库·人工智能
2501_9279935341 分钟前
SQL Server 2022安装详细教程(图文详解,非常详细)
数据库·sqlserver
星火s漫天42 分钟前
第一篇: 使用Docker部署flask项目(Flask + DB 容器化)
数据库·docker·flask
xcLeigh1 小时前
Python 项目实战:用 Flask 实现 MySQL 数据库增删改查 API
数据库·python·mysql·flask·教程·python3
威迪斯特1 小时前
Flask:轻量级Web框架的技术本质与工程实践
前端·数据库·后端·python·flask·开发框架·核心架构
xu_yule1 小时前
Redis存储(15)Redis的应用_分布式锁_Lua脚本/Redlock算法
数据库·redis·分布式
一灰灰blog1 小时前
Spring AI中的多轮对话艺术:让大模型主动提问获取明确需求
数据库·人工智能·spring