登录
首页 >  Golang >  Go问答

怎么重用模型类型来查询和解组“混合”结果(Mgo聚合)

来源:Golang技术栈

时间:2023-03-07 10:59:24 422浏览 收藏

学习Golang要努力,但是不要急!今天的这篇文章《怎么重用模型类型来查询和解组“混合”结果(Mgo聚合)》将会介绍到golang等等知识点,如果你想深入学习Golang,可以关注我!我会持续更新相关文章的,希望对大家都能有所帮助!

问题内容

假设我们有 2 个集合:"users""posts",由以下类型建模:

type User struct {
    ID         string    `bson:"_id"`
    Name       string    `bson:"name"`
    Registered time.Time `bson:"registered"`
}

type Post struct {
    ID      string    `bson:"_id"`
    UserID  string    `bson:"userID"`
    Content string    `bson:"content"`
    Date    time.Time `bson:"date"`
}

这些可以在存储/检索单个文档甚至文档集合时使用,例如:

usersColl := sess.DB("").C("users")
postsColl := sess.DB("").C("posts")

// Insert new user:
u := &User{
    ID:         "1",
    Name:       "Bob",
    Registered: time.Now(),
},
err := usersColl.Insert(u)
// Handle err

// Get Posts in the last 10 mintes:
var posts []*Post
err := postsColl.Find(
    bson.M{"date": bson.M{"$gt": time.Now().Add(-10 * time.Minute)}},
).Limit(20).All(&posts)
// Handle err

如果我们使用聚合来获取这些文档的混合怎么办?例如Collection.Pipe()

// Query users with their posts:
pipe := collUsers.Pipe([]bson.M{
    {
        "$lookup": bson.M{
            "from":         "posts",
            "localField":   "_id",
            "foreignField": "userID",
            "as":           "posts",
        },
    },
})

var doc bson.M
it := pipe.Iter()
for it.Next(&doc) {
    fmt.Println(doc)
}
// Handle it.Err()

我们在单个查询中查询用户的帖子。结果是用户和帖子的混合。我们如何重用我们的UserPost模型类型,而不必将结果作为“原始”文档(类型bson.M)处理?

正确答案

上面的查询返回“几乎”匹配User文档的文档,但它们也包含每个用户的帖子。所以基本上结果是一系列 嵌入UserPost数组或切片的文档。 __

一种方法是向自身添加一个Posts []*Post字段User,我们将完成:

type User struct {
    ID         string    `bson:"_id"`
    Name       string    `bson:"name"`
    Registered time.Time `bson:"registered"`
    Posts      []*Post   `bson:"posts,omitempty"`
}

User虽然这可行,但Posts仅仅为了单个查询而扩展似乎“过大” 。如果我们继续沿着这条路走下去,我们的User类型会变得臃肿,包含许多用于不同查询的“额外”字段。更不用说如果我们填写Posts字段并保存用户,这些帖子最终会保存在User文档中。不是我们想要的。

另一种方法是创建一个UserWithPosts类型 copyingUser并添加一个Posts []*Post字段。不用说这是丑陋和不灵活的(任何更改User都必须UserWithPosts手动反映)。

使用结构嵌入

我们可以利用结构嵌入(重用现有的和类型) ,而不是修改原来的,而不是从“零开始”User创建新类型:UserWithPostsUser``Post

type UserWithPosts struct {
    User  `bson:",inline"`
    Posts []*Post `bson:"posts"`
}

注意 bson[标记值](https://stackoverflow.com/questions/10858787/what-are-the-uses- for-tags-in-go/30889373#30889373)",inline"。这记录在bson.Marshal()andbson.Unmarshal()(我们将使用它进行解组):

inline     Inline the field, which must be a struct or a map.
           Inlined structs are handled as if its fields were part
           of the outer struct. An inlined map causes keys that do
           not match any other struct field to be inserted in the
           map rather than being discarded as usual.

通过使用嵌入和",inline"标签值,UserWithPosts类型本身将成为解组User文档的有效目标,其Post []*Post字段将成为查找的完美选择"posts"

使用它:

var uwp *UserWithPosts
it := pipe.Iter()
for it.Next(&uwp) {
    // Use uwp:
    fmt.Println(uwp)
}
// Handle it.Err()

或一步获得所有结果:

var uwps []*UserWithPosts
err := pipe.All(&uwps)
// Handle error

的类型声明UserWithPosts可能是也可能不是本地声明。如果您在其他地方不需要它,它可以是您执行和处理聚合查询的函数中的本地声明,因此它不会使您现有的类型和声明膨胀。如果您想重用它,您可以在包级别声明它(导出或未导出),并在需要的地方使用它。

修改聚合

另一种选择是使用 MongoDB$replaceRoot来“重新排列”结果文档,因此“简单”结构将完美地覆盖文档:

// Query users with their posts:
pipe := collUsers.Pipe([]bson.M{
    {
        "$lookup": bson.M{
            "from":         "posts",
            "localField":   "_id",
            "foreignField": "userID",
            "as":           "posts",
        },
    },
    {
        "$replaceRoot": bson.M{
            "newRoot": bson.M{
                "user":  "$$ROOT",
                "posts": "$posts",
            },
        },
    },
})

通过这种重新映射,结果文档可以像这样建模:

type UserWithPosts struct {
    User  *User   `bson:"user"`
    Posts []*Post `bson:"posts"`
}

请注意,虽然这有效,但posts所有文档的字段将从服务器获取两次:一次作为posts返回文档的字段,一次作为 ; 的字段user。我们不映射/使用它,但它存在于结果文档中。因此,如果选择此解决方案,user.posts则应删除该字段,例如使用一个$project阶段:

pipe := collUsers.Pipe([]bson.M{
    {
        "$lookup": bson.M{
            "from":         "posts",
            "localField":   "_id",
            "foreignField": "userID",
            "as":           "posts",
        },
    },
    {
        "$replaceRoot": bson.M{
            "newRoot": bson.M{
                "user":  "$$ROOT",
                "posts": "$posts",
            },
        },
    },
    {"$project": bson.M{"user.posts": 0}},
})

终于介绍完啦!小伙伴们,这篇关于《怎么重用模型类型来查询和解组“混合”结果(Mgo聚合)》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧!

声明:本文转载于:Golang技术栈 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>
评论列表