Golang 操作MongoDB
本章介绍如何创建使用 Go 驱动程序连接到 MongoDB Atlas 集群的应用程序,Go 驱动程序允许您从 Go 应用程序连接到 MongoDB 集群并与之通信。
MongoDB Atlas是一个完全托管的云数据库服务,可以在MongoDB集群上托管您的数据。如何申请免费份的MongoDB Atlas,请参考:如何MongoDB云服务免费开通;
链接MongoDB数据库
我们登录MongoDB Atlas,获取MongoDB的链接串。勾选 Include full driver code example 可以看到链接MongoDB的demo如下:
链接代码如下:
import "go.mongodb.org/mongo-driver/mongo"
serverAPIOptions := options.ServerAPI(options.ServerAPIVersion1)
clientOptions := options.Client().
ApplyURI("mongodb+srv://julywhj:<password>@cluster0.r1o1v.mongodb.net/?retryWrites=true&w=majority").
SetServerAPIOptions(serverAPIOptions)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
注:链接串mongodb+srv://julywhj:
@cluster0.r1o1v.mongodb.net/?retryWrites=true&w=majority 的
需要替换成我们设置的密码,密码修改后,可以通过main函数测试链接。如果没有出现错误说明链接成功。
CRUD操作
MongoDB添加数据
可以使用InsertOne
将文档插入到集合中.
注:不存在的数据库和集合
当您对它们执行写入操作时,服务器会隐式创建必要的数据库和集合(如果它们尚不存在)。
查看Demo:我们将张三这个文档插入到数据库中。
// InstallOne 插入一行数据
func InstallOne() {
collection := clientTest.Database(Database).Collection(Collection)
one, err := collection.InsertOne(context.TODO(), DemoUser{
Id: time.Now().UnixMicro(),
Name: "张三",
Phone: "18346566786",
Age: 28,
})
if err != nil {
panic(err)
}
fmt.Println(one)
}
这里我们定义了一个结构体DemoUser;通过创建一个struct对象将张三插入到数据库中。
// DemoUser 测试用户struct
type DemoUser struct {
Id int64 `bson:"_id"`
Name string `bson:"name"`
Phone string `bson:"phone"`
Age int64 `bson:"age"`
}
为了方便查看数据,我们先写个查询方法,并将查询数据进行格式化输出;
- 查看数据库中全部数据
// GetAll 查询全部数据
func GetAll() {
collection := clientTest.Database(Database).Collection(Collection)
var result []DemoUser
cursor, _ := collection.Find(context.TODO(), bson.D{})
if cursor.All(context.TODO(), &result) != nil {
fmt.Println("数据查询错误")
}
for i, user := range result {
UserFmt(i+1, user)
}
}
- 数据展示格式化方法
UserFmt
// UserFmt 格式化输出
func UserFmt(index int, user DemoUser) {
fmt.Printf("%d --姓名: %s -- 手机号: %s --- 年龄: %d--\n", index, user.Name, user.Phone, user.Age)
}
好,我们执行下插入数据和查询数据,编写main
方法:
func main() {
InstallOne()
GetAll()
}
非常简单,我们先插入数据在进行数据查询。
可以看到,数据插入成功,并查询到数据,为了验证数据真的存储到MongoDB中我们通过客户端工具MongoDB Compass
在次查看下数据;
可以看到我们通过GoLang插入数据到MongoDB成功。
上面是插入一条数据,接下来我们看下如何批量插入数据。
插入一个文档通过InsertOne
,批量插入文档我们使用InsertMany
方法。
// InstallMany 批量插入
func InstallMany() {
collection := clientTest.Database(Database).Collection(Collection)
users := []interface{}{
DemoUser{Id: 1, Name: "汪鸣晨", Phone: "13659630001", Age: 28},
DemoUser{Id: 2, Name: "盖流惠", Phone: "13659630004", Age: 28},
DemoUser{Id: 3, Name: "桓野云", Phone: "13659630004", Age: 28},
DemoUser{Id: 4, Name: "郝语彤", Phone: "18345654901", Age: 28},
DemoUser{Id: 5, Name: "沈振文", Phone: "18345654902", Age: 28},
DemoUser{Id: 6, Name: "古雪翎", Phone: "18345654903", Age: 28},
DemoUser{Id: 7, Name: "燕丁辰", Phone: "18345654904", Age: 28},
DemoUser{Id: 8, Name: "乔任真", Phone: "18345654905", Age: 28},
DemoUser{Id: 9, Name: "暴笑妍", Phone: "18345654906", Age: 28},
DemoUser{Id: 10, Name: "池雁风", Phone: "18345654907", Age: 28},
DemoUser{Id: 11, Name: "马玉萍", Phone: "18345654908", Age: 28},
DemoUser{Id: 12, Name: "崔子舒", Phone: "18345654909", Age: 28},
DemoUser{Id: 13, Name: "简晗晗", Phone: "18345654910", Age: 28},
DemoUser{Id: 14, Name: "邹许洌", Phone: "18345654911", Age: 28},
DemoUser{Id: 15, Name: "梁晶茹", Phone: "18345654912", Age: 28},
DemoUser{Id: 16, Name: "刘语林", Phone: "18345654913", Age: 28},
DemoUser{Id: 17, Name: "曹可可", Phone: "18345654914", Age: 28},
}
many, _ := collection.InsertMany(
context.TODO(),
users,
)
fmt.Println(many)
}
这里造了一批假的用户数据,我们通过InsertMany
将数据批量插入到MongoDB中。
func main() {
InstallMany()
GetAll()
}
先插入在查询,我们看下结果集:
可以看到我们插入的数据已经查询出来了,数据的插入就先到这里,我们看下数据的更新操作。
注:链接参数如下
// Database 测试数据库 const Database = "jim" // Collection 测试Collection const Collection = "demo_user"
Golang更新MongoDB数据
MongoDB中的所有写入操作在单个文档的级别上都是原子的。
MongoDB更新数据有两种操作方法
- updata: 更新操作会更改您指定的字段,同时保持其他字段和值不变
- replace: 替换操作将删除除文档中以外的所有现有字段,并将已删除的字段替换为您指定的新字段和值
MongoDB 集合中的每个文档都有一个唯一且不可变的字段_id。不能使用更新和替换操作来更改字段。如果尝试更改此字段,则 update and replace 方法将返回 a WriteError错误
在MongoDB中,所有更改文档的方法都遵循相同的模式:
- 指定查询筛选器以匹配一个或多个要更改的文档。
- 指定字段和值更改。
- 如果需要,请指定用于修改行为的选项。
Update
Update更新操作,主要涉及的方法有以下方法:
UpdateByID
:根据ID更新数据。UpdateOne
:根据指定筛选器匹配的数据,更新其中一条。UpdateMany
:根据指定筛选器匹配到的数据,全部更新。
UpdateByID:我们根据ID进行更新,根据ID进行更新需要注意一点,这里传的应该是ID,而不是filter.
UpdateByID(context.TODO(), id, bson.D{{“$set”, bson.D{{“name”, “汪鸣晨(改)”}}}})
func UpdateById() {
collection := clientTest.Database(Database).Collection(Collection)
id, _ := primitive.ObjectIDFromHex("629b4510c19424e0b1551723")
filter := bson.D{{"_id", id}}
user := DemoUser{}
collection.FindOne(context.TODO(), filter).Decode(&user)
fmt.Print("更新前:")
UserFmt(1, user)
updateRes, err := collection.UpdateByID(context.TODO(), id, bson.D{{"$set", bson.D{{"name", "汪鸣晨(改)"}}}})
if err != nil {
fmt.Println(err)
}
fmt.Println(updateRes)
collection.FindOne(context.TODO(), filter).Decode(&user)
fmt.Print("更新后:")
UserFmt(1, user)
}
我们看下执行结果:
数据更新成功。
UpdateOne:根据filter条件进行数据更新,和UpdateByID不用的是,这里筛选过滤器是可以随机既定字段的,而不是传ID的值。
// UpdateOne 根据手机号更新数据
func UpdateOne() {
collection := clientTest.Database(Database).Collection(Collection)
filter := bson.D{{"phone", "13659630001"}}
user := DemoUser{}
collection.FindOne(context.TODO(), filter).Decode(&user)
fmt.Print("更新前:")
UserFmt(1, user)
updateRes, err := collection.UpdateOne(context.TODO(), filter, bson.D{{"$set", bson.D{{"name", "汪鸣晨"}}}})
if err != nil {
fmt.Println(err)
}
fmt.Println(updateRes)
collection.FindOne(context.TODO(), filter).Decode(&user)
fmt.Print("更新后:")
UserFmt(1, user)
}
我们这里根据用户手机号进行更新数据,我们将上面根据ID修改的数据重新改回原来的数据,看下执行结果。
UpdateMany:更新筛选器匹配到的全部数据。
我们把手机号是1834565490开头的人的年龄全部更新成30岁。
func UpdateMany() {
collection := clientTest.Database(Database).Collection(Collection)
filter := bson.D{{"phone", bson.D{{"$regex", "^1834565490"}}}}
var users []DemoUser
find, err := collection.Find(context.TODO(), filter)
find.All(context.TODO(), &users)
fmt.Print("更新前:")
for i, user := range users {
UserFmt(i+1, user)
}
updateRes, err := collection.UpdateMany(context.TODO(), filter, bson.D{{"$set", bson.D{{"age", 30}}}})
if err != nil {
fmt.Println(err)
}
fmt.Println(updateRes)
find, err = collection.Find(context.TODO(), filter)
find.All(context.TODO(), &users)
fmt.Print("更新后:")
for i, user := range users {
UserFmt(i+1, user)
}
}
查看结果:
这里涉及根据手号模糊搜索的一个小知识点,我们放到后面讲查询的时候重点说明下。
replace
替换操作是讲之前的数据完全替换掉,和update完全不同,使用时需要区分场景使用。
Replace只有一个方法ReplaceOne
,这里我们根据Phone进行操作。
func ReplaceOne() {
collection := clientTest.Database(Database).Collection(Collection)
filter := bson.D{{"phone", "13659630001"}}
one, err := collection.ReplaceOne(context.TODO(), filter, DemoUser{Name: "ReplaceOne"})
if err != nil {
fmt.Println(err)
}
fmt.Println(one)
}
我们看下执行结果:
这里我们通过客户端查看,因为我们替换的对象使用的DemoUser,所以还会存在phone、age,但是它们的值已经成默认值了。而Update的时候是不会改变其他值的。这就是替换和更新的区别。
GoLang删除数据
使用Golang删除MongoDB数据时,可以通过两个方法进行删除DeleteOne
和DeleteMany
两个方法。
- DeleteOne:删除筛选器中第一个数据。
- DeleteMany:删除筛选器全部的数据。
删除操作比较简单,我们只实现其中一个即可。
// DeleteOne 根据手机号删除数据
func DeleteOne() {
collection := clientTest.Database(Database).Collection(Collection)
filter := bson.D{{"phone", "13659630004"}}
one, err := collection.DeleteOne(context.TODO(), filter)
if err != nil {
fmt.Println(err)
}
fmt.Println(one)
}
查询操作
若要匹配文档的子集,请指定包含匹配条件的查询筛选器。匹配条件由要在文档中显示的字段和值组成。查询筛选器至少包含一组匹配条件,以确定要在结果集中包含哪些文档。
在查询筛选器中,可以将字段与文本值或查询运算符进行匹配。查询运算符允许您执行数学或逻辑运算以在集合中查找文档。
-
将条件与文本值匹配使用下面格式:
filter := bson.D{{"<field>", "<value>"}}
-
将条件与运算符匹配使用下面格式:
filter := bson.D{{"<field>", bson.D{{"<operator>", "<value>"}}}}
查询操作涉及内容比较多,这里我们主要讲几种常用的查询操作:等值查询、模糊查询、in查询、OR查询、分页查询几种。
等值查询(eq)
等值查询在MongoDB中非常简单,我们在上面更新和删除操作中经常会用到,我们看个简单的例子:
filter := bson.D{{"phone", "13659630004"}}
简单的根据手机号查询;
模糊查询(Like)
Golang在MongoDB中模糊查询,需要采用正则表达式的方式进行查询,我们看个例子。
func Like() {
collection := clientTest.Database(Database).Collection(Collection)
filter := bson.D{{"phone", bson.D{{"$regex", "^1834565490"}}}}
var users []DemoUser
find, _ := collection.Find(context.TODO(), filter)
find.All(context.TODO(), &users)
fmt.Print("更新前:")
for i, user := range users {
UserFmt(i+1, user)
}
}
这里使用了运算符匹配方式,运算符$regex
,正则表达式规则^1834565490
匹配1834565490开头的手机号。
这里我们在实现一个根据用户名模糊查询的例子:
func Like() {
collection := clientTest.Database(Database).Collection(Collection)
var name = "雪"
filter := bson.D{{"name", bson.D{{"$regex", "^.*" + name + ".*$"}}}}
var users []DemoUser
find, _ := collection.Find(context.TODO(), filter)
find.All(context.TODO(), &users)
for i, user := range users {
UserFmt(i+1, user)
}
}
查看结果:
分页查询
func Page(limit, page int64) {
collection := clientTest.Database(Database).Collection(Collection)
var findOptions = &options.FindOptions{}
findOptions.SetLimit(limit)
findOptions.SetSkip(limit * page)
findOptions.SetSort(bson.D{{"phone", 1}})
cur, err := collection.Find(context.Background(), bson.D{}, findOptions)
if err != nil {
fmt.Println(err)
}
var users []DemoUser
cur.All(context.TODO(), &users)
for i, user := range users {
UserFmt(i+1, user)
}
}
func main() {
Page(3, 1)
Page(3, 2)
Page(3, 3)
}
查询结果:
这里我们设置分页查询参数,SetLimit、SetSkip,并且根据手机号进行升序查询。
我们讲模拟分页,进行三次查询操作。
好了,简单的增删改查就先到这里,后续我们继续学习MongoDB的索引和全文检索。