Golang 操作MongoDB

Golang 操作MongoDB

杰子学编程 295 2022-06-04

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如下:

MongoDB Atlas

链接代码如下:

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()
}

非常简单,我们先插入数据在进行数据查询。

result

可以看到,数据插入成功,并查询到数据,为了验证数据真的存储到MongoDB中我们通过客户端工具MongoDB Compass在次查看下数据;

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中,所有更改文档的方法都遵循相同的模式:‎

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)
}

我们看下执行结果:

updateById

数据更新成功。

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修改的数据重新改回原来的数据,看下执行结果。

updateOne

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)
	}
}

查看结果:

updateMany

这里涉及根据手号模糊搜索的一个小知识点,我们放到后面讲查询的时候重点说明下。

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)
}

我们看下执行结果:

ReplaceOne

这里我们通过客户端查看,因为我们替换的对象使用的DemoUser,所以还会存在phone、age,但是它们的值已经成默认值了。而Update的时候是不会改变其他值的。这就是替换和更新的区别

GoLang删除数据

使用Golang删除MongoDB数据时,可以通过两个方法进行删除DeleteOneDeleteMany两个方法。

  • 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)
	}
}

查看结果:

Like

分页查询

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)
}

查询结果:

Page

这里我们设置分页查询参数,SetLimit、SetSkip,并且根据手机号进行升序查询。

我们讲模拟分页,进行三次查询操作。

好了,简单的增删改查就先到这里,后续我们继续学习MongoDB的索引和全文检索。


# Golang # MongoDB # 源码 # 分页 # 模糊查询