定义
$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" } }
])
为了避免这个错误,可以使用$mergeObjects
把name
文档合并到缺省文档,例如:
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_name
和last_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'
}