$ unwind пустой массив

У меня есть группа пользователей, в которой каждый документ имеет следующую структуру:

{
  "_id": "<id>",
  "login": "xxx",
  "solved": [
    {
      "problem": "<problemID>",
      "points": 10
    },
    ...
  ]
}

Поле solved может быть пустым или содержать произвольное количество поддокументов. Моя цель - получить список пользователей вместе с общей оценкой (сумма points), где пользователям, которые еще не решили какую-либо проблему, будет назначена общая оценка 0. Возможно ли это сделать с помощью одного запроса (в идеале, используя структура агрегирования)?

Я пытался использовать следующий запрос в структуре агрегирования:

{ "$group": {
  "_id": "$_id",
  "login": { "$first": "$login" },
  "solved": { "$addToSet": { "points": 0 } }
} }
{ "$unwind": "$solved" }
{ "$group": {
  "_id": "$_id",
  "login": { "$first": "$login" },
  "solved": { "$sum": "$solved.points" }
} }

Однако я получаю следующую ошибку:

exception: The top-level _id field is the only field currently supported for exclusion

заранее спасибо


person Karel Horak    schedule 15.12.2012    source источник
comment
когда вы говорите, что решено может быть пустым, присутствует ли он и пустой массив или он отсутствует, если проблемы не решены?   -  person Asya Kamsky    schedule 16.12.2012
comment
Кроме того, $ first login получает первое значение (по порядку массива) - это то, что вы хотите, или вы хотите $ min? Если проблемы не решены, может ли логин также быть нулевым или отсутствующим?   -  person Asya Kamsky    schedule 16.12.2012


Ответы (2)


В версии MongoDB 3.2 и новее оператор $unwind теперь имеет некоторые варианты, где, в частности, это решит опция preserveNullAndEmptyArrays.

Если для этого параметра установлено значение true и если путь имеет значение null, отсутствует или пустой массив, $unwind выводит документ. Если false, $unwind не выводит документ, если путь имеет значение NULL, отсутствует или пустой массив. В вашем случае установите значение true:

db.collection.aggregate([
    { "$unwind": {
            "path": "$solved",
            "preserveNullAndEmptyArrays": true
    } },
    { "$group": {
        "_id": "$_id",
        "login": { "$first": "$login" },
        "solved": { "$sum": "$solved.points" }
    } }
])
person chridam    schedule 24.05.2016
comment
Отличный вопрос и отличный ответ. Ты спас мне день - person Hemadri Dasari; 19.03.2021

Вот решение - оно предполагает, что поле «решено» либо отсутствует, равно нулю, либо имеет решенный массив проблем и оценок. Случай, который он не обрабатывает, «решен» как пустой массив - хотя это было бы простой дополнительной настройкой, которую вы могли бы добавить.

project = {$project : {
        "s" : {
            "$ifNull" : [
                "$solved",
                [
                    {
                        "points" : 0
                    }
                ]
            ]
        },
        "login" : 1
    }
};
unwind={$unwind:"$s"};
group= { "$group" : {
        "_id" : "$_id",
        "login" : {
            "$first" : "$login"
        },
        "score" : {
            "$sum" : "$s.points"
        }
    }
}

db.students.aggregate( [ project, unwind, group ] );

person Asya Kamsky    schedule 16.12.2012
comment
Привет @AsyaKamsky, я пробовал ваши коды на MongoDB 3.0.3, но $ ifNull не работает точно? Также я ничего не мог об этой проблеме на docs.mongodb .org / manual / reference / operator / aggregation / ifNull /. - person efkan; 18.08.2015
comment
Вместо этого я использовал 's': { $cond: [{$eq: [{$size: '$solved'}, 0] }, [ { point: 0 } ], '$solved'] } ‹br› и сообщил о Jira. - person efkan; 18.08.2015
comment
На момент написания этого ответа $ size не существовало, поэтому я не мог использовать его в решении. В следующей версии (3.2) также будет оператор $ isArray. Я не знаю, что вы имеете в виду, говоря, что $ ifNull не работает точно, поэтому не могу это комментировать. - person Asya Kamsky; 18.08.2015
comment
То есть, я думал, что $ifNull будет работать как с документацией { $ifNull: [ <expression>, <replacement-expression-if-null> ] }. Однако это не заменило выражение, подобное вашим кодам выше. Подскажите, пожалуйста, есть ли для меня альтернативный оператор? (из-за отсутствия оператора $size) - person efkan; 18.08.2015
comment
Я решил, используя 's': { $cond: [{$eq: ['$solved', []] }, [ { point: 0 } ], '$solved'] }. Спасибо, в любом случае... - person efkan; 18.08.2015
comment
$ ifNull заменяет только значения нулевого типа (отсутствующие или нулевые), это не должно заменять [] значение, так как оно сильно отличается от нулевого. - person Asya Kamsky; 18.08.2015
comment
Спасибо, @AsyaKamsky, и я хочу извиниться перед вами за ложную тревогу. Проработав 11 часов, я подумал, что столкнулся с ошибкой. И еще одна ошибка по поводу оператора $size, связанного с моим грамматиком английского языка. Извините, опять же за ненужный трафик как здесь, так и в Jira. Хорошего дня.. - person efkan; 20.08.2015