web-dev-qa-db-ja.com

$ 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

前もって感謝します

23
Karel Horak

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" }
    } }
])
72
chridam

これが解決策です。「解決済み」フィールドが存在しないか、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 ] );

8
Asya Kamsky