4

I am in the middle of complex aggregation query (with lookup and many group/unwind stages) and encounter problem with combining two different arrays from different fields.

As for now, after one of the stages (in the middle or after lookup of my big query) I have the following output:

{
  _id: 1,
  quantity: [2, 1, 3]
  reagents: [ 
     {name: "FirstItem", other_field: ... },
     {name: "SecondItem", other_field: ... },
     {name: "ThirdItem", other_field: ... }
  ]
}

And I want:

  _id: 1,
  reagents: [ 
     {name: "FirstItem", quantity: 2 },
     {name: "SecondItem", quantity: 1 },
     {name: "ThirdItem", quantity: 3 }
  ]

So each [quantity] value I want to add to every object inside [reagents] as field according to it's index, like first {object} inside reagents should have first element from [quantity]

Schema with array indexes):

quantity: [2, 1, 3, ... n]
           ↓  ↓  ↓      ↓
reagents: [1, 2, 3, ... n]

THE PROBLEM:

I cannot do it after or before aggregation because:

  • I don't have a necessary data, cause it's result of lookup from different collections.

  • I cannot do it after because right after this, I should $group data by another field, so [reagents] sorting order have been lost and cannot be restored.

UPD: I understand that I could (and probably should use $unwind stage for [reagents] but I haven't found any relevant Aggregation Stage Operators in Mongo Manual. So if you know a necessary one, please point me in.

1
  • Do you need to keep other fields in reagents? Commented Apr 13, 2020 at 10:41

2 Answers 2

3

You can add below stage

 { "$addFields": {
   "reagents": {
     "$map": {
       "input": { "$range": [0, { "$size": "$reagents" }] },
       "in": {
         "name": {
           "$arrayElemAt": ["$reagents.name", "$$this"]
         },
         "quantity": {
           "$arrayElemAt": ["$quantity", "$$this"]
         }
       }
     }
   }
 }}

MongoPlayground

Sign up to request clarification or add additional context in comments.

1 Comment

Just "wow". I have heard something about MapReduce, but I have never would thought that $addFields with map could be my solution. I'll dig to the botton of it.
3

@Ashh's answer will work perfectly (and afaik is the best solution) if you don't need to keep other fields than name and quantity in your results. But if you have to keep them, you'll have to add each field manually, what can be sometimes tedious. There's another 'general' solution to resolve this case, with $zip operator which is designed to merge arrays by index.

db.collection.aggregate([
  {
    $addFields: {
      reagents: {
        $map: {
          input: {
            $zip: {
              inputs: [
                "$quantity",
                "$reagents"
              ]
            }
          },
          as: "reagent",
          in: {
            $mergeObjects: [
              {
                $arrayElemAt: [
                  "$$reagent",
                  1
                ]
              },
              {
                quantity: {
                  $arrayElemAt: [
                    "$$reagent",
                    0
                  ]
                }
              }
            ]
          }
        }
      }
    }
  }
])

Mongo playground

1 Comment

Yea, thank you so much. You answer is also relevant, because in my case [reagents] also have other fields too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.