上一节简单介绍了MongoDB,以及MongoDB在shell命令中有一个基本的了解。
那么本篇内容将讲述在Go中如何使用MongoDB。
作者:lomtom
个人网站:lomtom.cn
个人公众号:博思奥园
你的支持就是我最大的动力。
MongoDB系列:
- MongoDB(一)初识MongoDB
- MongoDB(二)在Go中使用MongoDB原来这么简单
- 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
得几种格式。
1 2 3 4 5 6 7
| type D = primitive.D
type E = primitive.E
type M = primitive.M
type A = primitive.A
|
第一种:bson.D
:D(Document)格式代表了一个BSON文档。也是使用比较多的一种格式。
1 2 3 4 5
| bson.D{ {"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}, }
|
每一对键值都需要用大括号括起来使用,括号内逗号前为key,逗号后为value。
第二种:bson.E
:E(Element)格式代表了一个BSON文档的一个元素,通常在D内进行使用。
1 2 3 4 5 6
| type D []E
type E struct { Key string Value interface{} }
|
点进D的源码进行查看,一个D
就是由多个E
构成。
所以之前的可以表示为:
1 2 3 4 5
| 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)。
1 2 3 4 5
| bson.M{ "foo": "bar", "hello": "world", "pi": 3.14159, }
|
M格式与D格式区别:
- D是有序的,M是无序的。
- D的没对键值需要用大括号括起来,M不需要。
- D的键值对用
,
隔开,M使用:
进行分隔
第四种:bson.A
:A(Array)格式代表了一个BSON数组。
1 2 3 4 5 6
| bson.A{ "bar", "world", 3.14159, bson.D{{"qux", 12345}}, }
|
MongoDB驱动
安装驱动
如果需要在Go中使用MongoDB
,则需要用到连接MongoDB
的驱动,而MongoDB
刚好提供了关于Go中使用MongoDB
的驱动。
1
| go get go.mongodb.org/mongo-driver/mongo
|
在项目中导入包依赖go.mongodb.org/mongo-driver
。
建立连接
- 简单连接
最简单的建立连接就是
1 2 3 4 5
| client, err := mongo.Connect(ctx, options.Client(). ApplyURI("mongodb://ip:port"))
|
就可以获取MongoDB
的连接,将其中的ip
和port
换成你自己的地址和端口即可。
- 连接时验证身份
如果你需要验证身份(废话),可以使用options.Credential
来设置验证参数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| client, err := mongo.Connect(ctx, options.Client(). ApplyURI("mongodb://ip:port"). SetAuth( options.Credential{ Username: "root", Password: "123456", }))
|
当然,也可以在URI
直接拼接验证参数,例如:
1 2 3 4 5
| client, err := mongo.Connect(ctx, options.Client(). ApplyURI("mongodb://root:123456@ip:port"))
|
- 设置连接池
当然,我们也可以设置连接池的大小,即最大连接数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| client, err := mongo.Connect(ctx, options.Client(). ApplyURI("mongodb://ip:port"). SetAuth( options.Credential{ Username: "root", Password: "123456", }). SetMaxPoolSize(20))
|
全局调用
能建立连接远远是不够的,我们又不可能每次使用都进行一次初始化,所以可以选择将连接当做一个初始化方法,在项目启动时初始化连接,使用的时候调用即可。
首先,建立全局变量,可以根据包访问权限对连接进行控制。
1
| var client *mongo.Client
|
使用init
方法,对client
进行初始化。
在初始化后,可以测试连接是否正常。
1 2 3 4 5 6
| err = conn.Ping(ctx, readpref.Primary()) if err != nil { log.Println(err) return }
|
最终,init
方法是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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中使用非常简单。
在这之前,我们需要明白怎么选择数据库与集合的:
1 2
| collection := client.Database("test").Collection("demo")
|
操作与shell不同的是需要将上下文(即context.Contxet
)作为参数传入,当然这是Go
后来规定的规则。
插入操作
插入单条
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 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()) 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
代替
1 2 3 4 5 6 7 8 9
| bson.D{ {"_id", "1"}, {"name","lomtom"}, }
bson.M{ "_id": "1", "name":"lomtom", }
|
插入多条
1 2 3 4 5 6 7 8 9 10
| insertMany, err := collection.InsertMany(context.Background(), []interface{}{ Demo{ Id: "2", Name: "lomtom", }, Demo{ Id: "3", Name: "lomtom", }, }
|
在这里需要注意[]intrface{}
的用法,区别:任意类型的切片/切片内可以存放任意值,当然也可以使用bson.A
来代替:
1 2 3 4 5 6 7 8 9 10
| bson.A{ Demo{ Id: "2", Name: "lomtom", }, Demo{ Id: "3", Name: "lomtom", }, }
|
更新操作
更新单条
1 2 3 4 5 6 7
| update, err := collection.UpdateOne(context.Background(), bson.D{ {"name", "lomtom"}, }, bson.D{ {"$set", bson.D{ {"name", "lomtom1"}, }}, })
|
上一节有说过,updateOne
、updateMany
与replaceOne
的区别,就是需不需要使用 更新运算符的区别。
所以说如果没有加$set
,而被你写成了这样:
1 2 3 4 5
| update, err := collection.UpdateOne(context.Background(), bson.D{ {"name", "lomtom"}, }, bson.D{ {"name", "lomtom1"}, })
|
那么,就会提示:
1
| update document must contain key beginning with '$'
|
意思就是让你必须使用更新运算符。
当然,上一节还提过使用更新操作来达到插入的操作,即设置参数options.Update().SetUpsert(true)
。
1 2 3 4 5 6 7
| update, err := collection.UpdateOne(context.Background(), bson.D{ {"name", "lomtom"}, }, bson.D{ {"$set", bson.D{ {"name", "lomtom1"}, }}, },options.Update().SetUpsert(true))
|
更新多条
1 2 3 4 5 6 7
| update, err := collection.UpdateMany(context.Background(), bson.D{ {"name", "lomtom"}, }, bson.D{ {"$set", bson.D{ {"name", "lomtom1"}, }}, })
|
替换单条
1 2 3 4 5
| update, err := collection.ReplaceOne(context.Background(), bson.D{ {"name", "lomtom"}, }, bson.D{ {"name", "lomtom1"}, })
|
删除操作
删除单条
1 2 3 4
| update, err := collection.DeleteOne(context.Background(), bson.D{ {"name", bson.D{ {"$in", []string{"lomtom", "lomtom1"}}, }}})
|
删除多条
1 2 3 4
| delete, err := collection.DeleteMany(context.Background(), bson.D{ {"name", bson.D{ {"$in", []string{"lomtom", "lomtom1"}}, }}})
|
查询操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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命令基本都大同小异,只需要注意一些细节即可。