Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Project nested array field values by their indexes in aggregation

I'm doing an aggregation in MongoDB which should have an array fields projection in its $project stage . But I can't access array fields by their indexes :

{//projection stage
  $project: {
    'foo' : { '$ifNull' : ['$bar.0.baz.0.qux', '*'] }
  }
}

This sets foo to an empty array . bar is a multidimensional array field. my MongoDB version is 3.2 . What can I do for this without the hassle of $unwind/$group old heavy solution ?

Thanks for your help .

like image 947
Ramin Omrani Avatar asked Feb 07 '23 11:02

Ramin Omrani


2 Answers

Use $slice, $map and $arrayElemAt:

{ "$project": {
    "foo": {
        "$ifNull": [
            { "$arrayElemAt": [
                { "$map": {
                    "input": { "$slice": [
                        { "$map": {
                            "input": { "$slice": [ "$bar", 0, 1 ] },
                            "as": "el",
                            "in": "$$el.baz"
                        }},
                        0, 1
                    ]},
                    "as": "el",
                    "in": { "$arrayElemAt": [ "$$el.qux", 0 ] }
                 }},
                 0
             ]},
            "*"
        ]
    }
}}

So the inner $map operators allow you to just select the particular fields out of each array, which you can $slice at the desired position to just return that element. i.e 0,1 is zero index and just one element.

The for the final resulting array you just use $arrayElemAt and retrieve the indexed element turning it into a single value.

Of course the $ifNull tests might need to be more involved depending on your structure, as if it's not consistent then you probably need to check each $map input and swap with a result accordingly.

But the general process is :

  • $map to get fields
  • $slice to reduce arrays from $map
  • $arrayElemAt on the final array result.

On something like this:

  db.foo.insert({
    "bar": [
      { 
        "baz": [
          { "qux": 2 },
          { "qux": 5 }
        ]
      },
      {
        "baz": [
          { "qux": 3 },
          { "qux": 4 }
        ]
      }
    ]
  })

Produces:

{ "_id" : ObjectId("56e8c6b8ff2a05c0da90b31e"), "foo" : 2 }
like image 150
Blakes Seven Avatar answered May 19 '23 10:05

Blakes Seven


Use multiple $project stage with $arrayElemAt operator with temporary fields.

{$project:{"tmp": {$arrayElemAt:["$bar",0]}}}, {$project:{"tmp2": {$arrayElemAt:["$tmp.baz",0]}}}

than your value is at $tmp2.qux.

like image 33
lacmuch Avatar answered May 19 '23 10:05

lacmuch