MongoDB(二)在Go中使用MongoDB原来这么简单
- December 9, 2021
作者:lomtom
个人网站:lomtom.cn 🔗
个人公众号:博思奥园 🔗
你的支持就是我最大的动力。
MongoDB系列:
BSON
在使用之前,你必须得明白什么是BSON
,以及在Go
中如何使用BSON
,因为在使用MongoDB
,你基本离不开BSON
。
在第一节中,提到过BSON
,BSON
是JSON的二进制格式。
BSON支持多种类型:
- string
- integer(32或64位)
- double
- decimal128
- date
- byte array(二进制数组)
- bool
- null
- BSON对象
- BSON数组
- JavaScript代码
- MD5 🔗二进制数据
- 正则表达式 🔗
在Go
中mongo-driver/bson
包下,有一个bson.go
,里面描述了MongoDB
中关于BSON
得几种格式。
type D = primitive.D
type E = primitive.E
type M = primitive.M
type A = primitive.A
第一种:bson.D
:D(Document)格式代表了一个BSON文档。也是使用比较多的一种格式。
bson.D{
{"foo", "bar"},
{"hello", "world"},
{"pi", 3.14159},
}
每一对键值都需要用大括号括起来使用,括号内逗号前为key,逗号后为value。
第二种:bson.E
:E(Element)格式代表了一个BSON文档的一个元素,通常在D内进行使用。
type D []E
type E struct {
Key string
Value interface{}
}
点进D的源码进行查看,一个D
就是由多个E
构成。
所以之前的可以表示为:
bson.D{
bson.E{Key: "foo", Value: "bar"},
bson.E{Key: "hello", Value: "world"},
bson.E{Key: "pi", Value: 3.14159},
}
第三种:bson.M
:M(Map)格式也代表了一个BSON文档,与D不同的是,D是有序的,M是无序的(可以理解为Map)。
bson.M{
"foo": "bar",
"hello": "world",
"pi": 3.14159,
}
M格式与D格式区别:
- D是有序的,M是无序的。
- D的没对键值需要用大括号括起来,M不需要。
- D的键值对用
,
隔开,M使用:
进行分隔
第四种:bson.A
:A(Array)格式代表了一个BSON数组。
bson.A{
"bar",
"world",
3.14159,
bson.D{{"qux", 12345}},
}
MongoDB驱动
安装驱动
如果需要在Go中使用MongoDB
,则需要用到连接MongoDB
的驱动,而MongoDB
刚好提供了关于Go中使用MongoDB
的驱动 🔗。
go get go.mongodb.org/mongo-driver/mongo
在项目中导入包依赖go.mongodb.org/mongo-driver
。
建立连接
- 简单连接
最简单的建立连接就是
// 建立连接
client, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://ip:port"))
就可以获取MongoDB
的连接,将其中的ip
和port
换成你自己的地址和端口即可。
- 连接时验证身份
如果你需要验证身份(废话),可以使用options.Credential
来设置验证参数。
// 建立连接
client, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://ip:port").
// 设置验证参数
SetAuth(
options.Credential{
// 用户名
Username: "root",
// 密码
Password: "123456",
}))
当然,也可以在URI
直接拼接验证参数,例如:
// 建立连接
client, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://root:123456@ip:port"))
- 设置连接池
当然,我们也可以设置连接池的大小,即最大连接数。
// 建立连接
client, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://ip:port").
// 验证参数
SetAuth(
options.Credential{
// 用户名
Username: "root",
// 密码
Password: "123456",
}).
// 设置连接数
SetMaxPoolSize(20))
全局调用
能建立连接远远是不够的,我们又不可能每次使用都进行一次初始化,所以可以选择将连接当做一个初始化方法,在项目启动时初始化连接,使用的时候调用即可。
首先,建立全局变量,可以根据包访问权限对连接进行控制。
var client *mongo.Client
使用init
方法,对client
进行初始化。
在初始化后,可以测试连接是否正常。
// 测试连接
err = conn.Ping(ctx, readpref.Primary())
if err != nil {
log.Println(err)
return
}
最终,init
方法是这样的。
func init() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 建立连接
conn, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://ip:port").
// 设置验证参数
SetAuth(
options.Credential{
// 用户名
Username: "root",
// 密码
Password: "123456",
}).
// 设置连接数
SetMaxPoolSize(20))
if err != nil {
log.Println(err)
return
}
// 测试连接
err = conn.Ping(ctx, readpref.Primary())
if err != nil {
log.Println(err)
return
}
log.Println("connect success!!!")
client = conn
}
接下来,就可以愉快在Go中玩耍啦。
当然,你也可以参考 Go(三)Go配置文件 采用配置文件的方式,进行MongoDB
的连接参数的配置。
Go中的CURD
上一节已经介绍MongoDB
关于CURD的一些细节,那么,本节将着重于代码层面。
MongoDB
驱动提供的方式与shell命令基本保持一致,所以在Go中使用非常简单。
在这之前,我们需要明白怎么选择数据库与集合的:
// 获取 test 数据库下 demo 集合的处理器
collection := client.Database("test").Collection("demo")
操作与shell不同的是需要将上下文(即context.Contxet
)作为参数传入,当然这是Go
后来规定的规则。
插入操作
插入单条
type Demo struct {
Id string `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
}
func TestInsertOne(t *testing.T) {
// 延迟关闭连接
defer func(client *mongo.Client, ctx context.Context) {
err := client.Disconnect(ctx)
if err != nil {
log.Println(err)
}
}(client, context.Background())
// 获取 test 数据库下 demo 集合的处理器
collection := client.Database("test").Collection("demo")
// 插入单条
one, err := collection.InsertOne(context.Background(), &Demo{
Id: "1",
Name: "lomtom",
})
if err != nil {
return
}
log.Println(one.InsertedID)
}
第一次,先贴上完整的函数,后续只需要将collection
操作进行更换即可。
其中的&Demo可以用bson.D
或bson.M
代替
bson.D{
{"_id", "1"},
{"name","lomtom"},
}
bson.M{
"_id": "1",
"name":"lomtom",
}
插入多条
insertMany, err := collection.InsertMany(context.Background(), []interface{}{
Demo{
Id: "2",
Name: "lomtom",
},
Demo{
Id: "3",
Name: "lomtom",
},
}
在这里需要注意[]intrface{}
的用法,区别:任意类型的切片/切片内可以存放任意值,当然也可以使用bson.A
来代替:
bson.A{
Demo{
Id: "2",
Name: "lomtom",
},
Demo{
Id: "3",
Name: "lomtom",
},
}
更新操作
更新单条
update, err := collection.UpdateOne(context.Background(), bson.D{
{"name", "lomtom"},
}, bson.D{
{"$set", bson.D{
{"name", "lomtom1"},
}},
})
上一节有说过,updateOne
、updateMany
与replaceOne
的区别,就是需不需要使用 更新运算符 🔗的区别。
所以说如果没有加$set
,而被你写成了这样:
update, err := collection.UpdateOne(context.Background(), bson.D{
{"name", "lomtom"},
}, bson.D{
{"name", "lomtom1"},
})
那么,就会提示:
update document must contain key beginning with '$'
意思就是让你必须使用更新运算符。
当然,上一节还提过使用更新操作来达到插入的操作,即设置参数options.Update().SetUpsert(true)
。
update, err := collection.UpdateOne(context.Background(), bson.D{
{"name", "lomtom"},
}, bson.D{
{"$set", bson.D{
{"name", "lomtom1"},
}},
},options.Update().SetUpsert(true))
更新多条
update, err := collection.UpdateMany(context.Background(), bson.D{
{"name", "lomtom"},
}, bson.D{
{"$set", bson.D{
{"name", "lomtom1"},
}},
})
替换单条
update, err := collection.ReplaceOne(context.Background(), bson.D{
{"name", "lomtom"},
}, bson.D{
{"name", "lomtom1"},
})
删除操作
删除单条
update, err := collection.DeleteOne(context.Background(), bson.D{
{"name", bson.D{
{"$in", []string{"lomtom", "lomtom1"}},
}}})
删除多条
delete, err := collection.DeleteMany(context.Background(), bson.D{
{"name", bson.D{
{"$in", []string{"lomtom", "lomtom1"}},
}}})
查询操作
// 建立查询 (无查询条件)
find, err := collection.Find(context.Background(), bson.D{})
if err != nil {
log.Println(err)
return
}
// 遍历查询结果
for find.Next(context.Background()) {
var demo Demo
// 解码绑定数据
err = find.Decode(&demo)
if err != nil {
log.Println(err)
return
}
log.Printf("%v", demo)
}
遍历查询操作是,有Next
方法判断当前文档后是否有下一个文档,Decode
方法是对当前文档进行解码与绑定操作。
其他的操作与shell命令基本都大同小异,只需要注意一些细节即可。