4

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 .

2 Answers 2

5

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 }
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks man . I'm trying to understand this solution. It seems to work well . I'll check your answer soon . its sad that they haven't replaced this giant code snippet with simple javascript object access pattern. Hope to see that in future releases.
3

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.

Comments

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.