## Qmgo
最近,七牛CDN研发团队开源了[Qmgo](https://github.com/qiniu/qmgo) - Go语言的MongoDB driver,它基于[Mongo官方driver](https://github.com/mongodb/mongo-go-driver)实现,但是有着更好的易用性,设计上参考了老牌的driver [Mgo](https://github.com/go-mgo/mgo) (比如Mgo的链式调用)。
## 背景
做Qmgo的初衷,来自于使用MongoDB的gopher们共同的困扰,在MongoDB的Go官方driver成型前(v1.0.0发布在2019年3月),一直是Mgo一统江湖,出色的接口设计让其非常流行。七牛做为最早使用Go的公司之一,自然也是Mgo的深度用户。
但是Mgo已经在3年前不再维护,bug不修复,MongoDB的新特性自然也无法支持,而官方driver的接口设计是出名的不易用。
这样的背景下,基于满足下面的需求,Qmgo诞生了
- 想要MongoDB新特性
- 想要更稳定的driver
- 想要Mgo出色的接口设计
- 想要从Mgo迁移到Qmgo,代码改动最小
下面,简单介绍一下Qmgo的特点,详情可以点击查看:[Qmgo](https://github.com/qiniu/qmgo)
## 好用在哪里?
举一个多文件查找、`sort`和`limit`的例子, 说明`qmgo`和`mgo`的相似,以及对`go.mongodb.org/mongo-driver`的改进
官方`Driver`需要这样实现
```go
// go.mongodb.org/mongo-driver
// find all 、sort and limit
findOptions := options.Find()
findOptions.SetLimit(7) // set limit
var sorts D
sorts = append(sorts, E{Key: "weight", Value: 1})
findOptions.SetSort(sorts) // set sort
batch := []UserInfo{}
cur, err := coll.Find(ctx, bson.M{"age": 6}, findOptions)
cur.All(ctx, &batch)
```
`Qmgo`和`mgo`更简单,而且实现相似:
```go
// qmgo
// find all 、sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
// mgo
// find all 、sort and limit
coll.Find(bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
```
## 当前支持的功能
- 文档的增删改查
- 索引配置
- `Sort`、`Limit`、`Count`、`Select`
- `Cursor`
- 聚合`Aggregate`
## 使用方法
- 开始,`import`并新建连接
```go
import(
"context"
"github.com/qiniu/qmgo"
)
ctx := context.Background()
client, err := qmgo.NewClient(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017"})
db := client.Database("class")
coll := db.Collection("user")
```
如果你的连接是指向固定的database和collection,我们推荐使用下面的更方便的方法初始化连接,后续操作都基于`cli`而不用再关心database和collection
```go
cli, err := qmgo.Open(ctx, &qmgo.Config{Uri: "mongodb://localhost:27017", Database: "class", Coll: "user"})
```
***后面都会基于`cli`来举例,如果你使用第一种传统的方式进行初始化,根据上下文,将`cli`替换成`client`、`db` 或 `coll`即可***
在初始化成功后,请`defer`来关闭连接
```go
defer func() {
if err = cli.Close(ctx); err != nil {
panic(err)
}
}()
```
- 创建索引
做操作前,我们先初始化一些数据:
```go
type UserInfo struct {
Name string `bson:"name"`
Age uint16 `bson:"age"`
Weight uint32 `bson:"weight"`
}
var oneUserInfo = UserInfo{
Name: "xm",
Age: 7,
Weight: 40,
}
```
创建索引
```go
cli.EnsureIndexes(ctx, []string{}, []string{"age", "name,weight"})
```
- 插入一个文档
```go
// insert one document
result, err := cli.Insert(ctx, oneUserInfo)
```
- 查找一个文档
```go
// find one document
one := UserInfo{}
err = cli.Find(ctx, bson.M{"name": oneUserInfo.Name}).One(&one)
```
- 删除文档
```go
err = cli.Remove(ctx, bson.M{"age": 7})
```
- 插入多条数据
```go
// multiple insert
var batchUserInfoI = []interface{}{
UserInfo{Name: "a1", Age: 6, Weight: 20},
UserInfo{Name: "b2", Age: 6, Weight: 25},
UserInfo{Name: "c3", Age: 6, Weight: 30},
UserInfo{Name: "d4", Age: 6, Weight: 35},
UserInfo{Name: "a1", Age: 7, Weight: 40},
UserInfo{Name: "a1", Age: 8, Weight: 45},
}
result, err = cli.Collection.InsertMany(ctx, batchUserInfoI)
```
- 批量查找、`Sort`和`Limit`
```go
// find all 、sort and limit
batch := []UserInfo{}
cli.Find(ctx, bson.M{"age": 6}).Sort("weight").Limit(7).All(&batch)
```
- Count
````go
count, err := cli.Find(ctx, bson.M{"age": 6}).Count()
````
- Aggregate
```go
matchStage := bson.D{{"$match", []bson.E{{"weight", bson.D{{"$gt", 30}}}}}}
groupStage := bson.D{{"$group", bson.D{{"_id", "$name"}, {"total", bson.D{{"$sum", "$age"}}}}}}
var showsWithInfo []bson.M
err = cli.Aggregate(context.Background(), Pipeline{matchStage, groupStage}).All(&showsWithInfo)
```
## 欢迎大家关注和贡献,我们会尽快积极响应
有疑问加站长微信联系(非本文作者))