mongo $ unwind和$ group

mch*_*ffe 5 mongodb nosql mongodb-query aggregation-framework

嗨,我是Mongo的新手,我需要收藏,其中一个收藏品我想添加对另一个的引用,并在返回时填充它.

这是一个我想要实现的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"
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

这是我得到的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"
        }
    }
}])
Run Code Online (Sandbox Code Playgroud)

结果如下:

{
    "_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"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

有没有办法实现所需的输出?还有一种方法可以不必在$group我希望返回的所有字段中定义,我想返回原始的Object但是在tags数组中引用了文档.

非常感谢.

chr*_*dam 17

由于您最初在tags数组字段上转移了原始文档,这意味着文档将被非规范化,因此$group管道应使用该_id字段作为其_id键,并使用$first$last运算符访问其他字段.

组管道运算符类似于SQL的GROUP BY子句.在SQL中,GROUP BY除非使用任何聚合函数,否则不能使用.同样,我们也必须在MongoDB中使用聚合函数,所以不幸的是,没有其他方法可以不必在$group管道中定义您希望在每个字段上使用$firstor $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" }
        }
    }
])
Run Code Online (Sandbox Code Playgroud)

每当我想调试一个产生意外结果的管道时,我总是使用的一个技巧就是只用第一个管道运算符来运行聚合.如果这给出了预期结果,请添加下一个.

在上面的答案中,你首先尝试聚合$unwind; 如果有效,请添加$lookup.这可以帮助您缩小哪个运营商导致问题.在这种情况下,您可以仅使用前三个步骤运行管道,因为您认为$group是导致问题的管道,然后从该管道检查生成的文档:

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

产生输出

/* 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"
    }
}
Run Code Online (Sandbox Code Playgroud)

从检查中你会看到,对于每个输入文档,最后一个管道输出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" }
        }
    }
])
Run Code Online (Sandbox Code Playgroud)