Definition
$replaceRoot
Replaces the input document with the specified document. The operation replaces all existing fields in the input document, including the
_id
field. You can promote an existing embedded document to the top level, or create a new document for promotion (see example).Note
You can also use the
$replaceWith
stage. The$replaceWith
stage peforms the same action as the$replaceRoot
stage, but the stages have different forms.The
$replaceRoot
stage has the following form:{ $replaceRoot: { newRoot: <replacementDocument> } } The replacement document can be any valid expression that resolves to a document. The stage errors and fails if
<replacementDocument>
is not a document. For more information on expressions, see Expression Operators.
Behavior
If the <replacementDocument>
is not a document,
$replaceRoot
errors and fails.
If the <replacementDocument>
resolves to a missing document (i.e.
the document does not exist), $replaceRoot
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 $replaceRoot
operation fails because one
of the documents does not have the name
field:
db.collection.aggregate([ { $replaceRoot: { newRoot: "$name" } } ])
To avoid the error, you can use $mergeObjects
to merge
the name
document into some default document; for example:
db.collection.aggregate([ { $replaceRoot: { newRoot: { $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 $replaceRoot
stage:
db.collection.aggregate([ { $match: { name : { $exists: true, $not: { $type: "array" }, $type: "object" } } }, { $replaceRoot: { newRoot: "$name" } } ])
Or, you can use $ifNull
expression to specify some other
document to be root; for example:
db.collection.aggregate([ { $replaceRoot: { newRoot: { $ifNull: [ "$name", { _id: "$_id", missingName: true} ] } } } ])
Examples
$replaceRoot
with an Embedded Document Field
A collection named people
contains the following documents:
{ "_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 $replaceRoot
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( [ { $replaceRoot: { newRoot: { $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 }
$replaceRoot
with a Document Nested in an Array
A collection named students
contains the following documents:
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:
db.students.aggregate( [ { $unwind: "$grades" }, { $match: { "grades.grade" : { $gte: 90 } } }, { $replaceRoot: { newRoot: "$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 }
$replaceRoot
with a newly created document
You can also create new documents as part of the
$replaceRoot
stage and use them to replace all the other fields.
A collection named contacts
contains the following documents:
{ "_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" }
The following operation creates a new document out of the
first_name
and last_name
fields.
db.contacts.aggregate( [ { $replaceRoot: { newRoot: { full_name: { $concat : [ "$first_name", " ", "$last_name" ] } } } } ] )
The operation returns the following results:
{ "full_name" : "Gary Sheffield" } { "full_name" : "Nancy Walker" } { "full_name" : "Peter Sumner" }
$replaceRoot
with a New Document Created from $$ROOT
and a Default Document
Create a collection named contacts
with the following documents:
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" } ] )
The following operation uses $replaceRoot
with
$mergeObjects
to output current documents with default
values for missing fields:
db.contacts.aggregate( [ { $replaceRoot: { newRoot: { $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' }
The C# examples on this page use the sample_mflix
database
from the Atlas sample datasets. 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.
The following Movie
class models the documents in the sample_mflix.movies
collection:
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; } [ ] public DateTime LastUpdated { get; set; } }
Note
ConventionPack for Pascal Case
The properties in the preceding class are named in Pascal case, 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:
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
The following class models ImdbData
documents:
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 $replaceRoot
stage to an aggregation
pipeline, call the ReplaceRoot() method on a PipelineDefinition
object.
The following example creates a pipeline stage that replaces each input Movie
document with the ImdbData
document stored
in its Imdb
property:
var pipeline = new EmptyPipelineDefinition<Movie>() .ReplaceRoot(m => m.ImdbData);