Go范文MongoDB
- 连接数据库
获取驱动,命令行输入:
go get go.mongodb.org/mongo-driver/mongo
通过ApplyURI()方法连接数据库:
import (
"context"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"log"
)
var mgoCli *mongo.Client
func initDb() {
var err error
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
//连接MongoDB
mgoCli, err = mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
//检查连接
err = mgoCli.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
}
func MgoCli() *mongo.Client {
if mgoCli == nil {
initDb()
}
return mgoCli
}
在连接数据库后,调用MgoCli()函数获取MongoDB客户端实例,用Database()方法指定数据库,用Collection()方法指定数据集合:
func main() {
var (
client = MgoCli()
db *mongo.Database
collection *mongo.Collection
)
//选择数据库
db = client.Database("test")
//选择表
collection = db.Collection("book")
fmt.Println(collection)
}
- 插入一条数据
首先编写模型文件,构建结构体ExecTime、LogRecord:
type ExecTime struct {
StartTime int64 `bson:"startTime"` //开始时间
EndTime int64 `bson:"endTime"` //结束时间
}
type LogRecord struct {
JobName string `bson:"JobName"`//任务名
Command string `bson:"command"`//shell命令
Err string `bson:"err"`//脚本错误
Content string `bson:"content"`//脚本输出
Tp ExecTime //执行时间
}
import (
"./model"
"./util"
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"time"
)
func main() {
var (
client = util.MgoCli()
err error
collection *mongo.Collection
iResult *mongo.InsertOneResult
id primitive.ObjectID
)
//选择数据库和表
collection = client.Database("test").Collection("logRecord")
//插入某一条数据
logRecord := model.LogRecord{
JobName: "job1",
Command: "echo 1",
Err: "",
Content: "1",
Tp: model.ExecTime{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
}
if iResult, err = collection.InsertOne(context.TODO(), logRecord); err != nil {
fmt.Println(err)
return
}
//_id:默认生成一个全局唯一ID
id = iResult.InsertedID.(primitive.ObjectID)
fmt.Println("自增ID", id.Hex())
}
- 批量插入数据
批量插入数据时,只需要调用InsertMany()方法:
func main() {
var (
client = util.MgoCli()
err error
collection *mongo.Collection
result *mongo.InsertManyResult
id primitive.ObjectID
)
//选择数据库和表
collection = client.Database("test").Collection("logRecord")
//批量插入
result, err = collection.InsertMany(context.TODO(), []interface{}{
model.LogRecord{
JobName: "job multi1",
Command: "echo multi1",
Err: "",
Content: "1",
Tp: model.ExecTime{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
model.LogRecord{
JobName: "job multi2",
Command: "echo multi2",
Err: "",
Content: "2",
Tp: model.ExecTime{
StartTime: time.Now().Unix(),
EndTime: time.Now().Unix() + 10,
},
},
})
if err != nil {
log.Fatal(err)
}
if result == nil {
log.Fatal("result nil")
}
for _, v := range result.InsertedIDs {
id = v.(primitive.ObjectID)
fmt.Println("自增ID:", id.Hex())
}
}
- 查询数据
首先,在model文件中添加一个查询结构体:
//查询结构体
type FindJobName struct {
JobName string `bson:"jobName"` //任务名
}
然后,通过Find()函数按照条件进行查找:
func find() {
var (
client = util.MgoCli()
err error
collection *mongo.Collection
cursor *mongo.Cursor
)
collection = client.Database("test").Collection("logRecord")
//查询条件
cond := model.FindJobName{
JobName: "job multi1",
}
if cursor, err = collection.Find(
context.TODO(),
cond,
options.Find().SetSkip(0),
options.Find().SetLimit(2)); err != nil {
fmt.Println(err)
return
}
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
//遍历邮编获取结果数据
for cursor.Next(context.TODO()) {
var lr model.LogRecord
//反序列化Bson到对象
if cursor.Decode(&lr) != nil {
fmt.Println(err)
return
}
fmt.Println(lr)
}
var results []model.LogRecord
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
}
- 用BSON进行复合查询
复合查询会使用到BSON包。MongoDB中的JSON文档存储在名为BSON(二进制编码的JSON)的二进制表示中。与其他编码将JSON数据存储为简单字符串和数字的数据库不同,BSON编码扩展了JSON表示,使其包含额外的类型,如int、long、date、decimal128等。这使得应用程序更容易可靠地处理、排序和比较数据。
在连接MongoDB的Go驱动程序中,有两大类型表示BSON数据:D类型和Raw类型。
1. D类型
D类型被用来简洁地构建使用本地Go类型的BSON对象。这对于构造传递给MongoDB的命令特别有用。D类型包括以下4个子类。
- D:一个BSON文档。这种类型应该在顺序重要的情况下使用,比如MongoDB命令。
- M:一张无序的map。它和D类似,只是它不保持顺序。
- A:一个BSON数组。
- E:D中的一个元素。
使用BSON可以更方便地用Go完成对数据库的CURD操作。要使用BSON,需要先导入下
面的包:
import "go.mongodb.org/mongo-driver/bson"
下面是一个使用D类型构建的过滤器文档的例子,它可以用来查找name字段与“Jim”或“Jack”匹配的文档:
bson.D{
"name",
bson.D{
"$in",
bson.A{"Jim", "Jack"}
}
}
2. Raw类型
Raw类型用于验证字节切片。Raw类型还可以将BSON反序列化成另一种类型。
import "fmt"
import "go.mongodb.org/mongo-driver/bson"
func main() {
//声明一个BSON类型
testM := bson.M{
"jobName": "job multi1",
}
//定义一个Raw类型
var raw bson.Raw
tmp, _ := bson.Marshal(testM)
bson.Unmarshal(tmp, &raw)
fmt.Println(testM) //map[jobName:job multi1]
fmt.Println(raw)//{"jobName": "job multi1"}
}
对于复合查询来说,D类型更加强大。下面介绍如何使用D类型进行常用的复合查询。
3. 聚合查询
如果需要对数据进行聚合查询,则要用到group()等聚合方法。示例代码如下:
import (
"./util"
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"log"
)
func main() {
var (
client = util.MgoCli()
collection *mongo.Collection
err error
cursor *mongo.Cursor
)
collection = client.Database("test").Collection("logRecord")
//按照jobName分组,统计countJob中每组的数目
groupStage := mongo.Pipeline{
bson.D{
{"$group", bson.D{
{"_id", "$JobName"},
{"countJob", bson.D{
{"$sum", 1},
}},
}},
},
}
if cursor, err = collection.Aggregate(context.TODO(), groupStage); err != nil {
log.Fatal(err)
}
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
var results []bson.M
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
//map[_id:job multi1 countJob:1]
//map[_id:job1 countJob:1]
//map[_id:job multi2 countJob:1]
}
4. 更新数据
同样的,更新数据也需要建立专门用于更新的结构体。结构体有Command、Content两个字段。
更新时需要同时对这两个字段进行赋值,否则未被赋值的字段会被更新为G0的数据类型初始值。为更新更方便些,可采用bson.M{"$set":bson.M{"command":"ByBsonM",}}
来进行更新。
创建UpdateByJobName结构体:
//更新结构体
type UpdateByJobName struct {
Command string `bson:"command"`//shell命令
Content string `bson:"content"`//脚本输出
}
根据结构体进行更新:
import (
"./util"
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"log"
)
func main() {
var (
client = util.MgoCli()
collection *mongo.Collection
err error
uResult *mongo.UpdateResult
)
collection = client.Database("test").Collection("logRecord")
filter := bson.M{"JobName": "job multi1"}
//update := bson.M{
// "$set": model.UpdateByJobName{Command: "byModel", Content: "model"},
//}
//使用bson方式
update := bson.M{
"$set": bson.M{
"command": "xxxxxxx",
"content": "yyyyyyy",
},
}
if uResult, err = collection.UpdateMany(context.TODO(), filter, update); err != nil {
log.Fatal(err)
}
fmt.Println("更新了:", uResult.ModifiedCount, "条记录")
}
使用$inc
可以对字段的值进行增减,例如:
bson.M("$inc": bson.M{"age": -1})
使用$push
给该字段增加1个元素,例如:
bson.M{"$push": bson.M{"interests": "Golang"}}
使用$pull
可以对该字段删除一个元素,例如:
bson.M{"$pull":bson.M{"interests": "Golang"}}
5. 删除数据
可以用DeleteMany()方法来删除数据,示例如下:
import (
"./util"
"context"
"fmt"
"go.mongodb.org/mongo-driver/mongo"
"log"
"time"
)
type DeleteCond struct {
BeforeCond TimeBeforeCond `bson:"tp.startTime"`
}
// startTime小于某个时间,用这种方式提前定义要进行的操作($set、$group等)
type TimeBeforeCond struct {
BeforeTime int64 `bson:"$lt"`
}
func main() {
var (
client = util.MgoCli()
collection *mongo.Collection
err error
uResult *mongo.DeleteResult
)
collection = client.Database("test").Collection("logRecord")
//使用结构体构建条件,删除jobName中名为job0的数据
delCond := &DeleteCond{
BeforeCond: TimeBeforeCond{
BeforeTime: time.Now().Unix(),
},
}
//使用BSON构建条件
//filter := bson.M{
// "tp.startTime": bson.M{
// "$lt": time.Now().Unix(),
// },
//}
if uResult, err = collection.DeleteMany(context.TODO(), delCond); err != nil {
log.Fatal(err)
}
fmt.Println(uResult.DeletedCount)
}
如果要忽略被初始化的值,则可以直接在结构体中增加omitempty属性:
type ExecTimeFilter struct {
StartTime interface{} `bson:"tp.startTime,omitempty"` //开始时间
EndTime interface{} `bson:"tp.endTime,omitempty"` //结束时间
}
type LogRecordFilter struct {
ID interface{} `bson:"_id,omitempty"`
JobName interface{} `bson:"JobName,omitempty" json:"jobName"` //任务名
Command interface{} `bson:"command,omitempty"` //shell命令
Err interface{} `bson:"err,omitempty"` //脚本错误
Content interface{} `bson:"content,omitempty"` //脚本输出
Tp interface{} `bson:"tp,omitempty"`//执行时间
}
另外,可以在结构体中添加$lt、$group、$sum
等表示逻辑关系的属性:
//小于示例
type Lt struct {
Lt int64 `bson:"$lt"`
}
//分组示例
type Group struct {
Group interface{} `bson:"$group"`
}
//求和示例
type Sum struct {
Sum interface{} `bson:"$sum"`
}
用$group
进行分组求和的示例:
import (
"./model"
"./util"
"context"
"fmt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"log"
)
func main() {
var (
client = util.MgoCli()
collection *mongo.Collection
err error
cursor *mongo.Cursor
)
collection = client.Database("test").Collection("logRecord")
groupStage := []model.Group{}
groupStage = append(groupStage, model.Group{
Group: bson.D{
{"_id", "$JobName"},
{"countJob", model.Sum{Sum: 1}},
},
})
if cursor, err = collection.Aggregate(context.TODO(), groupStage); err != nil {
log.Fatal(err)
}
defer func() {
if err = cursor.Close(context.TODO()); err != nil {
log.Fatal(err)
}
}()
var results []bson.M
if err = cursor.All(context.TODO(), &results); err != nil {
log.Fatal(err)
}
for _, result := range results {
fmt.Println(result)
}
}