MongoDB
是一个非常强大的数据库,它提供了一个非常简单的接口,可以让我们在Go
中使用它。
前几天用gin
+gorm
实现一个小功能,根据操作的方法,发现很容易实现通用的CRUD
操作。
比如批量查询数据时,可以定义一个interface
或具体结构体去绑定结果集:
# 返回值也可以定义为interface{}
func FindAllAd(filter interface{}, sort interface{}) ([]model.Ad, error) {
cursor, err := model.Mongo.Collection((&model.Ad{}).CollectionName()).Find(context.Background(), filter, options.Find().SetSort(sort))
if err != nil {
return nil, err
}
defer cursor.Close(context.Background())
var results []model.Ad
for cursor.Next(context.Background()) {
# 根据返回值的类型,可以定义为interface{}
var result model.Ad
err := cursor.Decode(&result)
if err != nil {
return nil, err
}
results = append(results, result)
}
return results, nil
}
上述的代码里,加入返回值是interface{}
,那么返回结果的格式是这样的:
[[{Key:_id Value:3644d977-d2ff-430e-a8ca-b19c643fcb91} {Key:title Value:重版来袭} {Key:url Value:} {Key:type Value:0} {Key:sort Value:0} {Key:create_at Value:1659175157} {Key:update_at Value:1659175157}]]
这样的格式不方便前端处理。
如果是绑定具体的结构体,那么返回结果的格式是这样的:
[{ID:3644d977-d2ff-430e-a8ca-b19c643fcb91 Title:重版来袭 Url: Type:0 Sort:0 CreateAt:1659175157 UpdateAt:1659175157}]
显然下面这种方式更加方便处理。 如果不做封装的情况下,需要在每个模型结构体去定义查询方法,那么就需要额外增加代码,通用型不强。 于是我就尝试用范型去封装通用查询方法:
package model
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type IMongo interface {
CollectionName() string
}
type Dao[T IMongo] []T
func (d *Dao[T]) CollectionName() string {
var m T
return m.CollectionName()
}
// InsertOne 新增单条记录
func (d *Dao[T]) InsertOne(document T) (*mongo.InsertOneResult, error) {
return Mongo.Collection(d.CollectionName()).InsertOne(context.Background(), document)
}
// FindAll 获取所有记录
func (d *Dao[T]) FindAll(filter interface{}, sort interface{}) ([]T, error) {
cursor, err := Mongo.Collection(d.CollectionName()).Find(context.Background(), filter, options.Find().SetSort(sort))
if err != nil {
return nil, err
}
defer cursor.Close(context.Background())
var results []T
for cursor.Next(context.Background()) {
var result T
err := cursor.Decode(&result)
if err != nil {
return nil, err
}
results = append(results, result)
}
return results, nil
}
至于要额外封装IMongo
的接口,主要考虑两个原因:
- 每个模型结构体要额外定义一个返回文档名称的方法,作为操作目标;
- 目前
interface
只能约束基本类型,但结构体不支持,比如:# 如果是结构体不支持约束 type Number interface { int | int32 | int64 | float32 | float64 }
即使后面能支持约束,也不方便维护,因为每增加一个模型就要去修改这个约束接口。