各ドキュメントの構造が次のユーザーのコレクションがあります。
{
"_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
前もって感謝します
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" }
} }
])
これが解決策です。「解決済み」フィールドが存在しないか、nullに等しいか、問題とスコアの配列が解決されていることを前提としています。それが処理しない場合は、空の配列であることが「解決」されます-それはあなたが追加できる簡単な追加の調整ですが。
_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 ] );