dongmeng1875 2016-08-04 03:01
浏览 62
已采纳

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

I have a db structure defined in a following way.

{
    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"
        }
    ]
}

I want to return a slice of all films - []Film and, if possible, in another query a slice of all titles - []string from the collection. I can pull out entire collection and extract relevant data in the application logic, but is it possible to achieve within a query? I've tried to work with Select() method, something like this: c.Find(nil).Select(<various conditions>).All(&results) but I wasn't successful.

  • 写回答

1条回答 默认 最新

  • dtf24224 2016-08-04 08:22
    关注

    I think this is one of the most popular questions on mongo tag. And I must say if you need that you are doing something wrong and maybe you should use RDBMS instead of Mongo as performance drop on this kind of queries will nullify all profit from Mongo's features like schemaless, "all-in-one" documents, etc..

    Anyway, the answer is simple - you can't get films list as you want. Mongo's find can return only complete or partial top-level documents. I mean the best result you can get with the db.collection.find({}, {'films': 1}) query is the list like

    {
        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
            }
        ]
    }
    

    Not what you are expectied, right?

    The only way to get array like

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

    is to use <kbd>aggregation</kbd>.

    The basic Mongo query to retrieve array of films is

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

    Go's code for this query is

    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]]
    }
    

    If you need additional conditions to select top-level documents code would be

    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")]]
    

    And if you need to filter films you can use next query

    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]]
    

    The problem with aggregation is the simple fact that most part of aggregation operations doesn't use the indexes and could be slow on large collections. That's why I suggested you to think about RDBMS which can be better choice if you need a lot of aggregations.

    And there is no way to get []string from mgo as it always returns bson.M (or []bson.M) which is map[string]interface{}.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 在获取boss直聘的聊天的时候只能获取到前40条聊天数据
  • ¥20 关于URL获取的参数,无法执行二选一查询
  • ¥15 液位控制,当液位超过高限时常开触点59闭合,直到液位低于低限时,断开
  • ¥15 marlin编译错误,如何解决?
  • ¥15 有偿四位数,节约算法和扫描算法
  • ¥15 VUE项目怎么运行,系统打不开
  • ¥50 pointpillars等目标检测算法怎么融合注意力机制
  • ¥20 Vs code Mac系统 PHP Debug调试环境配置
  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集