In MongoDB, the $mergeObjects
aggregation pipeline operator combines multiple documents into a single document.
Syntax
The $mergeObjects
operator supports two syntaxes.
Syntax 1:
{ $mergeObjects: [ <document1>, <document2>, ... ] }
Syntax 2:
{ $mergeObjects: <document> }
The first syntax accepts multiple arguments and the second syntax accepts one argument.
Example of Syntax 1 (Multiple Arguments)
The first syntax involves providing $mergeObjects
with more than one argument/document. $mergeObjects
then combines those documents into one.
Suppose we have a collection called users
with the following document:
{ "_id" : 1, "name" : { "f_name" : "Homer", "l_name" : "Simpson" }, "contact" : { "email" : "[email protected]", "ph" : null } }
We can use $mergeObjects
to merge the name
and contact
fields:
db.users.aggregate(
[
{
$project:
{
user: { $mergeObjects: [ "$name", "$contact" ] }
}
}
]
).pretty()
Result:
{ "_id" : 1, "user" : { "f_name" : "Homer", "l_name" : "Simpson", "email" : "[email protected]", "ph" : null } }
In this case, we merged both fields into a single field called user
. If we had more fields/documents we could have merged those too if we wanted.
Duplicate Field Names
If the documents to be merged contain duplicate field names, $mergeObjects
overwrites the field as it merges the documents. Therefore, the field in the resulting document contains the value from the last document merged for that field.
Suppose we have the following document:
{ "_id" : 2, "name" : { "f_name" : "Peter", "l_name" : "Griffin" }, "contact" : { "email" : "[email protected]", "f_name" : "Bart" } }
We can see that both documents contain a field named f_name
.
Here’s what happens when we merge those documents:
db.users.aggregate(
[
{ $match: { _id: 2 } },
{
$project:
{
user: { $mergeObjects: [ "$name", "$contact" ] }
}
}
]
).pretty()
Result:
{ "_id" : 2, "user" : { "f_name" : "Bart", "l_name" : "Griffin", "email" : "[email protected]" } }
The f_name
field in the resulting document contains Bart
, which is the value from the last document that was merged.
Null Values
If merging a document with null
, the resulting document will be returned without any changes.
But if all documents to be merged are null
, then an empty document is returned.
Suppose we have the following documents:
{ "_id" : 3, "name" : { "f_name" : "Hubert", "l_name" : "Farnsworth" }, "contact" : null } { "_id" : 4, "name" : null, "contact" : null }
Here’s what happens when we merge the name
and contact
fields in those two documents:
db.users.aggregate(
[
{ $match: { _id: { $in: [ 3, 4 ] } } },
{
$project:
{
user: { $mergeObjects: [ "$name", "$contact" ] }
}
}
]
)
Result:
{ "_id" : 3, "user" : { "f_name" : "Hubert", "l_name" : "Farnsworth" } } { "_id" : 4, "user" : { } }
Examples of Syntax 2 (Single Argument)
Here are two examples that use the the single argument syntax.
$group
Stage Accumulator
In the first example, $mergeObjects
is used as a $group
stage accumulator.
Suppose we have a collection called products
with the following documents:
{ "_id" : 1, "product" : "Shirt", "inventory" : { "blue" : 10, "red" : 2 } } { "_id" : 2, "product" : "Shirt", "inventory" : { "green" : 3, "black" : 1 } } { "_id" : 3, "product" : "Shorts", "inventory" : { "blue" : 2, "red" : 8 } } { "_id" : 4, "product" : "Shorts", "inventory" : { "green" : 5, "black" : 3 } }
We can group these documents by their product
field, and then use $mergeObjects
to merge the inventory
field for each group:
db.products.aggregate( [
{ $group: {
_id: "$product",
mergedProducts: { $mergeObjects: "$inventory" }
}
}
]).pretty()
Result:
{ "_id" : "Shorts", "mergedProducts" : { "blue" : 2, "red" : 8, "green" : 5, "black" : 3 } } { "_id" : "Shirt", "mergedProducts" : { "blue" : 10, "red" : 2, "green" : 3, "black" : 1 } }
Arrays
This example applies $mergeObjects
to a single document that contains a field with an array of documents.
Suppose we have a collection called test
with the following documents:
{ "_id" : 1, "data" : [ { "a" : 1, "b" : 2 }, { "c" : 3, "d" : 4 } ] }
We can apply $mergeObjects
to the data
field:
db.test.aggregate(
[
{
$project:
{
result: { $mergeObjects: "$data" }
}
}
]
)
Result:
{ "_id" : 1, "result" : { "a" : 1, "b" : 2, "c" : 3, "d" : 4 } }
Missing Fields
$mergeObjects
ignores any missing fields. That is, if you supply a field that doesn’t exist, it ignores it. If none of the fields exist, then it returns an empty document.
Example:
db.users.aggregate(
[
{
$project:
{
user: { $mergeObjects: [ "$name", "$oops" ] }
}
}
]
).pretty()
Result:
{ "_id" : 1, "user" : { "f_name" : "Homer", "l_name" : "Simpson" } } { "_id" : 2, "user" : { "f_name" : "Peter", "l_name" : "Griffin" } } { "_id" : 3, "user" : { "f_name" : "Hubert", "l_name" : "Farnsworth" } } { "_id" : 4, "user" : { } }
However, here’s what happens when none of the fields exist:
db.users.aggregate(
[
{
$project:
{
user: { $mergeObjects: [ "$wrong", "$oops" ] }
}
}
]
).pretty()
Result:
{ "_id" : 1, "user" : { } } { "_id" : 2, "user" : { } } { "_id" : 3, "user" : { } } { "_id" : 4, "user" : { } }
The result is an empty document.
It’s the same when using the single-argument syntax.
Example:
db.products.aggregate( [
{ $group: {
_id: "$product",
mergedProducts: { $mergeObjects: "$oops!" }
}
}
]).pretty()
Result:
{ "_id" : "Shorts", "mergedProducts" : { } } { "_id" : "Shirt", "mergedProducts" : { } }