定义
$replaceWithReplaces the input document with the specified document. The operation replaces all existing fields in the input document, including the
_idfield. With$replaceWith, you can promote an embedded document to the top-level. You can also specify a new document as the replacement.The
$replaceWithstage performs the same action as the$replaceRootstage, but the stages have different forms.The
$replaceWithstage has the following form:{ $replaceWith: <replacementDocument> }
行为
If the <replacementDocument> is not a document, $replaceWith errors and fails.
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:
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:
db.collection.aggregate([ { $replaceWith: "$name" } ])
为避免错误,可以使用 $mergeObjects 将 name 文档与某份默认文档合并;例如:
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:
db.collection.aggregate([ { $match: { name : { $exists: true, $not: { $type: "array" }, $type: "object" } } }, { $replaceWith: "$name" } ])
也可以使用 $ifNull 表达式指定其他文档作为根文档;例如:
db.collection.aggregate([ { $replaceWith: { $ifNull: [ "$name", { _id: "$_id", missingName: true} ] } } ])
示例
$replaceWith 嵌入式文档字段
使用以下文档创建名为 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. The $mergeObjects expression merges the specified default document with the pets document.
db.people.aggregate( [ { $replaceWith: { $mergeObjects: [ { dogs: 0, cats: 0, birds: 0, fish: 0 }, "$pets" ] } } ] )
操作返回以下结果:
[ { 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 嵌套在数组中的文档
一个名为 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 } ] } ])
以下操作将 grade 字段大于或等于 90 的嵌入式文档提升到顶层:
db.students.aggregate( [ { $unwind: "$grades" }, { $match: { "grades.grade" : { $gte: 90 } } }, { $replaceWith: "$grades" } ] )
操作返回以下结果:
[ { 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 新创建的文档
示例 1
使用以下文档填充的示例集合 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. The $replaceWith calculates the total amount as well as uses the variable NOW to get the current time.
db.sales.aggregate([ { $match: { status: "C" } }, { $replaceWith: { _id: "$_id", item: "$item", amount: { $multiply: [ "$price", "$quantity"]}, status: "Complete", asofDate: "$$NOW" } } ])
该操作将返回以下文档:
[ { _id: 1, item: 'butter', amount: 20, status: 'Complete', asofDate: '...' }, { _id: 3, item: 'jam', amount: 50, status: 'Complete', asofDate: '...' }, { _id: 4, item: 'muffins', amount: 50, status: 'Complete', asofDate: '...' } ]
示例 2
示例集合 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 } ] )
假设出于报告目的,您希望按季度查看报告的销售数据;例如
{ "_id" : "2019Q1", "A" : 400, "B" : 550, "C" : 1000 }
要查看按季度分组的数据,可以使用以下聚合管道:
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" } } ] )
- 第一个阶段:
$addFields阶段添加了一个新的obj文档字段,该字段将键k定义为区域值,将值v定义为该区域的数量。例如:{ "_id" : 1, "quarter" : "2019Q1", "region" : "A", "qty" : 400, "obj" : { "k" : "A", "v" : 400 } } - 第二阶段:
$group阶段按季度分组,并使用$push将obj字段累加到新的items数组字段中。例如:{ "_id" : "2019Q1", "items" : [ { "k" : "A", "v" : 400 }, { "k" : "B", "v" : 550 }, { "k" : "C", "v" : 1000 } ] } - 第三阶段:
$project阶段使用$concatArrays创建新数组items2,其中包含_id信息和items数组的元素:{ "_id" : "2019Q1", "items2" : [ { "k" : "_id", "v" : "2019Q1" }, { "k" : "A", "v" : 400 }, { "k" : "B", "v" : 550 }, { "k" : "C", "v" : 1000 } ] } - 第四阶段:
The
$replaceWithuses the$arrayToObjectto convert theitems2into a document, using the specified keykand valuevpairs and outputs that document to the next stage. For example:{ "_id" : "2019Q1", "A" : 400, "B" : 550, "C" : 1000 }
该聚合返回以下文档:
[ { _id: '2019Q1', A: 400, B: 550, C: 1000 }, { _id: '2019Q2', A: 660, B: 500, C: 1200 } ]
$replaceWith 一份从 $$ROOT 创建的新文档和一份默认文档
使用以下文档创建名为 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:
db.contacts.aggregate( [ { $replaceWith: { $mergeObjects: [ { _id: "", name: "", email: "", cell: "", home: "" }, "$$ROOT" ] } } ] )
该聚合返回以下文档:
[ { _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: '987-654-3210', home: '' } ]
本页上的C#示例使用Atlas示例数据集中的 sample_mflix数据库。要学习;了解如何创建免费的MongoDB Atlas 群集并加载示例数据集,请参阅MongoDB .NET/ C#驱动程序文档中的入门。
以下 Movie 类对 sample_mflix.movies 集合中的文档进行建模:
[] public class Movie { [] public ObjectId Id { get; set; } [] public string Title { get; set; } = null!; [] public int? Year { get; set; } [] public int? Runtime { get; set; } [] public string? Rated { get; set; } [] public int Metacritic { get; set; } [] public string? Plot { get; set; } [] public string? Type { get; set; } [] public string[]? Cast { get; set; } [] public string[]? Directors { get; set; } [] public string[]? Writers { get; set; } [] public ImdbData? Imdb { get; set; } }
以下 ImdbData 类对每个 Movie 的 imdb字段中的嵌入式文档进行建模:
[] public class ImdbData { [] public int? ImdbId { get; set; } [] public double? Rating { get; set; } [] public int? Votes { get; set; } }
要使用MongoDB .NET/C# 驱动程序将 $replaceWith 阶段添加到聚合管道,请对 PipelineDefinition 对象调用 UnionWith() 方法。
以下示例创建一个管道阶段,该阶段按标题字母顺序对 Movie 文档进行排序,将结果限制为五个文档,并将每个 Movie 文档替换为存储在其 Imdb 属性中的 ImdbData 文档:
var pipeline = new EmptyPipelineDefinition<Movie>() .Sort(Builders<Movie>.Sort.Ascending(m => m.Title)) .Limit(5) .ReplaceWith(m => m.Imdb);
本页上的 Node.js 示例使用 Atlas 示例数据集中的 sample_mflix数据库。要学习如何创建免费的MongoDB Atlas 集群并加载示例数据集,请参阅MongoDB Node.js驱动程序文档中的入门。
要使用MongoDB Node.js驱动程序将 $replaceWith 阶段添加到聚合管道,请在管道对象中使用 $replaceWith操作符。
以下示例创建了一个管道阶段,该阶段将每个输入的 movie文档替换为其 imdb字段中存储的文档。然后,该示例运行聚合管道:
const pipeline = [{ $replaceWith: '$imdb' }]; const cursor = collection.aggregate(pipeline); return cursor;
了解详情
要学习;了解有关相关管道阶段的更多信息,请参阅 $replaceRoot 指南。