我搜索并没有找到解决该问题的Go解决方案,无论是否使用mgo.v2
,都没有,也没有在其他任何站点上。
假设我们users
在Gogo中建模了一个MongoDB集合struct
:
type User struct {
ID bson.ObjectId `bson:"_id"`
Name string `bson:"name"`
Country string `bson:"country"`
}
我们希望根据某些条件对用户进行排序和列出,但是由于预期结果列表较长,因此已实现了分页。
为了实现对某些查询结果的分页,MongoDB和mgo.v2
驱动程序包以Query.Skip()
和的形式内置支持Query.Limit()
,例如:
session, err := mgo.Dial(url) // Acquire Mongo session, handle error!
c := session.DB("").C("users")
q := c.Find(bson.M{"country" : "USA"}).Sort("name", "_id").Limit(10)
// To get the nth page:
q = q.Skip((n-1)*10)
var users []*User
err = q.All(&users)
但是,如果页数增加,这将变得很慢,因为MongoDB不能仅“神奇地”跳到结果中的第x
个文档,它必须遍历所有结果文档并忽略(不返回)x
需要删除的第一个文档跳过。
MongoDB提供了正确的解决方案:如果查询对索引进行操作(必须对索引进行操作),cursor.min()
则可用于指定第一个
索引条目 以开始列出结果。
注意:以上查询所需的索引为:
db.users.createIndex(
{
country: 1,
name: 1,
_id: 1
}
)
但是,有一个问题:mgo.v2
软件包不支持指定this min()
。
我们如何cursor.min()
通过mgo.v2
驱动程序使用MongoDB的功能来实现有效的分页?
不幸的是,mgo.v2
驱动程序没有提供API调用来指定cursor.min()
。
但是有一个解决方案。该mgo.Database
类型提供了Database.Run()
一种运行任何MongoDB命令的方法。可在此处找到可用的命令及其文档:数据库命令
从MongoDB
3.2开始,提供了一个新find
命令,该命令可用于执行查询,并且支持指定min
表示第一个索引条目的参数,该索引条目开始列出结果。
好。我们需要做的是在每批(一页min
文档)从查询结果的最后一个文档生成文档之后,该文档必须包含用于执行查询的索引条目的值,然后是下一个批次(可以通过在执行查询之前设置此最小索引条目来获取下一页的文档)。
该索引条目(从现在开始将其称为 光标) 可以被编码为a string
并与结果一起发送给客户端,并且当客户端想要下一页时,他会发回 光标,
说他想要从该光标之后开始的结果。
要执行的命令可以采用不同的形式,但是命令名称(find
)必须在经过编组的结果中位于第一个位置,因此我们将使用bson.D
(与相比保留了顺序bson.M
):
limit := 10
cmd := bson.D{
{Name: "find", Value: "users"},
{Name: "filter", Value: bson.M{"country": "USA"}},
{Name: "sort", Value: []bson.D{
{Name: "name", Value: 1},
{Name: "_id", Value: 1},
},
{Name: "limit", Value: limit},
{Name: "batchSize", Value: limit},
{Name: "singleBatch", Value: true},
}
if min != nil {
// min is inclusive, must skip first (which is the previous last)
cmd = append(cmd,
bson.DocElem{Name: "skip", Value: 1},
bson.DocElem{Name: "min", Value: min},
)
}
可以使用以下类型捕获执行MongoDB find
命令的结果Database.Run()
:
var res struct {
OK int `bson:"ok"`
WaitedMS int `bson:"waitedMS"`
Cursor struct {
ID interface{} `bson:"id"`
NS string `bson:"ns"`
FirstBatch []bson.Raw `bson:"firstBatch"`
} `bson:"cursor"`
}
db := session.DB("")
if err := db.Run(cmd, &res); err != nil {
// Handle error (abort)
}
现在,我们得到结果,但类型为[]bson.Raw
。但是我们希望它是type的一部分[]*User
。这是Collection.NewIter()
方便的地方。它可以将类型的值转换(解组)为[]bson.Raw
我们通常传递给Query.All()
或的任何类型Iter.All()
。好。让我们来看看它:
firstBatch := res.Cursor.FirstBatch
var users []*User
err = db.C("users").NewIter(nil, firstBatch, 0, nil).All(&users)
现在,我们有了下一页的用户。只剩下一件事了:如果需要,生成用于获取下一页的游标:
if len(users) > 0 {
lastUser := users[len(users)-1]
cursorData := []bson.D{
{Name: "country", Value: lastUser.Country},
{Name: "name", Value: lastUser.Name},
{Name: "_id", Value: lastUser.ID},
}
} else {
// No more users found, use the last cursor
}
一切都很好,但是我们如何将a转换cursorData
为string
,反之亦然?我们可能会使用bson.Marshal()
和bson.Unmarshal()
用base64编码相结合;
的使用base64.RawURLEncoding
将为我们提供一个网络安全的游标字符串,可以将其添加到URL查询中而不进行转义。
这是一个示例实现:
// CreateCursor returns a web-safe cursor string from the specified fields.
// The returned cursor string is safe to include in URL queries without escaping.
func CreateCursor(cursorData bson.D) (string, error) {
// bson.Marshal() never returns error, so I skip a check and early return
// (but I do return the error if it would ever happen)
data, err := bson.Marshal(cursorData)
return base64.RawURLEncoding.EncodeToString(data), err
}
// ParseCursor parses the cursor string and returns the cursor data.
func ParseCursor(c string) (cursorData bson.D, err error) {
var data []byte
if data, err = base64.RawURLEncoding.DecodeString(c); err != nil {
return
}
err = bson.Unmarshal(data, &cursorData)
return
}
最后,我们有了高效但不那么短的MongoDB mgo
分页功能。继续阅读…
github.com/icza/minquery
(“简单”方式)手动方式相当冗长;它可以 通用 和 自动化
。这就是github.com/icza/minquery
图片所在的位置(
披露:我是作者 )。它提供了一个包装器来配置和执行MongoDB
find
命令,允许您指定一个游标,并且在执行查询后,它会返回给您新的游标,以用于查询下一批结果。包装器的MinQuery
类型非常相似,mgo.Query
但是它支持min
通过MinQuery.Cursor()
方法指定MongoDB的类型。
上面使用的解决方案minquery
如下所示:
q := minquery.New(session.DB(""), "users", bson.M{"country" : "USA"}).
Sort("name", "_id").Limit(10)
// If this is not the first page, set cursor:
// getLastCursor() represents your logic how you acquire the last cursor.
if cursor := getLastCursor(); cursor != "" {
q = q.Cursor(cursor)
}
var users []*User
newCursor, err := q.All(&users, "country", "name", "_id")
就这样。newCursor
是用于获取下一批的光标。
注意#1: 调用时MinQuery.All()
,必须提供游标字段的名称,这将用于从中构建游标数据(最终是游标字符串)。
注意#2:
如果您要检索部分结果(使用MinQuery.Select()
),则即使您不打算直接使用它们,也必须包括所有属于光标的字段(索引条目),否则MinQuery.All()
就不会游标字段的所有值,因此它将无法创建正确的游标值。
在minquery
此处查看打包文档:https :
//godoc.org/github.com/icza/minquery,它很短,希望可以清理。
问题内容: 我读过多个有关使用mgo保存文件的博客,但找不到满足特定需求的解决方案,请大声喊叫! 下面在MongoDb中插入对象: 下面能够保存文件: 现在,在插入对象之前,我需要将上述文件设置为: 我的结构对象: 用外行术语来说,问题是:如何使用mgo驱动程序通过GoLang结构在mongoDb中插入文件(从HTML表单接收)? 谢谢阅读!:) 更新: PDF存储在MongoDB中,如下所示:
问题内容: 我目前正在尝试在mongodb内部的文档数组中提取单个对象。这是一个样本数据集: 我可以在mongo中使用以下命令成功查询: 我无法使用mgo进行相同操作,并尝试了以下操作: 使用嵌套(抛出:合成文字中缺少类型,地图文字中缺少键) 我正在使用httprouter,p.ByName(“ …”)调用是传递给处理程序的参数。 提前致谢。 问题答案: 将与该 方法一起使用,因为doc指出,这使
问题内容: 我想在管道中使用mgo在golang中运行以下查询。 我到处都看过,但是找不到这样的例子。我尝试了许多不同的组合,例如: 正确编译的,什么也找不到。有任何想法吗? 先感谢您 问题答案: 您的mongo查询可以转换为以下内容: 该查询应等效于mongo控制台中的以下内容: 如果您希望使用无序列图,则可能是这样的:
问题内容: 我正在通过mgo运行地图缩减任务。它运行在具有超过350万条记录的集合上。由于某些原因,我现在无法将此端口移植到聚合中。可能会晚一些。因此,map- reduce是我期待的事情。当我从为测试代码和输出而创建的原始js文件运行该作业时,它运行良好。我试图将地图和减少代码放在两个字符串中,然后尝试调用mgo.MapReduce为我做地图减少,以便在其他集合中编写输出。它给了我 读取TCP
问题内容: 有人知道我们在mongodb shell中对golang mgo / bson使用的聚合命令等效吗? 像这样: 问题答案: 假设这是您的收藏夹: GoDoc参考: Collection.Pipe文档 管道及其方法
我想在RESTful API中支持分页。 我的API方法应该通过返回产品的JSON列表http://localhost/products/v1/getproductsbycategory,可能有数千种产品,我想翻阅它们,因此我的请求应该如下所示: