Получить первый элемент в массиве и вернуться с помощью агрегата? (MongoDB)

Как получить первый элемент в массиве и вернуть его в агрегате Mongo?

Я пытаюсь запустить этот код в mongo, но сбой:

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

OBS: 'my_field' - это массив с 4-мя позициями, мне нужно вернуть только первый элемент.

Возврат:

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

Ответ 1

В настоящее время оператор $slice недоступен в операции $project конвейера агрегации. Итак, что вы можете сделать,

Сначала $unwind, массив my_field, а затем сгруппируйте их вместе и возьмите элемент $first группы.

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

Или используя команду find(), где вы можете использовать оператор $slice в части projection.

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

Обновить: на основе ваших комментариев. Скажем, вы хотите только элемент second в массиве, для записи с идентификатором, id.

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

Затем команда ниже агрегации дает вам 2-й элемент в массиве my_field записи с _id, id.

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

Вышеупомянутая логика не может применяться для большей записи, так как она будет включать оператор $group после $unwind. Оператор $group создает одну запись для всех записей в этой конкретной группе, что делает операторы $limit или $skip, примененные на последующих этапах, неэффективными.

Небольшая вариация в запросе find() выше привела бы к ожидаемому результату.

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

Кроме того, всегда есть способ сделать это на стороне клиента, хотя и немного дорого, если количество записей очень велико:

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

Выбор из указанных выше параметров зависит от размера ваших данных и дизайна приложения.

Ответ 2

Начиная с 3.2, мы можем использовать $arrayElemAt, чтобы получить первый элемент в массиве

db.my_collection.aggregate([
    { $project: {
        resp : { $arrayElemAt: ['$my_field',0] }
    }}
])

Ответ 3

Оператор $slice планируется сделать доступным в операции $project в Mongo 3.1.4 в соответствии с этим билетом: https://jira.mongodb.org/browse/SERVER-6074

Это устранит проблему.

Эта версия в настоящее время является только выпуском разработчика и еще не стабильна (по состоянию на июль 2015 года). Ожидайте это примерно в октябре/ноябре.

Ответ 4

Mongo 3.1.6,

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

где 0 - начальный индекс, а 1 - количество элементов для среза