Docs HomeMongoDB Manual

$replaceWith (aggregation)

Definition定义

$replaceWith

New in version 4.2. 4.2版新增。

Replaces the input document with the specified document. 用指定的文档替换输入文档。The operation replaces all existing fields in the input document, including the _id field. 该操作将替换输入文档中的所有现有字段,包括_id字段。With $replaceWith, you can promote an embedded document to the top-level. 使用$replaceWith,您可以将嵌入文档提升到顶级文档。You can also specify a new document as the replacement.您也可以指定一个新文档作为替换文档。

The $replaceWith stage performs the same action as the $replaceRoot stage, but the stages have different forms.$replaceWith阶段执行与$replaceRoot阶段相同的操作,但阶段具有不同的形式。

The $replaceWith stage has the following form:$replaceWith阶段具有以下形式:

{ $replaceWith: <replacementDocument> }

The replacement document can be any valid expression that resolves to a document. For more information on expressions, see Expressions.替换文档可以是解析为文档的任何有效表达式。有关表达式的详细信息,请参阅表达式

Behavior行为

If the <replacementDocument> is not a document, $replaceWith errors and fails.如果<replacementDocument>不是文档,$replaceWith将出错并失败。

If the <replacementDocument> resolves to a missing document (i.e. the document does not exist), $replaceWith errors and fails. For example, create a collection with the following documents:如果<replacementDocument>解析为缺少文档(即文档不存在),$replaceWith将出错并失败。例如,使用以下文档创建集合:

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" },
])

Then the following $replaceWith operation fails because one of the document does not have the name field:然后,以下$replaceWith操作失败,因为其中一个文档没有name字段:

db.collection.aggregate([
{ $replaceWith: "$name" }
])

To avoid the error, you can use $mergeObjects to merge the name document with some default document; 为了避免错误,您可以使用$mergeObjectsname文档与某些默认文档合并;for example:例如:

db.collection.aggregate([
{ $replaceWith: { $mergeObjects: [ { _id: "$_id", first: "", last: "" }, "$name" ] } }
])

Alternatively, you can skip the documents that are missing the name field by including a $match stage to check for existence of the document field before passing documents to the $replaceWith stage:或者,您可以跳过缺少name字段的文档,方法是在将文档传递到$replaceWith阶段之前,包括$match阶段以检查文档字段的存在:

db.collection.aggregate([
{ $match: { name : { $exists: true, $not: { $type: "array" }, $type: "object" } } },
{ $replaceWith: "$name" }
])

Or, you can use $ifNull expression to specify some other document to be root; 或者,您可以使用$ifNull表达式指定其他文档为根文档;for example:例如:

db.collection.aggregate([
{ $replaceWith: { $ifNull: [ "$name", { _id: "$_id", missingName: true} ] } }
])

Examples实例

$replaceWith an Embedded Document Field嵌入式文档字段

Create a collection named people with the following documents:使用以下文档创建一个名为people的集合:

db.people.insertMany([
{ "_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 }
])

The following operation uses the $replaceWith stage to replace each input document with the result of a $mergeObjects operation. 以下操作使用$replaceWith阶段将每个输入文档替换为$mergeObjects操作的结果。The $mergeObjects expression merges the specified default document with the pets document.$mergeObjects表达式将指定的默认文档与pets文档合并。

db.people.aggregate( [
{ $replaceWith: { $mergeObjects: [ { dogs: 0, cats: 0, birds: 0, fish: 0 }, "$pets" ] } }
] )

The operation returns the following results:该操作返回以下结果:

{ "dogs" : 2, "cats" : 1, "birds" : 0, "fish" : 0 }
{ "dogs" : 0, "cats" : 1, "birds" : 0, "fish" : 3 }
{ "dogs" : 0, "cats" : 0, "birds" : 0, "fish" : 0 }

$replaceWith a Document Nested in an Array嵌套在数组中的文档

A collection named students contains the following documents:一个名为students的集合包含以下文档:

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 }
]
}
])

The following operation promotes the embedded document(s) with the grade field greater than or equal to 90 to the top level:以下操作将grade字段大于或等于90的嵌入文档提升到最高级别:

db.students.aggregate( [
{ $unwind: "$grades" },
{ $match: { "grades.grade" : { $gte: 90 } } },
{ $replaceWith: "$grades" }
] )

The operation returns the following results:该操作返回以下结果:

{ "test" : 3, "grade" : 95, "mean" : 85, "std" : 6 }
{ "test" : 1, "grade" : 90, "mean" : 75, "std" : 6 }
{ "test" : 3, "grade" : 91, "mean" : 85, "std" : 4 }

$replaceWith a Newly Created Document新创建的文档

Example 1

An example collection sales is populated with the following documents:示例集合sales由以下文档填充:

db.sales.insertMany([
{ "_id" : 1, "item" : "butter", "price" : 10, "quantity": 2, date: ISODate("2019-03-01T08:00:00Z"), status: "C" },
{ "_id" : 2, "item" : "cream", "price" : 20, "quantity": 1, date: ISODate("2019-03-01T09:00:00Z"), status: "A" },
{ "_id" : 3, "item" : "jam", "price" : 5, "quantity": 10, date: ISODate("2019-03-15T09:00:00Z"), status: "C" },
{ "_id" : 4, "item" : "muffins", "price" : 5, "quantity": 10, date: ISODate("2019-03-15T09:00:00Z"), status: "C" }
])

Assume that for reporting purposes, you want to calculate for each completed sale, the total amount as of the current report run time. 假设出于报告目的,您希望为每个已完成的销售计算截至当前报告运行时的总金额。The following operation finds all the sales with status C and creates new documents using the $replaceWith stage. 以下操作查找状态为C的所有销售,并使用$replaceWith阶段创建新文档。The $replaceWith calculates the total amount as well as uses the variable NOW to get the current time.$replaceWith计算总额,并使用变量NOW获取当前时间。

db.sales.aggregate([
{ $match: { status: "C" } },
{ $replaceWith: { _id: "$_id", item: "$item", amount: { $multiply: [ "$price", "$quantity"]}, status: "Complete", asofDate: "$$NOW" } }
])

The operation returns the following documents:该操作返回以下文档:

{ "_id" : 1, "item" : "butter", "amount" : 20, "status" : "Complete", "asofDate" : ISODate("2019-06-03T22:47:54.812Z") }
{ "_id" : 3, "item" : "jam", "amount" : 50, "status" : "Complete", "asofDate" : ISODate("2019-06-03T22:47:54.812Z") }
{ "_id" : 4, "item" : "muffins", "amount" : 50, "status" : "Complete", "asofDate" : ISODate("2019-06-03T22:47:54.812Z") }

Example 2

An example collection reportedsales is populated with the reported sales information by quarter and regions:示例集合reportedsales按季度和地区填充报告的销售信息:

db.reportedsales.insertMany( [
{ _id: 1, quarter: "2019Q1", region: "A", qty: 400 },
{ _id: 2, quarter: "2019Q1", region: "B", qty: 550 },
{ _id: 3, quarter: "2019Q1", region: "C", qty: 1000 },
{ _id: 4, quarter: "2019Q2", region: "A", qty: 660 },
{ _id: 5, quarter: "2019Q2", region: "B", qty: 500 },
{ _id: 6, quarter: "2019Q2", region: "C", qty: 1200 }
] )

Assume that for reporting purposes, you want to view the reported sales data by quarter; e.g.假设出于报告目的,您希望按季度查看报告的销售数据;例如。

{ "_id" : "2019Q1", "A" : 400, "B" : 550, "C" : 1000 }

To view the data grouped by quarter, you can use the following aggregation pipeline:要查看按季度分组的数据,可以使用以下聚合管道:

db.reportedsales.aggregate( [
{ $addFields: { obj: { k: "$region", v: "$qty" } } },
{ $group: { _id: "$quarter", items: { $push: "$obj" } } },
{ $project: { items2: { $concatArrays: [ [ { "k": "_id", "v": "$_id" } ], "$items" ] } } },
{ $replaceWith: { $arrayToObject: "$items2" } }
] )
First stage:第一阶段:

The $addFields stage adds a new obj document field that defines the key k as the region value and the value v as the quantity for that region. $addFields阶段添加一个新的obj文档字段,该字段将键k定义为区域值,将值v定义为该区域的数量。For example:例如:

{ "_id" : 1, "quarter" : "2019Q1", "region" : "A", "qty" : 400, "obj" : { "k" : "A", "v" : 400 } }
Second stage:第二阶段:

The $group stage groups by the quarter and uses $push to accumulate the obj fields into a new items array field. $group阶段按季度分组,并使用$pushobj字段累积到一个新的items数组字段中。For example:例如:

{ "_id" : "2019Q1", "items" : [ { "k" : "A", "v" : 400 }, { "k" : "B", "v" : 550 }, { "k" : "C", "v" : 1000 } ] }
Third stage:第三阶段:

The $project stage uses $concatArrays to create a new array items2 that includes the _id info and the elements from the items array:$project阶段使用$concatArrays创建一个新的数组items2,其中包括_id信息和items数组中的元素:

{ "_id" : "2019Q1", "items2" : [ { "k" : "_id", "v" : "2019Q1" }, { "k" : "A", "v" : 400 }, { "k" : "B", "v" : 550 }, { "k" : "C", "v" : 1000 } ] }
Fourth stage:第四阶段:

The $replaceWith uses the $arrayToObject to convert the items2 into a document, using the specified key k and value v pairs and outputs that document to the next stage. $replaceWith使用$arrayToObjectitems2转换为文档,使用指定的键k值v对,并将该文档输出到下一阶段。For example:例如:

{ "_id" : "2019Q1", "A" : 400, "B" : 550, "C" : 1000 }

The aggregation returns the following document:聚合返回以下文档:

{ "_id" : "2019Q1", "A" : 400, "B" : 550, "C" : 1000 }
{ "_id" : "2019Q2", "A" : 660, "B" : 500, "C" : 1200 }

$replaceWith a New Document Created from $$ROOT and a Default Document$replaceWith$$ROOT创建的新文档和默认文档

Create a collection named contacts with the following documents:使用以下文档创建名为contacts的集合:

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", cell: "987-654-3210", email: "beo@example.net" }
] )

The following operation uses $replaceWith with $mergeObjects to output current documents with default values for missing fields:以下操作使用$replaceWith$mergeObjects来输出缺少字段的具有默认值的当前文档:

db.contacts.aggregate( [
{ $replaceWith:
{ $mergeObjects:
[
{ _id: "", name: "", email: "", cell: "", home: "" },
"$$ROOT"
]
}
}
] )

The aggregation returns the following documents:聚合返回以下文档:

{
_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'
}