Search code examples
mongodbaggregation-framework

How do you access a specific array item in MongoDB projection aggregation?


I'm trying to create a projection within a MongoDB aggregation function, see the following:

[
    {$match : {"geo" : {$ne: null}}},
    {$project : {"id" : "$id_str", lat: "$geo.coordinates.0", lon: "$geo.coordinates.1"}}
]

However when I do this it does not pass over the array item it simply projects an empty array to the properties lat and lon.

What is needed here? I have looked at the Documentation but cannot figure it out, and even tried a permutation of $unwind and $group but with no success.


Solution

  • The feature that will help you achieve this is not yet available. However, there will be a new aggregation operator that gives an array element for a given index. The new expression is called $arrayElemAt

    Use the new aggregation operator that's available for MongoDB versions 3.2.X and greater, this returns an array element for a given index. The new expression is called $arrayElemAt. It takes two arguments, an array and an index and returns the element at the given index in the array. Negative indices are accepted as indexes from the back of the array. If the index is out of bounds, it returns the missing value, meaning the field will not exist in the output:

    var pipeline = [    
        { $match : {"geo" : {$ne: null}}},
        {
            $project: {
                _id: "$id_str", 
                lat: { $arrayElemAt: ['$geo.coordinates', 0] },
                lon: { $arrayElemAt: ['$geo.coordinates', 1] }
            }
        }
    ];
    

    As a workaround for now (assuming the coordinates array will always have two elements at any given time), you could try the following aggregation pipeline which will take advantage of the $first and $last group accumulator operators to get the elements after a $sort:

    var pipeline = [
        {$match : {"geo" : {$ne: null}}},
        { "$unwind": "$geo.coordinates" },
        { "$sort": {"geo.coordinates": 1} } ,
        {
            "$group": {
                "_id": "$_id",
                "lat": { "$first": "$geo.coordinates" },
                "lon": { "$last": "$geo.coordinates" }
            }
        }
    ];