web-dev-qa-db-ja.com

mongo $ unwindおよび$ group

2つのコレクションがあります。 1つは、もう1つへの参照を追加し、戻り時にそれを設定したいです。

これは私が結果として達成しようとしているjsonの例です:

{
  "title": "Some Title",
  "uid": "some-title",
  "created_at": "1412159926",
  "updated_at": "1412159926",
  "id": "1",
  "metadata": {
    "date": "2016-10-17",
    "description": "a description"
  },
  "tags": [
    {
      "name": "Tag 1",
      "uid": "tag-1"
    },
    {
      "name": "Tag 2",
      "uid": "tag-2"
    },
    {
      "name": "Tag 3",
      "uid": "tag-3"
    }
  ]
}

これが私のクローズを取得するmongoクエリですが、_idオブジェクト内のアイテムの元の本文をネストしています。

db.tracks.aggregate([{
    $unwind: "$tags"
}, {
    $lookup: {
        from: "tags",
        localField: "tags",
        foreignField: "_id",
        as: "tags"
    }
}, {
    $unwind: "$tags"
}, {
    $group: {
        "_id": {
            "title": "$title",
            "uid": "$uid",
            "metadata": "$metadata"
        },
        "tags": {
            "$Push": "$tags"
        }
    }
}])

結果はこれです:

{
    "_id" : {
        "title" : "Some Title",
        "uid" : "some-title",
        "metadata" : {
            "date" : "2016-10-17",
            "description" : "a description"
        }
    },
    "tags" : [ 
        {
            "_id" : ObjectId("580499d06fe29ce7093fb53a"),
            "name" : "Tag 1",
            "uid" : "tag-1"
        }, 
        {
            "_id" : ObjectId("580499d06fe29ce7093fb53b"),
            "name" : "Tag 2",
            "uid" : "tag-2"
        }
    ]
}

望ましい出力を達成する方法はありますか?また、返したいすべてのフィールドを$groupで定義する必要がない方法があります。元のオブジェクトを返したいのですが、tags配列の参照ドキュメントを使用します。

8
mchaffe

最初に元のドキュメントをtags配列フィールドにピボットしました。これは、ドキュメントが非正規化されることを意味します。$groupパイプラインは、 _idフィールドを_idキーとして使用し、$firstまたは$last演算子。

グループパイプライン演算子は、SQLのGROUP BY句に似ています。 SQLでは、集計関数を使用しない限り、GROUP BYを使用できません。同様に、MongoDBでも集計関数を使用する必要があるため、残念ながら$groupパイプラインで定義する必要がない他の方法はありません。 $firstまたは$lastを使用しないで返したいすべてのフィールド=各フィールドの演算子:

db.tracks.aggregate([
    { "$unwind": "$tags" }, 
    {
        "$lookup": {
            "from": "tags",
            "localField": "tags",
            "foreignField": "_id",
            "as": "resultingArray"
        }
    }, 
    { "$unwind": "$resultingArray" },
    {
        "$group": {
            "_id": "$_id",
            "title": { "$first": "$title" },
            "uid": { "$first": "$uid" },
            "created_at": { "$first": "$created_at" },
            "updated_at": { "$first": "$updated_at" },
            "id": { "$first": "$id" },
            "metadata": { "$first": "$metadata" },
            "tags": { "$Push": "$resultingArray" }
        }
    }
])

予期しない結果をもたらすパイプラインをデバッグする場合に常に使用する1つのトリックは、最初のパイプライン演算子だけで集計を実行することです。期待どおりの結果が得られた場合は、次を追加します。

上記の答えでは、最初に$unwindだけを集計してみます。それが機能する場合は、$lookupを追加します。これにより、問題の原因となっているオペレーターを絞り込むことができます。この場合、$groupが問題の原因であると考えているため、最初の3つのステップだけでパイプラインを実行し、結果のドキュメントを検査できます。そのパイプライン:

db.tracks.aggregate([
    { "$unwind": "$tags" }, 
    {
        "$lookup": {
            "from": "tags",
            "localField": "tags",
            "foreignField": "_id",
            "as": "resultingArray"
        }
    }, 
    { "$unwind": "$resultingArray" }
])

出力を生成します

/* 1 */
{
    "_id" : ObjectId("5804a6c900ce8cbd028523d9"),
    "title" : "Some Title",
    "uid" : "some-title",
    "created_at" : "1412159926",
    "updated_at" : "1412159926",
    "id" : "1",
    "metadata" : {
        "date" : "2016-10-17",
        "description" : "a description"
    },
    "resultingArray" : {
        "name" : "Tag 1",
        "uid" : "tag-1"
    }
}

/* 2 */
{
    "_id" : ObjectId("5804a6c900ce8cbd028523d9"),
    "title" : "Some Title",
    "uid" : "some-title",
    "created_at" : "1412159926",
    "updated_at" : "1412159926",
    "id" : "1",
    "metadata" : {
        "date" : "2016-10-17",
        "description" : "a description"
    },
    "resultingArray" : {
        "name" : "Tag 2",
        "uid" : "tag-2"
    }
}

/* 3 */
{
    "_id" : ObjectId("5804a6c900ce8cbd028523d9"),
    "title" : "Some Title",
    "uid" : "some-title",
    "created_at" : "1412159926",
    "updated_at" : "1412159926",
    "id" : "1",
    "metadata" : {
        "date" : "2016-10-17",
        "description" : "a description"
    },
    "resultingArray" : {
        "name" : "Tag 3",
        "uid" : "tag-3"
    }
}

検査から、各入力ドキュメントについて、最後のパイプラインは3つのドキュメントを出力することがわかります。ここで、3は計算フィールドresultingArrayの配列要素の数であり、それらすべてに共通の_idと他のフィールドがあります異なる[resultingArray]フィールドを除いて、_idフィールドでドキュメントをグループ化するパイプラインを追加し、その後- $firstまたは$last演算子、与えられたソリューションのように:

db.tracks.aggregate([
    { "$unwind": "$tags" }, 
    {
        "$lookup": {
            "from": "tags",
            "localField": "tags",
            "foreignField": "_id",
            "as": "resultingArray"
        }
    }, 
    { "$unwind": "$resultingArray" },
    {
        "$group": {
            "_id": "$_id",
            "title": { "$first": "$title" },
            "uid": { "$first": "$uid" },
            "created_at": { "$first": "$created_at" },
            "updated_at": { "$first": "$updated_at" },
            "id": { "$first": "$id" },
            "metadata": { "$first": "$metadata" },
            "tags": { "$Push": "$resultingArray" }
        }
    }
])
19
chridam