Reputation: 7374
2 Collections are available in the DB:
Products:
{
"_id":"unique_product_id",
"name":"Product 1",
"price":50
}
Events:
{
"_id":"unique_event_id_1",
"product_id":"unique_product_id",
"action":"created"
},
{
"_id":"unique_event_id_2",
"product_id":"unique_product_id",
"action":"updated"
}
where Events
collection contains documents associated with documents from Products
collection.
The idea that I have is just to inject documents from Events
collection to Products
, as results the Products
will look like:
Products:
{
"_id":"unique_product_id",
"name":"Product 1",
"price":50,
"events":[
{
"_id":"unique_event_id",
"product_id":"unique_product_id_1",
"action":"created"
},
{
"_id":"unique_event_id",
"product_id":"unique_product_id_1",
"action":"updated"
}
]
}
And I want to understand if it possible to implement it as part of the Mongodb Aggregation framework
?
I need to find something like $out
that takes the documents returned by the aggregation pipeline and writes them to a specified collection, but I need to write not to a specific collection, but to the specific document of the specific collection.
Upvotes: 0
Views: 585
Reputation: 3444
Starting in MongoDB 4.4, $merge can output to the same collection that is being aggregated:
db.products.aggregate([
{ /**
* from: The target collection.
* localField: The local join field.
* foreignField: The target join field.
* as: The name for the results.
* pipeline: The pipeline to run on the joined collection.
* let: Optional variables to use in the pipeline field stages.
*/
$lookup: {
from: 'events',
localField: '_id',
foreignField: 'product_id',
as: 'events'
}},
{/**
* into: The target collection.
* on: Fields to identify.
* whenMatched: Action for matching docs.
* whenNotMatched: Action for non-matching docs.
*/
$merge: {
into: 'products',
on: "_id",
whenMatched: 'merge',
whenNotMatched: 'insert'
}}
])
Be aware: 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. More details here https://docs.mongodb.com/manual/reference/operator/aggregation/merge/#merge-behavior-same-collection
If it is a one off update you can safeguard the pipeline by adding initial filter as the first stage to ensure a document is updated exactly once:
{ $match: { events: { $exists: false } }
Upvotes: 1