$replaceWith (aggregation)

On this page本页内容

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 is an alias for $replaceRoot.$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. 如果<replacementDocument>解析为缺少的文档(即该文档不存在),$replaceWith将出现错误并失败。For example, create a collection with the following documents:例如,创建包含以下文档的集合:

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; for example:为了避免错误,可以使用$mergeObjectsname文档与一些默认文档合并;例如:

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:或者,在将文档传递到$replaceWith阶段之前,可以通过包含$match阶段来检查文档字段是否存在,从而跳过缺少name字段的文档:

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; for example:或者,您可以使用$ifNull表达式指定其他文档作为根;例如:

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例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字段累积到新的项目数组字段中。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.insert([
   { "_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" }