user471011
user471011

Reputation: 7374

Mongo DB: How to copy the Document from one collection and add it as a field to a related document from another collection?

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

Answers (1)

Yahya
Yahya

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

Related Questions