Go - mgo,从集合中检索所有嵌套字段

时间:2016-08-04 03:01:55

标签: mongodb go mgo

我有一个以下列方式定义的db结构。

{
    name: "Jane",
    films: [
        {
            title: "The Shawshank Redemption",
            year: "1994"
        },
        {
            title: "The Godfather",
            year: "1972"
        }
    ]
},
{
    name: "Jack",
    films: [
        {
            title: "12 Angry Men",
            year: "1957"
        },
        {
            title: "The Dark Knight",
            year: "2008"
        }
    ]
}

我希望返回所有电影的片段 - []Film,如果可能的话,在另一个查询中返回所有标题的片段 - 来自集合的[]string。我可以在应用程序逻辑中提取整个集合并提取相关数据,但是可以在查询中实现吗?我尝试使用Select()方法,类似这样:c.Find(nil).Select(<various conditions>).All(&results)但我没有成功。

1 个答案:

答案 0 :(得分:2)

我认为这是mongo标签上最受欢迎的问题之一。我必须说,如果你需要你做错了,也许你应该使用RDBMS而不是Mongo,因为这种查询的性能下降将使Mongo的所有功能无效,如无模式,“一体化”文档等..

无论如何,答案很简单 - 你无法按照自己的意愿获得电影列表。 Mongo的find只能返回完整或部分顶级文档。我的意思是使用db.collection.find({}, {'films': 1})查询可以获得的最佳结果是

之类的列表
{
    films: [
        {
            title: "The Shawshank Redemption",
            year: 1994
        },
        {
            title: "The Godfather",
            year: 1972
        }
    ]
},
{
    films: [
        {
            title: "12 Angry Men",
            year: 1957
        },
        {
            title: "The Dark Knight",
            year: 2008
        }
    ]
}

不是你所期待的,对吧?

获取数组的唯一方法

{
    title: "The Shawshank Redemption",
    year: 1994
},
{
    title: "The Godfather",
    year: 1972
},
{
    title: "12 Angry Men",
    year: 1957
},
{
    title: "The Dark Knight",
    year: 2008
}

是使用 aggregation

检索电影数组的基本Mongo查询是

db.collection.aggregate([{
    $unwind: '$films'
}, {
    $project: {
        title: '$films.title',
        year: '$films.year'
    }
}])

这个查询的Go代码是

package main

import (
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
    "fmt"
)

func main() {
    session, err := mgo.Dial("mongodb://127.0.0.1:27017/db")

    if err != nil {
        panic(err)
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)

    c := session.DB("db").C("collection")

    pipe := c.Pipe(
        []bson.M{
            bson.M{
                "$unwind": "$films",
            },
            bson.M{
                "$project": bson.M{
                    "title": "$films.title",
                    "year": "$films.year",
                },
            },
        },
    )
    result := []bson.M{}
    err = pipe.All(&result)
    fmt.Printf("%+v", result) // [map[_id:ObjectIdHex("57a2ed6640ce01187e1c9164") title:The Shawshank Redemption year:1994] map[_id:ObjectIdHex("57a2ed6640ce01187e1c9164") title:The Godfather year:1972] map[_id:ObjectIdHex("57a2ed6f40ce01187e1c9165") title:12 Angry Men year:1957] map[year:2008 _id:ObjectIdHex("57a2ed6f40ce01187e1c9165") title:The Dark Knight]]
}

如果您需要其他条件来选择顶级文档代码

pipe := c.Pipe(
    []bson.M{
        bson.M{
            "$match": bson.M{
                "name": "Jane",
            },
        },
        bson.M{
            "$unwind": "$films",
        },
        bson.M{
            "$project": bson.M{
                "title": "$films.title",
                "year": "$films.year",
            },
        },
    },
)
// result [map[_id:ObjectIdHex("57a2ed6640ce01187e1c9164") title:The Shawshank Redemption year:1994] map[title:The Godfather year:1972 _id:ObjectIdHex("57a2ed6640ce01187e1c9164")]]

如果您需要过滤电影,可以使用下一个查询

pipe := c.Pipe(
    []bson.M{
        bson.M{
            "$unwind": "$films",
        },
        bson.M{
            "$project": bson.M{
                "title": "$films.title",
                "year": "$films.year",
            },
        },
        bson.M{
            "$match": bson.M{
                "year": bson.M{
                    "$gt": 2000,
                },
            },
        },
    },
)
// result [map[_id:ObjectIdHex("57a2ed6f40ce01187e1c9165") year:2008 title:The Dark Knight]]

聚合问题是一个简单的事实,即聚合操作的大部分都不使用索引,并且在大型集合上可能会很慢。这就是我建议你考虑RDBMS的原因,如果你需要大量的聚合,这可能是更好的选择。

并且无法从[]string获取mgo,因为它总是返回bson.M(或[]bson.Mmap[string]interface{}