如何使用Go操作MongoDB呢,来看看这篇文章吧?

Go 如何操作 MongoDB?

简介

MongoDB 是 NoSQL 中的一种。所谓的 NoSQL, 是指 Not Only SQL,不仅仅是 SQL。

MongoDB 具有一下基本特性:

  • 面向集合存储:一个集合我们可以理解为 MySQL 中的表,集合中存储了很多文档,一个文档类似于 MySQL 中的一行数据。
  • 模式自由:MongoDB 采用无模式结构存储数据,也就是说我们不需要预先定义文档必须满足的格式。
  • 支持分片:MongoDB 支持分片,并且 MongoDB 自动解决了分片的各种问题,包括自动扩容。
  • 强一致性:MongoDB 支持复制集,数据可以自动复制到多个节点,提供高可用性和数据冗余。当主节点故障时,可以自动切换到从节点,确保系统的可靠性。

MongoDB 的基本概念:

  • 数据库(Database):MongoDB 中的数据库是文档集合的容器。一个 MongoDB 实例可以包含多个数据库,每个数据库有独立的权限控制。
  • 集合(Collection):集合是文档的容器,类似于关系型数据库中的表。集合中的文档可以有不同的结构。
  • 文档(Document):文档是 MongoDB 中的基本数据单元,使用 BSON 格式存储。每个文档是一个键值对的集合。
  • 字段(Field):字段是文档中的键值对,类似于关系型数据库中的列。

安装

go get "go.mongodb.org/mongo-driver/mongo"

BSON

在 MongoDB 中,我们使用 BSON 定义结构体,你可以将它理解为类似于 JSON 一样的序列化与反序列化协议。MongoDB 在存储的时候,就是利用 BSON 将一个结构体转化为字节流,而后存储下来的。

在 BSON 中存在 EDMA类型,我们可以按照如下的理解方式:

  • E:一个普通的键值对结构体,Value 可以是其他三个。
// E represents a BSON element for a D. It is usually used inside a D.
type E struct {
	Key   string
	Value interface{}
}
  • D:本质上是一个 E 的切片。
// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters,
// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead.
//
// Example usage:
//
//	bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
type D []E
  • M:本质上是一个 Map,key 必须是 stringvalue可以是任何值,也可以是其他三种。
// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not
// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be
// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead.
//
// Example usage:
//
//	bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
type M map[string]interface{}
  • A:切片,元素可以是其他三个。
// An A is an ordered representation of a BSON array.
//
// Example usage:
//
//	bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
type A []interface{}

初始化客户端

在初始化可以客户端时,我们一般分为三个步骤:

  • 初始化客户端,注意传入的 option 里面传入了具体的链接信息。
  • 获取一个 DataBase。
  • 在 DataBase 里面获取一个集合。
func TestMogoDB(t *testing.T) {
	// 控制初始化超时时间
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	// 首先初始化一个mongoDB客户端
	opts := options.Client().ApplyURI("mongodb://localhost:27017")
	client, err := mongo.Connect(ctx, opts)
	assert.NoError(t, err)

	// 创建一个mongoDB的数据库
	mdb := client.Database("we_book")
	// 创建一个mongoDB的集合
	col := mdb.Collection("article")

	defer func() {
		_, err = col.DeleteMany(ctx, bson.D{})
	}()
}

定义字段

type Article struct {
	Id       int64  `bson:"id,omitempty"`
	Title    string `bson:"title,omitempty"`
	Content  string `bson:"content,omitempty"`
	AuthorId int64  `bson:"author_id,omitempty"`
	Status   uint8  `bson:"status,omitempty"`
	Ctime    int64  `bson:"ctime,omitempty"`
	Utime    int64  `bson:"utime,omitempty"`
}

插入文档

插入文档与 ORM 类似,唯一的却别就是,MongoDB 没有自增主键的说法。

res, err := col.InsertOne(ctx, Article{
		Id:       1,
		Title:    "我的标题",
		Content:  "我的内容",
		AuthorId: 12,
		Status:   1,
		Ctime:    time.Now().UnixMilli(),
		Utime:    time.Now().UnixMilli(),
	})
	if err != nil {
		panic(err)
	}
	// InsertedID 返回插入的文档id,并非是article的id
	fmt.Printf("插入结果的ID:%d\n", res.InsertedID)
 // 插入结果的ID:[102 172 60 161 155 169 96 179 110 143 120 175]

查找文档

查找文档时候,我们可以使用 MongoDB 自己设计的查询方式,但是这种方式比较难用。我们可以用结构体自然构造生成查询条件,但要小心零值的处理。

// 查找 Id = 1 的文章
	filter := bson.D{bson.E{Key: "id", Value: 1}}
	var art Article
	err = col.FindOne(ctx, filter).Decode(&art)
	assert.NoError(t, err)
	fmt.Printf("查找结果:%+v\n", art)
	//查找结果:{Id:1 Title:我的标题 Content:我的内容 AuthorId:12 Status:1 Ctime:1722564149250 Utime:1722564149250}

使用结构体进行查询:

	art = Article{}
	err = col.FindOne(ctx, Article{Id: 1}).Decode(&art)
	if err == mongo.ErrNoDocuments {
		fmt.Println("没有数据")
	}
	//assert.NoError(t, err)
	fmt.Printf("%#v \n", art)

更新文档

更新文档核心在两个点:

  • 构造更新条件,和查找中的用法一模一样
  • 构造更新字段:
    • 利用 BSON 来更新
    • 直接使用结构体本身,缺点是一样要忽略零值。
// 更新文档
	sets := bson.D{bson.E{Key: "$set", Value: bson.E{Key: "title", Value: "新的标题"}}}
	updateRes, err := col.UpdateOne(ctx, filter, sets)
	assert.NoError(t, err)
	fmt.Printf("更新后的文档数量:%d\n", updateRes.ModifiedCount)
	// 更新后的文档数量:1

	updateRes1, err := col.UpdateOne(ctx, filter, bson.D{
		bson.E{Key: "$set", Value: Article{Title: "新的标题2", AuthorId: 123456}}})
	assert.NoError(t, err)
	fmt.Printf("更新后的文档数量:%d\n", updateRes1.ModifiedCount)
	// 更新后的文档数量:1

删除文档

删除文档的核心在于构造删除条件,这一点和查询条件也是一样。

// 删除文档
	filter = bson.D{bson.E{Key: "id", Value: 1}}
	delRes, err := col.DeleteOne(ctx, filter)
	assert.NoError(t, err)
	fmt.Printf("删除后的文档数量:%d\n", delRes.DeletedCount)
	// 删除后的文档数量:1

复杂查询 OR

// 使用 Or 查询条件
	or := bson.A{bson.M{"id": 1}, bson.M{"id": 123}}
	orRes, err := col.Find(ctx, bson.D{bson.E{"$or", or}})
	assert.NoError(t, err)
	var arts []Article
	err = orRes.All(ctx, &arts)
	assert.NoError(t, err)
	fmt.Printf("使用or进行查找结果:%+v\n", arts)
	// 使用or进行查找结果:[{Id:1 Title:新的标题2 Content:我的内容 AuthorId:123456 Status:1 Ctime:1722576620900 Utime:1722576620900}]

复杂查询 AND

// 使用 And 查询条件
	and := bson.A{bson.D{bson.E{"id", 1}},
		bson.D{bson.E{"title", "新的标题2"}}}
	andRes, err := col.Find(ctx, bson.D{bson.E{"$and", and}})
	assert.NoError(t, err)
	ars := []Article{}
	err = andRes.All(ctx, &ars)
	assert.NoError(t, err)
	fmt.Printf("使用and进行查找结果:%+v\n", ars)
	// 使用and进行查找结果:[{Id:1 Title:新的标题2 Content:我的内容 AuthorId:123456 Status:1 Ctime:1722576444390 Utime:1722576444390}]

复杂查询 IN

// IN 查询
	in := bson.D{bson.E{Key: "id", Value: bson.M{"$in": []any{1, 2, 3}}}}
	inRes, err := col.Find(ctx, in)
	ars = []Article{}
	err = inRes.All(ctx, &ars)
	assert.NoError(t, err)
	fmt.Printf("使用in查找结果:%+v\n", ars)
	// 使用in查找结果:[{Id:1 Title:新的标题2 Content:我的内容 AuthorId:123456 Status:1 Ctime:1722576444390 Utime:1722576444390}]

复杂查询 特定字段查询

inRes, err = col.Find(ctx, in, options.Find().SetProjection(bson.M{
		"id":    1,
		"title": "我的标题",
	}))
	ars = []Article{}
	err = inRes.All(ctx, &ars)
	assert.NoError(t, err)
	fmt.Printf("自定义查询的查找结果:%+v\n", ars)
	// 自定义查询的查找结果:[{Id:1 Title:我的标题 Content: AuthorId:0 Status:0 Ctime:0 Utime:0}]

索引

类似于 MySQL,一般都是根据业务中最常用的那些查询条件,来决定在什么列上创建索引。

idxRes, err := col.Indexes().CreateMany(ctx, []mongo.IndexModel{
		{
			Keys:    bson.M{"id": 1},
			Options: options.Index().SetUnique(true),
		},
		{
			Keys: bson.M{"author_id": 1},
		},
	})
	assert.NoError(t, err)
	fmt.Printf("创建索引结果:%+v\n", idxRes)
	// 创建索引结果:[id_1 author_id_1]

注意

在使用时,一定会要注意对零值的判断,否则可能会出现无法查不到数据的问题。

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值