31

How can I get and return the first element in an array using a Mongo aggregation?

I tried using this code:

db.my_collection.aggregate([
    { $project: {
        resp : { my_field: { $slice: 1 } }
    }}
])

but I get the following error:

uncaught exception: aggregate failed: {
    "errmsg" : "exception: invalid operator '$slice'",
    "code" : 15999,
    "ok" : 0
}

Note that 'my_field' is an array of 4 elements, and I only need to return the first element.

5 Answers 5

28

Since 3.2, we can use $arrayElemAt to get the first element in an array

db.my_collection.aggregate([
    { $project: {
        resp : { $arrayElemAt: ['$my_field',0] }
    }}
])
Sign up to request clarification or add additional context in comments.

Comments

10

Currently, the $slice operator is unavailable in the the $project operation, of the aggregation pipeline. So what you could do is,

First $unwind, the my_field array, and then group them together and take the $first element of the group.

db.my_collection.aggregate([
{$unwind:"$my_field"},
{$group:{"_id":"$_id","resp":{$first:"$my_field"}}},
{$project:{"_id":0,"resp":1}}
])

Or using the find() command, where you could make use of the $slice operator in the projection part.

db.my_collection.find({},{"my_field":{$slice:1}})

Update: based on your comments, Say you want only the second item in an array, for the record with an id, id.

var field = 2;
var id = ObjectId("...");

Then, the below aggregation command gives you the 2nd item in the my_field array of the record with the _id, id.

db.my_collection.aggregate([
{$match:{"_id":id}},
{$unwind:"$my_field"},
{$skip:field-1},
{$limit:1}
])

The above logic cannot be applied for more a record, since it would involve a $group, operator after $unwind. The $group operator produces a single record for all the records in that particular group making the $limit or $skip operators applied in the later stages to be ineffective.

A small variation on the find() query above would yield you the expected result as well.

db.my_collection.find({},{"my_field":{$slice:[field-1,1]}})

Apart from these, there is always a way to do it in the client side, though a bit costly if the number of records is very large:

var field = 2; 
db.my_collection.find().map(function(doc){
return doc.my_field[field-1];
})

Choosing from the above options depends upon your data size and app design.

2 Comments

You answered my question without solving my problem unfortunately. I was in need of a generic aggregate, in which a parameter defines which element of $my_field I'll return. I try add $skip, but it isn't a $group operator.
See my updated answer. Anyway i thought my initial post had answered your question. If you required more clarification, you can always update your question or add a new question.
2

Starting Mongo 4.4, the aggregation operator $first can be used to access the first element of an array:

// { "my_field": ["A", "B", "C"] }
// { "my_field": ["D"] }
db.my_collection.aggregate([
  { $project: { resp: { $first: "$my_field" } } }
])
// { "resp" : "A" }
// { "resp" : "D" }

Comments

1

The $slice operator is scheduled to be made available in the $project operation in Mongo 3.1.4, according to this ticket: https://jira.mongodb.org/browse/SERVER-6074

This will make the problem go away.

This version is currently only a developer release and is not yet stable (as of July 2015). Expect this around October/November time.

Comments

0

Mongo 3.1.6 onwards,

db.my_collection.aggregate([
{ 
    "$project": {
        "newArray" : { "$slice" : [ "$oldarray" , 0, 1 ] }
    }
}
])

where 0 is the start index and 1 is the number of elements to slice

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.