Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs Menu
Docs Home
/ /

$merge (aggregation stage)

Note

This page describes the $merge stage, which outputs the aggregation pipeline results to a collection. For the $mergeObjects operator, which merges documents into a single document, see $mergeObjects.

$merge

Writes the results of the aggregation pipeline to a specified collection. The $merge operator must be the last stage in the pipeline.

The $merge stage:

  • Can output to a collection in the same or different database.

  • Can output to the same collection that is being aggregated. For more information, see Output to the Same Collection that is Being Aggregated.

  • Consider the following points when using $merge or $out stages in an aggregation pipeline:

    • Starting in MongoDB 5.0, pipelines with a $merge stage can run on replica set secondary nodes if all the nodes in the cluster have the featureCompatibilityVersion set to 5.0 or higher and the read preference allows secondary reads.

      • $merge and $out stages run on secondary nodes, but write operations are sent to the primary node.

      • Not all driver versions support $merge operations sent to the secondary nodes. For details, see the driver documentation.

    • In earlier MongoDB versions, pipelines with $out or $merge stages always run on the primary node and read preference isn't considered.

  • Creates a new collection if the output collection does not already exist.

  • Can incorporate results (insert new documents, merge documents, replace documents, keep existing documents, fail the operation, process documents with a custom update pipeline) into an existing collection.

  • Can output to a sharded collection. Input collection can also be sharded.

For a comparison with the $out stage which also outputs the aggregation results to a collection, see $merge and $out Comparison.

Note

On-Demand Materialized Views

$merge can incorporate the pipeline results into an existing output collection rather than perform a full replacement of the collection. This functionality allows users to create on-demand materialized views, where the content of the output collection is incrementally updated when the pipeline is run.

For more information on this use case, see On-Demand Materialized Views as well as the examples on this page.

Materialized views are separate from read-only views. For information on creating read-only views, see read-only views.

You can use $merge for deployments hosted in the following environments:

  • MongoDB Atlas: The fully managed service for MongoDB deployments in the cloud

$merge has the following syntax:

{ $merge: {
into: <collection> -or- { db: <db>, coll: <collection> },
on: <identifier field> -or- [ <identifier field1>, ...], // Optional
let: <variables>, // Optional
whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional
whenNotMatched: <insert|discard|fail> // Optional
} }

For example:

{ $merge: { into: "myOutput", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }

If using all default options for $merge, including writing to a collection in the same database, you can use the simplified form:

{ $merge: <collection> } // Output collection is in the same database

The $merge stage takes a document with the following fields:

Field
Description

The output collection. Specify either:

  • The collection name as a string to output to a collection in the same database where the aggregation is run. For example:

    into: "myOutput"

  • The database and collection name in a document to output to a collection in the specified database. For example:

    into: { db:"myDB", coll:"myOutput" }

If the output collection does not exist, $merge creates the collection:

  • For a replica set or a standalone, if the output database does not exist, $merge also creates the database.

  • For a sharded cluster, the specified output database must already exist.

The output collection can be a sharded collection.

Optional. Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. Specify either:

  • A single field name as a string. For example:

    on: "_id"

  • A combination of fields in an array. For example:

    on: [ "date", "customerId" ]
    The order of the fields in the array does not matter, and you cannot specify the same field multiple times.

For the specified field or fields:

  • The aggregation results documents must contain the field(s) specified in the on, unless the on field is the _id field. If the _id field is missing from a results document, MongoDB adds it automatically.

  • For deployments running MongoDB 8.0 and earlier, specified field or fields for on cannot be missing or contain a null value. Starting in MongoDB 8.1, if the supporting index is not sparse, the specified field or fields for on can be missing or contain a null value.

  • The specified field or fields cannot contain an array value.

$merge requires a unique index with keys that correspond to the on identifier fields. Although the order of the index key specification does not matter, the unique index must only contain the on fields as its keys.

  • The index must also have the same collation as the aggregation's collation.

  • The unique index can be a sparse index.

  • The unique index cannot be a partial index.

  • For output collections that already exist, the corresponding index must already exist.

The default value for on depends on the output collection:

  • If the output collection does not exist, the on identifier must be and defaults to the _id field. The corresponding unique _id index is automatically created.

    • To use a different on identifier field(s) for a collection that does not exist, you can create the collection first by creating a unique index on the desired field(s). See the section on non-existent output collection for an example.

    • Starting in MongoDB 8.3, the server checks to ensure that the automatically created _id index matches the query's collation. If the collations do not match, the _id index cannot provide uniqueness for the query, and the query will not run.

  • If the existing output collection is unsharded, the on identifier defaults to the _id field.

  • If the existing output collection is a sharded collection, the on identifier defaults to all the shard key fields and the _id field. If specifying a different on identifier, the on must contain all the shard key fields.

Optional. The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s).

You can specify either:

  • One of the pre-defined action strings:

    Action
    Description

    Replace the existing document in the output collection with the matching results document.

    When performing a replace, the replacement document cannot result in a modification of the _id value or, if the output collection is sharded, the shard key value. Otherwise, the operation generates an error.

    To avoid this error, if the on field does not include the _id field, remove the _id field in the aggregation results to avoid the error, such as with a preceding $unset stage, and so on.

    Keep the existing document in the output collection.

    "merge" (Default)

    Merge the matching documents (similar to the $mergeObjects operator).

    • If the results document contains fields not in the existing document, add these new fields to the existing document.

    • If the results document contains fields in the existing document, replace the existing field values with those from the results document.

    For example, if the output collection has the document:

    { _id: 1, a: 1, b: 1 }

    And the aggregation results has the document:

    { _id: 1, b: 5, z: 1 }

    Then, the merged document is:

    { _id: 1, a: 1, b: 5, z: 1 }

    When performing a merge, the merged document cannot result in a modification of the _id value or, if the output collection is sharded, the shard key value. Otherwise, the operation generates an error.

    To avoid this error, if the on field does not include the _id field, remove the _id field in the aggregation results to avoid the error, such as with a preceding $unset stage, and so on.

    Stop and fail the aggregation operation. Any changes to the output collection from previous documents are not reverted.

  • An aggregation pipeline to update the document in the collection.

    [ <stage1>, <stage2> ... ]

    The pipeline can only consist of the following stages:

    The pipeline cannot modify the on field's value. For example, if you are matching on the field month, the pipeline cannot modify the month field.

    The whenMatched pipeline can directly access the fields of the existing documents in the output collection using $<field>.

    To access the fields from the aggregation results documents, use either:

    • The built-in $$new variable to access the field. Specifically, $$new.<field>. The $$new variable is only available if the let specification is omitted.

    • The user-defined variables in the let field.

      Specify the double dollar sign ($$) prefix together with the variable name in the form $$<variable_name>. For example, $$year. If the variable is set to a document, you can also include a document field in the form $$<variable_name>.<field>. For example, $$year.month.

      For more examples, see Use Variables to Customize the Merge.

Optional. Specifies variables for use in the whenMatched pipeline.

Specify a document with the variable names and value expressions:

{ <variable_name_1>: <expression_1>,
...,
<variable_name_n>: <expression_n> }

If unspecified, defaults to { new: "$$ROOT" } (see ROOT). The whenMatched pipeline can access the $$new variable.

To access the variables in the whenMatched pipeline:

Specify the double dollar sign ($$) prefix together with the variable name in the form $$<variable_name>. For example, $$year. If the variable is set to a document, you can also include a document field in the form $$<variable_name>.<field>. For example, $$year.month.

For examples, see Use Variables to Customize the Merge.

Optional. The behavior of $merge if a result document does not match an existing document in the out collection.

You can specify one of the pre-defined action strings:

Action
Description

"insert" (Default)

Insert the document into the output collection.

Discard the document. Specifically, $merge does not insert the document into the output collection.

Stop and fail the aggregation operation. Any changes already written to the output collection are not reverted.

If the _id field is not present in a document from the aggregation pipeline results, the $merge stage generates it automatically.

For example, in the following aggregation pipeline, $project excludes the _id field from the documents passed into $merge. When $merge writes these documents to the "newCollection", $merge generates a new _id field and value.

db.movies.aggregate( [
{ $project: { _id: 0 } },
{ $merge : { into : "newCollection" } }
] )

The $merge operation creates a new collection if the specified output collection does not exist.

  • The output collection is created when $merge writes the first document into the collection and is immediately visible.

  • If the aggregation fails, any writes completed by the $merge before the error will not be rolled back.

Note

For a replica set or a standalone, if the output database does not exist, $merge also creates the database.

For a sharded cluster, the specified output database must already exist.

If the output collection does not exist, $merge requires the on identifier to be the _id field. To use a different on field value for a collection that does not exist, you can create the collection first by creating a unique index on the desired field(s) first. For example, if the output collection newDailyCommentCount does not exist and you want to specify the commentDate field as the on identifier:

db.newDailyCommentCount.createIndex(
{ commentDate: 1 }, { unique: true } )
db.comments.aggregate( [
{ $match: { date: { $gte: new Date("2002-01-01"),
$lt: new Date("2002-02-01") } } },
{ $group: { _id: { $dateToString: { format: "%Y-%m-%d",
date: "$date" } }, count: { $sum: 1 } } },
{ $project: { _id: 0, commentDate: { $toDate: "$_id" },
count: 1 } },
{ $merge : { into : "newDailyCommentCount",
on: "commentDate" } }
] )

The $merge stage can output to a sharded collection. When the output collection is sharded, $merge uses the _id field and all the shard key fields as the default on identifier. If you override the default, the on identifier must include all the shard key fields:

{ $merge: {
into: "<shardedColl>" or { db:"<sharding enabled db>", coll: "<shardedColl>" },
on: [ "<shardkeyfield1>", "<shardkeyfield2>",... ], // Shard key fields and any additional fields
let: <variables>, // Optional
whenMatched: <replace|keepExisting|merge|fail|pipeline>, // Optional
whenNotMatched: <insert|discard|fail> // Optional
} }

For example, use the sh.shardCollection() method to create a new sharded collection moviesByYearAndRating with the rated field as the shard key.

sh.shardCollection(
"sample_mflix.moviesByYearAndRating", // Namespace of the collection to shard
{ rated: 1 }, // Shard key
);

The moviesByYearAndRating collection will contain documents with movie statistics by year (year field) and content rating (shard key); specifically, the on identifier is ["year", "rated"] (the ordering of the fields does not matter). Because $merge requires a unique index with keys that correspond to the on identifier fields, create the unique index (the ordering of the fields do not matter): [1]

db.moviesByYearAndRating.createIndex(
{ rated: 1, year: 1 }, { unique: true } )

With the sharded collection moviesByYearAndRating and the unique index created, you can use $merge to output the aggregation results to this collection, matching on [ "year", "rated" ] as in this example:

db.movies.aggregate( [
{ $match: { rated: { $ne: null }, year: { $ne: null } } },
{ $group: {
_id: { year: "$year", rated: "$rated" },
movieCount: { $sum: 1 } } },
{ $project: { _id: 0, year: "$_id.year", rated: "$_id.rated",
movieCount: 1 } },
{ $merge: { into: "moviesByYearAndRating",
"on": [ "year", "rated" ], whenMatched: "replace",
whenNotMatched: "insert" } }
] )
[1] The sh.shardCollection() method can also create a unique index on the shard key when passed the { unique: true } option if: the shard key is range-based, the collection is empty, and a unique index on the shard key doesn't already exist.In the previous example, because the on identifier is the shard key and another field, a separate operation to create the corresponding index is required.

$merge can replace an existing document in the output collection if the aggregation results contain a document or documents that match based on the on specification. As such, $merge can replace all documents in the existing collection if the aggregation results include matching documents for all existing documents in the collection and you specify "replace" for whenMatched.

However, to replace an existing collection regardless of the aggregation results, use $out instead.

The $merge errors if the $merge results in a change to an existing document's _id value.

Tip

To avoid this error, if the on field does not include the _id field, remove the _id field in the aggregation results to avoid the error, such as with a preceding $unset stage, and so on.

Additionally, for a sharded collection, $merge also generates an error if it results in a change to the shard key value of an existing document.

Any writes completed by the $merge before the error will not be rolled back.

If the unique index used by $merge for on field(s) is dropped mid-aggregation, there is no guarantee that the aggregation will be killed. If the aggregation continues, there is no guarantee that documents do not have duplicate on field values.

If the $merge attempts to write a document that violates any unique index on the output collection, the operation generates an error. For example:

  • Insert a non-matching document that violates a unique index other than the index on the on field(s).

  • Fail if there is a matching document in the collection. Specifically, the operation attempts to insert the matching document which violates the unique index on the on field(s).

  • Replace an existing document with a new document that violates a unique index other than the index on the on field(s).

  • Merge the matching documents that results in a document that violates a unique index other than the index on the on field(s).

If your collection uses schema validation and has validationAction set to error, inserting an invalid document or updating a document with invalid values with $merge throws a MongoServerError and the document is not written to the target collection. If there are multiple invalid documents, only the first invalid document encountered throws an error. All valid documents are written to the target collection, and all invalid documents fail to write.

If all of the following are true for a $merge stage, $merge inserts the document directly into the output collection:

  • The value of whenMatched is an aggregation pipeline,

  • The value of whenNotMatched is insert, and

  • There is no match for a document in the output collection,

$merge inserts the document directly into the output collection.

With the introduction of $merge, MongoDB provides two stages, $merge and $out, for writing the results of the aggregation pipeline to a collection:

$merge
  • Can output to a collection in the same or different database.

  • Can output to a collection in the same or different database.

  • Creates a new collection if the output collection does not already exist.

  • Creates a new collection if the output collection does not already exist.

  • Replaces the output collection completely if it already exists.

  • Can output to a sharded collection. Input collection can also be sharded.

  • Cannot output to a sharded collection. Input collection, however, can be sharded.

  • Corresponds to SQL statements:

    • MERGE.

    • INSERT INTO T2 SELECT FROM T1.

    • SELECT INTO T2 FROM T1.

    • Create/Refresh Materialized Views.

  • Corresponds to SQL statement:

    • INSERT INTO T2 SELECT FROM T1.

    • SELECT INTO T2 FROM T1.

Warning

When $merge outputs to the same collection that is being aggregated, documents may get updated multiple times or the operation may result in an infinite loop. This behavior occurs when the update performed by $merge changes the physical location of documents stored on disk. When the physical location of a document changes, $merge may view it as an entirely new document, resulting in additional updates. For more information on this behavior, see Halloween Problem.

$merge can output to the same collection that is being aggregated. You can also output to a collection which appears in other stages of the pipeline, such as $lookup.

Restrictions
Description

An aggregation pipeline cannot use $merge inside a transaction.

An aggregation pipeline cannot use $merge to output to a time series collection.

Separate from materialized view

A view definition cannot include the $merge stage. If the view definition includes nested pipeline (for example, the view definition includes $facet stage), this $merge stage restriction applies to the nested pipelines as well.

$lookup stage

$lookup stage's nested pipeline cannot include the $merge stage.

$facet stage

$facet stage's nested pipeline cannot include the $merge stage.

$unionWith stage's nested pipeline cannot include the $merge stage.

"linearizable" read concern

The $merge stage cannot be used in conjunction with read concern "linearizable". That is, if you specify "linearizable" read concern for db.collection.aggregate(), you cannot include the $merge stage in the pipeline.

The examples on this page use data from the sample_mflix sample dataset. For details on how to load this dataset into your self-managed MongoDB deployment, see Load the sample dataset. If you made any modifications to the sample databases, you may need to drop and recreate the databases to run the examples on this page.

If the output collection does not exist, the $merge creates the collection.

Note

For a replica set or a standalone deployment, if the output database does not exist, $merge also creates the database.

For a sharded cluster deployment, the specified output database must already exist.

You can use the $group and $merge stages to create a movieRatingSummary collection that summarizes critically acclaimed films by release year and content rating:

db.movies.aggregate( [
{ $match: { metacritic: 100, rated: { $ne: null },
year: { $lte: 1972 } } },
{ $group: { _id: { year: "$year", rated: "$rated" },
count: { $sum: 1 } } },
{ $merge : { into: "movieRatingSummary", on: "_id",
whenMatched: "replace", whenNotMatched: "insert" } }
] )

The pipeline uses the following stages:

  • $match stage to filter for critically acclaimed films released through 1972 with a content rating

  • $group stage to group the films by year and rated

  • $merge stage to write the output of the preceding $group stage to the movieRatingSummary collection in the sample_mflix database

To view the documents in the new movieRatingSummary collection:

db.movieRatingSummary.find().sort(
{ _id: 1 } )
[
{ _id: { year: 1939, rated: 'PASSED' }, count: 1 },
{ _id: { year: 1962, rated: 'PG' }, count: 1 },
{ _id: { year: 1963, rated: 'PG' }, count: 1 },
{ _id: { year: 1970, rated: 'R' }, count: 1 },
{ _id: { year: 1972, rated: 'R' }, count: 1 }
]

To update the movieRatingSummary collection from the previous example to include critically acclaimed films from 1963 onward, this aggregation pipeline uses the following stages:

  • $match stage to find all films with metacritic: 100, a content rating, and a release year greater than or equal to 1963.

  • $group stage to group the films by year and rated.

  • $merge to write the result set to the movieRatingSummary collection, replacing documents with the same _id value. For documents that do not have matches in the collection, $merge inserts the new documents.

db.movies.aggregate( [
{ $match: { metacritic: 100, rated: { $ne: null },
year: { $gte: 1963 } } },
{ $group: { _id: { year: "$year", rated: "$rated" },
count: { $sum: 1 } } },
{ $merge : { into: "movieRatingSummary", on: "_id",
whenMatched: "replace", whenNotMatched: "insert" } }
] )

After the aggregation runs, view the documents in the movieRatingSummary collection:

db.movieRatingSummary.find().sort(
{ _id: 1 } )
[
{ _id: { year: 1939, rated: 'PASSED' }, count: 1 },
{ _id: { year: 1962, rated: 'PG' }, count: 1 },
{ _id: { year: 1963, rated: 'PG' }, count: 1 },
{ _id: { year: 1970, rated: 'R' }, count: 1 },
{ _id: { year: 1972, rated: 'R' }, count: 1 },
{ _id: { year: 1982, rated: 'R' }, count: 1 },
{ _id: { year: 2014, rated: 'R' }, count: 1 }
]

To ensure that the $merge does not overwrite existing data in the collection, set whenMatched to keepExisting or fail.

A collection movieArchive in the sample_mflix database contains historical records of critically acclaimed films per release year.

The movieArchive collection has a unique index on the year field. At most one record should exist per release year:

db.movieArchive.createIndex(
{ year: 1 }, { unique: true } )

This aggregation pipeline updates the movieArchive collection with data from the movies collection to include critically acclaimed films from 1963 onward. The pipeline uses the following stages:

  • $match stage to find all films with metacritic: 100, a content rating, and year >= 1963.

  • $group stage to group the film titles by year.

  • $project stage to suppress the _id field and promote year to a top-level field. When the documents are passed to $merge, $merge automatically generates a new _id field for the documents.

  • $merge to write the result set to movieArchive.

    The $merge stage matches documents on the year field and fails when matched. That is, if a document already exists for that release year, the $merge errors.

db.movies.aggregate( [
{ $match: { metacritic: 100, rated: { $ne: null },
year: { $gte: 1963 } } },
{ $group: { _id: "$year",
titles: { $push: "$title" } } },
{ $project: { _id: 0, year: "$_id", titles: 1 } },
{ $merge : { into: "movieArchive", on: "year",
whenMatched: "fail" } }
] )

If the movieArchive collection already contains a document for any year in the range 1963–2014, the aggregation fails because of the duplicate key error. However, the pipeline does not roll back any documents that it inserted before the error.

If you specify keepExisting for the matching document, the aggregation does not affect the matching document and does not error with a duplicate key error. Similarly, if you specify replace, the operation does not fail; however, the operation replaces the existing document.

By default, if a document in the aggregation results matches a document in the collection, the $merge stage merges the documents.

You can use $merge to merge results from the movies collection and the comments collection to create a new collection yearlyStats.

To create the yearlyStats collection, run the following pipeline:

db.movies.aggregate( [
{ $match: { metacritic: 100, rated: { $ne: null },
year: { $gte: 1970, $lte: 1972 } } },
{ $group: { _id: "$year", movieCount: { $sum: 1 } } },
{ $merge : { into: "yearlyStats", on: "_id",
whenMatched: "merge", whenNotMatched: "insert" } }
])
First stage:
The $match stage filters for critically acclaimed films (metacritic: 100) with a content rating, released between 1970 and 1972.
Second stage:
The $group stage groups by year and counts films into a new movieCount field.
Third stage:
The $merge stage writes the documents to the yearlyStats collection in the same database. If the stage finds an existing document in the collection that matches on the _id field, the stage merges the matching documents. Otherwise, the stage inserts the document. For the initial creation, no documents match.

To view the documents in the collection, run the following operation:

db.yearlyStats.find().sort( { _id: 1 } )
[
{ _id: 1970, movieCount: 1 },
{ _id: 1972, movieCount: 1 }
]

Similarly, run the following aggregation pipeline against the comments collection to merge the comment counts into the yearlyStats collection.

db.comments.aggregate( [
{ $match: { date: { $gte: new Date("1970-01-01"),
$lt: new Date("1973-01-01") } } },
{ $group: { _id: { $year: "$date" },
commentCount: { $sum: 1 } } },
{ $merge : { into: "yearlyStats", on: "_id",
whenMatched: "merge", whenNotMatched: "insert" } }
])
First stage:
The $match stage filters for comments posted between 1970 and 1972.
Second stage:
The $group stage groups by the year extracted from the comment date and counts comments into a new commentCount field.
Third stage:
The $merge stage writes the documents to the yearlyStats collection in the same database. If the stage finds an existing document in the collection that matches on the _id field (the year), the stage merges the matching documents. Otherwise, the stage inserts the document.

To view the documents in the yearlyStats collection after the data has been merged, run the following operation:

db.yearlyStats.find().sort( { _id: 1 } )
[
{ _id: 1970, movieCount: 1, commentCount: 889 },
{ _id: 1971, commentCount: 825 },
{ _id: 1972, movieCount: 1, commentCount: 863 }
]

The $merge can use a custom update pipeline when documents match. The whenMatched pipeline can have the following stages:

A collection monthlyCommentTotals tracks the running count of comments for each month.

Each day, new comments arrive in the sample_mflix.comments collection. The following aggregation pipeline updates the monthly total with that day's comment count:

db.comments.aggregate([
{ $match: { date: { $gte: new Date("1970-01-15"),
$lt: new Date("1970-01-16") } } },
{ $group: { _id: { $dateToString: { format: "%Y-%m",
date: "$date" } }, count: { $sum: 1 } } },
{ $merge: {
into: "monthlyCommentTotals",
on: "_id",
whenMatched: [
{ $addFields: {
count: { $add: [ "$count", "$$new.count" ] }
} }
],
whenNotMatched: "insert"
} }
])
First stage:
The $match stage finds all comments posted on January 15, 1970.
Second stage:
The $group stage groups the matching comments by year-month and counts them.
Third stage:

The $merge stage writes the documents to the monthlyCommentTotals collection. If the stage finds an existing document in the collection that matches on the _id field, the stage uses a pipeline to add the day's count to the existing monthly total.

  • This pipeline cannot directly access the fields from the results document. To access the count field in the results document, the pipeline uses the $$new variable; i.e. $$new.count.

  • This pipeline can directly access the count field in the existing document in the collection; i.e. $count.

The resulting document replaces the existing document.

To view documents in the monthlyCommentTotals collection after the merge operation, run the following operation:

db.monthlyCommentTotals.find()
[ { _id: '1970-01', count: 71 } ]

You can use variables in the $merge stage whenMatched field. Variables must be defined before they can be used.

Define variables in one or both of the following:

To use variables in whenMatched:

Specify the double dollar sign ($$) prefix together with the variable name in the form $$<variable_name>. For example, $$year. If the variable is set to a document, you can also include a document field in the form $$<variable_name>.<field>. For example, $$year.month.

The tabs below demonstrate behavior when variables are defined in the merge stage, the aggregate command, or both.

You can define variables in the $merge stage let and use the variables in the whenMatched field.

The following example:

  • seeds a movieDetails collection with a film from the movies collection

  • runs an aggregate command that defines a year variable in the $merge let and adds the year to movieDetails using whenMatched

  • retrieves the movieDetails document

db.movies.aggregate( [
{ $match: { title: "The Godfather" } },
{ $limit: 1 },
{ $project: { title: 1 } },
{ $merge: { into: "movieDetails", whenNotMatched: "insert" } }
] )
db.runCommand( {
aggregate: db.movieDetails.getName(),
pipeline: [ {
$merge: {
into: db.movieDetails.getName(),
let : { year: "2023" },
whenMatched: [ {
$addFields: { "addedYear": "$$year" }
} ]
}
} ],
cursor: {}
} )
db.movieDetails.find()
[ { _id: ..., title: 'The Godfather', addedYear: '2023' } ]

New in version 5.0.

You can define variables in the aggregate command let and use the variables in the $merge stage whenMatched field.

The following example:

  • seeds a movieDetails collection with a film from the movies collection

  • runs an aggregate command that defines a year variable in the aggregate command let and adds the year to movieDetails using whenMatched

  • retrieves the movieDetails document

db.movies.aggregate( [
{ $match: { title: "The Godfather" } },
{ $limit: 1 },
{ $project: { title: 1 } },
{ $merge: { into: "movieDetails", whenNotMatched: "insert" } }
] )
db.runCommand( {
aggregate: db.movieDetails.getName(),
pipeline: [ {
$merge: {
into: db.movieDetails.getName(),
whenMatched: [ {
$addFields: { "addedYear": "$$year" }
} ]
}
} ],
cursor: {},
let : { year: "2023" }
} )
db.movieDetails.find()
[ { _id: ..., title: 'The Godfather', addedYear: '2023' } ]

You can define variables in the $merge stage and, starting in MongoDB 5.0, the aggregate command.

If two variables with the same name are defined in the $merge stage and the aggregate command, the $merge stage variable is used.

In this example, the pipeline uses year: "2023" instead of the year: "2019" aggregate command variable:

db.movies.aggregate( [
{ $match: { title: "The Godfather" } },
{ $limit: 1 },
{ $project: { title: 1 } },
{ $merge: { into: "movieDetails", whenNotMatched: "insert" } }
] )
db.runCommand( {
aggregate: db.movieDetails.getName(),
pipeline: [ {
$merge: {
into: db.movieDetails.getName(),
let : { year: "2023" },
whenMatched: [ {
$addFields: { "addedYear": "$$year" }
} ]
}
} ],
cursor: {},
let : { year: "2019" }
} )
db.movieDetails.find()
[ { _id: ..., title: 'The Godfather', addedYear: '2023' } ]

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; }
[BsonElement("lastupdated")]
public DateTime LastUpdated { get; set; }
}

Note

ConventionPack for Pascal Case

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:

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

To use the MongoDB .NET/C# driver to add a $merge stage to an aggregation pipeline, call the Merge() method on a PipelineDefinition object.

When you call the Merge() method, you must pass an instance of the MergeStageOptions class. This object lets you specify options for the $merge stage, such as how to handle matching documents.

The following example creates a pipeline stage that merges the documents in the pipeline into the movies collection. The MergeStageOptions object specifies the following options:

  • The OnFieldNames option specifies that the operation should use the "id" and "title" fields to find matching documents in the source collection and the movies collection.

  • The WhenMatched option specifies that if a document in the source collection matches a document in the movies collection, it should replace the document in the movies collection.

  • The WhenNotMatched option specifies that if a document in the source collection does not match a document in the movies collection, it should be inserted into the movies collection.

var movieCollection = client
.GetDatabase("sample_mflix")
.GetCollection<Movie>("movies");
var pipeline = new EmptyPipelineDefinition<Movie>()
.Merge(movieCollection,
new MergeStageOptions<Movie>(
{
OnFieldNames = new List<string>() { "id", "title" },
WhenMatched = MergeStageWhenMatched.Replace,
WhenNotMatched = MergeStageWhenNotMatched.Insert,
});

The Node.js 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 Node.js driver documentation.

To use the MongoDB Node.js driver to add a $merge stage to an aggregation pipeline, use the $merge operator in a pipeline object.

The following example creates a pipeline stage that merges the documents in the pipeline into the movies collection. The example includes the following fields:

  • The on option specifies that the operation should use the "_id" and "title" fields to find matching documents in the source collection and the movies collection.

  • The whenMatched option specifies that if a document in the source collection matches a document in the movies collection, it replaces the document in the movies collection.

  • The whenNotMatched option specifies that if a document in the source collection does not match a document in the movies collection, the operation inserts the document into the movies collection.

The example then runs the aggregation pipeline:

const pipeline = [
{
$merge: {
into: "movies",
on: ["_id", "title"],
whenMatched: "replace",
whenNotMatched: "insert"
}
}
];
const cursor = collection.aggregate(pipeline);
return cursor;

Back

$match

On this page