在Go语言生态中,mgo是早期广泛使用的MongoDB驱动,虽然现在官方推荐go.mongodb.org/mongo-driver,但很多存量项目仍然基于mgo开发,掌握其查询与投影的构建方法是维护这类项目的必备技能。mgo通过bson.D、bson.M等类型来构造查询条件和投影参数,能够覆盖绝大多数日常的数据查询需求。

mgo 基础环境准备
首先需要在项目中引入mgo依赖,完成数据库连接的初始化,后续的所有查询操作都基于这个会话对象展开。
package main
import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
// 初始化MongoDB连接
func initMgoSession() (*mgo.Session, error) {
// 连接本地MongoDB,端口默认27017
session, err := mgo.Dial("127.0.0.1:27017")
if err != nil {
return nil, err
}
// 设置一致性模式
session.SetMode(mgo.Monotonic, true)
return session, nil
}
构建 MongoDB 查询条件
mgo中查询条件通常使用bson.M或者bson.D来构造,bson.M是无序的键值对映射,bson.D是有序的条件切片,多数场景下使用bson.M即可满足需求。
基础等值查询
等值查询是最简单的查询场景,直接指定字段名和对应的目标值即可。
func queryByEqual(session *mgo.Session) {
// 获取test数据库的user集合
c := session.DB("test").C("user")
var result bson.M
// 查询name为张三的文档
err := c.Find(bson.M{"name": "张三"}).One(&result)
if err != nil {
// 处理错误
return
}
// 输出查询结果
fmt.Println(result)
}
范围查询与多条件查询
范围查询通过MongoDB的查询操作符实现,常见的操作符有$gt(大于)、$lt(小于)、$gte(大于等于)、$lte(小于等于)。多条件查询直接在bson.M中追加多个键值对,默认是逻辑与的关系。
func queryByRangeAndMulti(session *mgo.Session) {
c := session.DB("test").C("user")
var results []bson.M
// 查询age大于18且小于等于30,同时status为正常的文档
err := c.Find(bson.M{
"age": bson.M{
"$gt": 18,
"$lte": 30,
},
"status": "正常",
}).All(&results)
if err != nil {
return
}
for _, res := range results {
fmt.Println(res)
}
}
逻辑或查询
逻辑或查询需要使用$or操作符,值为条件数组,满足其中任意一个条件即可被匹配。
func queryByOr(session *mgo.Session) {
c := session.DB("test").C("user")
var results []bson.M
// 查询name为张三或者age小于20的文档
err := c.Find(bson.M{
"$or": []bson.M{
{"name": "张三"},
{"age": bson.M{"$lt": 20}},
},
}).All(&results)
if err != nil {
return
}
fmt.Println(results)
}
数组与嵌套字段查询
如果文档中包含数组字段或者嵌套对象字段,查询时可以直接通过字段路径匹配,数组字段的查询会匹配数组中包含目标元素的文档。
func queryArrayAndNested(session *mgo.Session) {
c := session.DB("test").C("user")
var results []bson.M
// 查询hobbies数组中包含读书的文档
err := c.Find(bson.M{"hobbies": "读书"}).All(&results)
if err != nil {
return
}
// 查询嵌套字段addr.city为北京的文档
err = c.Find(bson.M{"addr.city": "北京"}).All(&results)
if err != nil {
return
}
fmt.Println(results)
}
构建投影参数筛选返回字段
投影用于指定查询返回的字段,减少不必要的数据传输,提升查询性能。在mgo中,投影参数同样是bson.M类型,键为字段名,值为1表示返回该字段,0表示不返回该字段,_id字段默认会返回,需要显式设置为0才会排除。
func queryWithProjection(session *mgo.Session) {
c := session.DB("test").C("user")
var result bson.M
// 只返回name和age字段,不返回_id和其他字段
projection := bson.M{
"name": 1,
"age": 1,
"_id": 0,
}
err := c.Find(bson.M{"status": "正常"}).Select(projection).One(&result)
if err != nil {
return
}
fmt.Println(result)
}
查询与投影结合使用示例
实际开发中通常会同时结合查询条件和投影参数,实现精准的数据筛选和字段返回。
func queryWithConditionAndProjection(session *mgo.Session) {
c := session.DB("test").C("user")
var results []bson.M
// 查询age大于20的文档,只返回name、age、hobbies三个字段
condition := bson.M{"age": bson.M{"$gt": 20}}
projection := bson.M{
"name": 1,
"age": 1,
"hobbies": 1,
"_id": 0,
}
err := c.Find(condition).Select(projection).All(&results)
if err != nil {
return
}
for _, res := range results {
fmt.Println(res)
}
}
注意事项
- 查询条件中的字段名需要和MongoDB文档中的字段名完全一致,区分大小写。
- 投影参数中不能同时设置同一个字段为1和0,除了
_id字段可以单独设置为0之外,其他字段只能统一设置返回或者不返回。 - mgo的会话对象不是线程安全的,多个goroutine使用时需要调用
session.Copy()获取副本使用,使用完毕后调用Close()释放。 - 如果查询结果为空,
One方法会返回mgo.ErrNotFound错误,需要做对应的错误处理。