Database Manual / Reference / Query Language / Aggregation Stages

$replaceWith (aggregation stage)(聚合阶段)

Definition定义

$replaceWith

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. You can also specify a new document as the replacement.使用$replaceWith,您可以将嵌入式文档提升到顶级。您还可以指定一个新文档作为替换。

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

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

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

Examples示例

MongoDB Shell

$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示例2

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

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. For example:$addFields阶段添加了一个新的obj文档字段,该字段将键k定义为区域值,将值v定义为该区域的数量。例如:

{ "_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. For example:$group阶段按季度分组,并使用$pushobj字段累积到新的items数组字段中。例如:

{ "_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. For example:$replaceWith使用$arrayToObjectitems2转换为文档,使用指定的键k和值v对,并将该文档输出到下一阶段。例如:

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

The C# examples on this page use the sample_mflix database from the Atlas sample datasets. 本页上的C#示例使用Atlas示例数据集中的sample_mflix数据库。To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see Get Started in the MongoDB .NET/C# Driver documentation.要了解如何创建免费的MongoDB Atlas集群并加载示例数据集,请参阅MongoDB NET/C#驱动程序文档中的入门

The following Movie class models the documents in the sample_mflix.movies collection:以下Movie类对sample_mflix.movies集合中的文档进行建模:

public class Movie
{
public ObjectId Id { get; set; }

public int Runtime { get; set; }

public string Title { get; set; }

public string Rated { get; set; }

public List<string> Genres { get; set; }

public string Plot { get; set; }

public ImdbData Imdb { get; set; }

public int Year { get; set; }

public int Index { get; set; }

public string[] Comments { get; set; }

[BsonElement("lastupdated")]
public DateTime LastUpdated { get; set; }
}

Note

ConventionPack for Pascal CasePascal大小写的约定包

The C# classes on this page use Pascal case for their property names, but the field names in the MongoDB collection use camel case. To account for this difference, you can use the following code to register a ConventionPack when your application starts:此页面上的C#类使用Pascal大小写作为其属性名,但MongoDB集合中的字段名使用驼峰大小写。为了解释这种差异,您可以在应用程序启动时使用以下代码注册ConventionPack

var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

The following class models ImdbData documents:以下类对ImdbData文档进行建模:

public class ImdbData
{
public string Id { get; set; }

public int Votes { get; set; }

public float Rating { get; set; }
}

To use the MongoDB .NET/C# driver to add a $replaceWith stage to an aggregation pipeline, call the ReplaceWith() method on a PipelineDefinition object.要使用MongoDB NET/C#驱动程序向聚合管道添加$replaceWith阶段,请在PipelineDefinition对象上调用ReplaceWith()方法。

The following example creates a pipeline stage that replaces each input Movie document with the ImdbData document stored in its Imdb property:以下示例创建了一个管道阶段,该阶段将每个输入的Movie文档替换为存储在其Imdb属性中的ImdbData文档:

var pipeline = new EmptyPipelineDefinition<Movie>()
.ReplaceWith(m => m.ImdbData);
Node.js

The Node.js examples on this page use the sample_mflix database from the Atlas sample datasets. 本页上的Node.js示例使用Atlas示例数据集中的sample_mflix数据库。To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see Get Started in the MongoDB Node.js driver documentation.要了解如何创建免费的MongoDB Atlas集群并加载示例数据集,请参阅MongoDB Node.js驱动程序文档中的入门

To use the MongoDB Node.js driver to add a $replaceWith stage to an aggregation pipeline, use the $replaceWith operator in a pipeline object.要使用MongoDB Node.js驱动程序将$replaceWith阶段添加到聚合管道中,请在管道对象中使用$replaceWith运算符。

The following example creates a pipeline stage that replaces each input movie document with the document stored in its imdb field. The example then runs the aggregation pipeline:以下示例创建了一个管道阶段,将每个输入的movie文档替换为存储在其imdb字段中的文档。然后,该示例运行聚合管道:

const pipeline = [{ $replaceWith: { replacement: "$imdb" } }];

const cursor = collection.aggregate(pipeline);
return cursor;

Learn More了解更多

To learn more about related pipeline stages, see the $replaceRoot guide.要了解有关相关管道阶段的更多信息,请参阅$replaceRoot指南。