MongoDB(二)在Go中使用MongoDB原来这么简单

MongoDB(二)在Go中使用MongoDB原来这么简单

上一节简单介绍了MongoDB,以及MongoDB在shell命令中有一个基本的了解。

那么本篇内容将讲述在Go中如何使用MongoDB。

作者:lomtom

个人网站:lomtom.cn

个人公众号:博思奥园

你的支持就是我最大的动力。

MongoDB系列:

  1. MongoDB(一)初识MongoDB
  2. MongoDB(二)在Go中使用MongoDB原来这么简单
  3. 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二进制数据
  • 正则表达式

Gomongo-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格式区别:

  1. D是有序的,M是无序的。
  2. D的没对键值需要用大括号括起来,M不需要。
  3. 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. 简单连接

最简单的建立连接就是

1
2
3
4
5
// 建立连接
client, err := mongo.Connect(ctx,
options.Client().
// 连接地址
ApplyURI("mongodb://ip:port"))

就可以获取MongoDB的连接,将其中的ipport换成你自己的地址和端口即可。

  1. 连接时验证身份

如果你需要验证身份(废话),可以使用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. 设置连接池

当然,我们也可以设置连接池的大小,即最大连接数。

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
// 获取 test 数据库下 demo 集合的处理器
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())
// 获取 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.Dbson.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"},
}},
})

上一节有说过,updateOneupdateManyreplaceOne的区别,就是需不需要使用 更新运算符的区别。

所以说如果没有加$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命令基本都大同小异,只需要注意一些细节即可。

MongoDB(二)在Go中使用MongoDB原来这么简单

https://lomtom.cn/d83d54c7.html

作者

lomtom

发布于

2021-12-09

更新于

2021-12-22

许可协议