go客户端实现mongoDB的增删改查
所有api的使用和说明都在官方文档: https://pkg.go.dev/go.mongodb.org/mongo-driver/mongo#pkg-index
下面只记录一下项目中用到的简单的增删改查操作
可能用过mysql和redis客户端的不太习惯mongo客户端的操作,它获取一个表操作对象分成了几个部分:获取客户端连接、选择数据库、选择表
// 建立连接
client, err := mongo.Connect(context.TODO(),
options.Client().ApplyURI("mongodb://xxx.xxx.xxx.xxx:27017"),
options.Client().SetConnectTimeout(5*time.Second))
if err != nil {
fmt.Println(err)
return
}
// 选择数据库
database := client.Database("cron")
// 选择表log
collection := database.Collection("log")
因为mongo中数据是使用bson格式存储的,所以在定义插入数据结构体时,需要使用bson标签进行反射
// 任务的执行时间点
type TimePoint struct {
StartTime int64 `bson:"startTime"`
EndTime int64 `bson:"endTime"`
}
// 日志结构
type logRecord struct {
JobName string `bson:"jon_name"` //任务名
Command string `bson:"commond"` // shell命令
Err string `bson:"err"` // 脚本错误
Content string `bson:"content"` //脚本输出
TimePoint TimePoint `bson:"timePoint"` //执行时间
}
1. 向mongoDB中插入数据
插入数据是对我们获取到的表操作对象进行的,具体的插入函数签名有如下几个:
func (coll *Collection) InsertMany(ctx context.Context, documents []interface{}, ...) (*InsertManyResult, error)
func (coll *Collection) InsertOne(ctx context.Context, document interface{}, opts ...*options.InsertOneOptions) (*InsertOneResult, error)
根据函数名可以看出可以选择插入一个和多个,opt是options.InsertOneOptions结构体指针,里面有可选择的参数
向mongo中插入一个任务日志:
// 定义数据结构体
record := &logRecord{
JobName: "job10",
Command: "ceho hello",
Err: "",
Content: "hello",
TimePoint: TimePoint{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
}
// 插入数据
insertOneResult, err := collection.InsertOne(context.TODO(), record)
if err != nil {
fmt.Println(err)
return
}
// 默认生成一个全局唯一的id,12字节的二进制
docID := insertOneResult.InsertedID.(primitive.ObjectID)
fmt.Println("自增ID:", docID.Hex())
注意insertOneResult只有InsertID这一个属性,是一个12字节的二进制,如果想要变成易读数据格式需要进行类型断言,并且使用.Hex()方法来转换
插入多条数据也是一样的操作,需要先声明一个插入数据的切片
// 批量插入多条数据
logArr := []interface{}{record, record, record}
insertManyResult, err := collection.InsertMany(context.TODO(), logArr)
if err != nil {
fmt.Println(err)
return
}
for _, id := range insertManyResult.InsertedIDs {
docID := id.(primitive.ObjectID)
fmt.Println("自增ID:", docID.Hex())
}
2. 从mongoDB中读取数据
首先来看一下查找数据的函数签名,有很多,大抵就是查找和更新删除等操作的组合,其参数列表里有一个 filter过滤器,这个需要自己定义过滤器,来指明我们需要针对什么条件来查询,opts仍然是可选参数,findopt就有很多,分页limit排序等等,如果需要的话可以看看各个参数的释义。
func (coll *Collection) Find(ctx context.Context, filter interface{}, opts ...*options.FindOptions) (*Cursor, error)
func (coll *Collection) FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *SingleResult
func (coll *Collection) FindOneAndDelete(ctx context.Context, filter interface{}, ...) *SingleResult
func (coll *Collection) FindOneAndReplace(ctx context.Context, filter interface{}, replacement interface{}, ...) *SingleResult
func (coll *Collection) FindOneAndUpdate(ctx context.Context, filter interface{}, update interface{}, ...) *SingleResult
首先定义一个简单的过滤器:
// jobname过滤条件
type FindByJobName struct {
JobName string `bson:"jon_name"`
}
// 按照jobname过滤,找出jobname=job10
cond := &FindByJobName{
JobName: "job10",
}
执行find,返回的是一个*mongo.Cursor类型的值,就是一个游标,类似于mysql中的游标,之后我们可以使用这个游标遍历结果集
skip := int64(0)
limit := int64(2)
// 查询
//cursor, err := collection.Find(context.TODO(), bson.D{{"jon_name", "job10"}}) // 这是官方案例中的写法,显得不像在写客户端
cursor, err := collection.Find(context.TODO(), cond, &options.FindOptions{
Skip: &skip,
Limit: &limit,
})
defer cursor.Close(context.TODO())
if err != nil {
fmt.Println(err)
return
}
遍历结果集,得到查询到的数据
// 遍历结果集
for cursor.Next(context.TODO()) {
record := &logRecord{}
// 反序列化bson到结构体对象
err := cursor.Decode(record)
if err != nil {
fmt.Println(err)
return
}
// 打印结构体
fmt.Println(*record)
}
3. 删除mongoDB中的数据
首先定义一个删除规则:我们要删除创建时间小于当前时间的日志,等同于清空数据库了。
先来看看删除的函数签名,其实和find是类似的,需要一个过滤器
func (coll *Collection) DeleteMany(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*DeleteResult, error)
func (coll *Collection) DeleteOne(ctx context.Context, filter interface{}, opts ...*options.DeleteOptions) (*DeleteResult, error)
根据删除规则我们定义一个过滤器,我们要删除的是创建时间小于当前时间的,那么在mongo ctl中就应该写成json表达式
delete({"timePoint.startPoint":{"$lt":当前时间}})
我们可以利用go的bson反射来做到这个表达式的定义,记住反射是怎么序列化的就行,后面的bson标签是key
// startTime小于某时间
// {"$lt":timestamp}
type TimeBeforeCond struct {
Before int64 `bson:"$lt"`
}
// 定义删除条件
// {"timePoint.startPoint":{"$lt":timestamp}}
type DelCond struct {
beforeCond TimeBeforeCond `bson:"timePoint.startTime"`
}
现在让我们执行deletemany操作,返回的*mongo.DeleteResult只有一个属性就是被删除了多少行
// 定义删除条件
delCond := &DelCond{
beforeCond: TimeBeforeCond{
Before: time.Now().Unix(),
},
}
deleteResult, err := collection.DeleteMany(context.TODO(), delCond)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("删除了多少行:", deleteResult.DeletedCount)
以上只是一些go-mongo客户端的简单操作,如果有更复杂的需求建议去阅读官方文档,里面有每个方法的详细释义及example