​​Go语言操作MongoDB:从入门到实战CRUD​

151 阅读3分钟

​一、前言​

在上一篇文章《为什么选择MongoDB?—— 一篇写给开发者的数据库选型指南​》中,我们从​​场景适配性​​、​​数据结构灵活性​​和​​性能优化​​等维度,深入对比了 MongoDB 与 MySQL 的核心差异。文章指出,MongoDB 凭借其​​动态模式​​(Schema-less)、​​水平扩展能力​​和对​​非结构化数据​​的天然支持,成为现代高并发、快速迭代业务的理想选择。

本文作为该指南的 ​​实战篇​​,将聚焦于 ​​Go 语言与 MongoDB 的深度集成​​。通过手把手实现 CRUD(增删改查)操作、事务管理、索引优化等核心功能,帮助开发者快速构建高性能的 MongoDB 应用。

二、环境准备

1. ​​安装MongoDB​

****因为docker的发展,以及优势(后续会出相关文章介绍),我推荐使用docker安装各种数据库,MySQL、MongoDB、Redis等。​使用 Docker 部署 MongoDB​

# docker-compose.yaml
version: '3.8'

services:
  mongo:
    image: mongo:6.0               # 使用官方镜像
    restart: always                # 自动重启
    environment:
      MONGO_INITDB_ROOT_USERNAME: root    # 初始化管理员账号
      MONGO_INITDB_ROOT_PASSWORD: example
    ports:
      - "27017:27017"              # 暴露默认端口
    volumes:
      - mongo_data:/data/db        # 持久化数据存储

volumes:
  mongo_data:

2. ​​Go语言驱动​

找了下相关的MongoDB没有发现较好的第三方包(可能我没看到qaq),所以本文介绍的是使用官方MongoDB Go驱动:

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

三、连接MongoDB 

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
monitor := &event.CommandMonitor{
	//每个命令执行之前
	Started: func(ctx context.Context, cse *event.CommandStartedEvent) {
		fmt.Println(cse.Command)
	},
	//执行成功
	Succeeded: func(ctx context.Context, cse *event.CommandSucceededEvent) {

	},
	//执行失败
	Failed: func(ctx context.Context, cfe *event.CommandFailedEvent) {},
}
opts :=options.Client().ApplyURI("mongodb://root:example@localhost:27017").SetMonitor(monitor)
client, err := mongo.Connect(ctx, opts)
//建库
mdb := client.Database("test")
//建表
col := mdb.Collection("articles")

四、CRUD操作实战​

1. ​​插入文档(Create)​

	res, err := col.InsertOne(ctx, Article{
		Id:      123,
		Title:   "我的标题",
		Content: "我的内容",
	})
	//mongodb 文档id,非业务id
	fmt.Println(res.InsertedID)

2. ​​查询文档(Read)​

  • 查询单个文档:
filter := bson.D{bson.E{Key: "id", Value: 123}}
var art Article
err = col.FindOne(ctx, filter).Decode(&art)
fmt.Println(art)
art = Article{}
err = col.FindOne(ctx, Article{Id: 123}).Decode(&art)
if err == mongo.ErrNoDocuments {
	//mongoDB 查询结果为空的条件
}
  • 查询多个文档:
res, err := m.liveCol.Find(ctx, bson.M{"id": id})
	if err != nil {
		return PublishArticle{}, err
	}
	defer res.Close(ctx)
	if err = res.Decode(&article); err != nil {
		return PublishArticle{}, err
	}

3.更新文档(UPDATE)

sets := bson.D{{Key: "$set", Value: bson.D{{Key: "title", Value: "测试D"}}}}
	//使用的ID是InsertedID
	//col.UpdateByID()
	rsp, err := col.UpdateMany(ctx, filter, sets)
	assert.NoError(t, err)
    //影响的数据
	fmt.Println("affected:", rsp.ModifiedCount)

4. ​​删除文档(Delete)​

	del, err := col.DeleteMany(ctx, filter)
	assert.NoError(t, err)
	fmt.Println("affected:", del.DeletedCount)

5.完整代码(demo)

package mongo

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/event"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func TestMongo(t *testing.T) {
	//控制初始化超时时间,初始化
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	monitor := &event.CommandMonitor{
		//每个命令执行之前
		Started: func(ctx context.Context, cse *event.CommandStartedEvent) {
			fmt.Println(cse.Command)
		},
		//执行成功
		Succeeded: func(ctx context.Context, cse *event.CommandSucceededEvent) {

		},
		//执行失败
		Failed: func(ctx context.Context, cfe *event.CommandFailedEvent) {},
	}
	opts := options.Client().ApplyURI("mongodb://root:example@localhost:27017").SetMonitor(monitor)
	client, err := mongo.Connect(ctx, opts)
	assert.NoError(t, err)

	//建库
	mdb := client.Database("test")
	//建表
	col := mdb.Collection("articles")
	defer func() {
		_, err = col.DeleteMany(ctx, bson.D{})
	}()
	//插入
	res, err := col.InsertOne(ctx, Article{
		Id:      123,
		Title:   "我的标题",
		Content: "我的内容",
	})
	assert.NoError(t, err)
	//mongodb 文档id,非业务id
	fmt.Println(res.InsertedID)
	//查询
	filter := bson.D{bson.E{Key: "id", Value: 123}}
	var art Article
	err = col.FindOne(ctx, filter).Decode(&art)
	assert.NoError(t, err)
	fmt.Println(art)

	art = Article{}
	err = col.FindOne(ctx, Article{Id: 123}).Decode(&art)
	if err == mongo.ErrNoDocuments {
		//mongoDB 查询结果为空的条件
	}
	assert.NoError(t, err)
	//修改
	sets := bson.D{{Key: "$set", Value: bson.D{{Key: "title", Value: "测试D"}}}}
	//使用的ID是InsertedID
	//col.UpdateByID()
	rsp, err := col.UpdateMany(ctx, filter, sets)
	assert.NoError(t, err)
	fmt.Println("affected:", rsp.ModifiedCount)

	rsp, err = col.UpdateMany(ctx, filter, bson.D{
		bson.E{Key: "$set", Value: Article{
			Title: "我的标题2", AuthorID: 1234567,
		}},
	})
	assert.NoError(t, err)
	fmt.Println("affected:", rsp.ModifiedCount)
	//OR 查询
	//or := bson.A{bson.D{bson.E{"id", 123}},
	//	bson.D{bson.E{"id", 456}}}
	or := bson.A{bson.M{"id": 123}, bson.M{"id": 456}}
	orRes, err := col.Find(ctx, bson.D{bson.E{Key: "$or", Value: or}})
	assert.NoError(t, err)
	var ars []Article
	err = orRes.All(ctx, &ars)
	assert.NoError(t, err)
	fmt.Print(ars)
	// AND查询
	and := bson.A{bson.D{bson.E{Key: "id", Value: 123}},
		bson.D{bson.E{Key: "title", Value: "我的标题2"}}}
	andRes, err := col.Find(ctx, bson.D{bson.E{Key: "$and", Value: and}})
	assert.NoError(t, err)
	ars = []Article{}
	err = andRes.All(ctx, &ars)
	assert.NoError(t, err)
	//IN查询
	//in := bson.D{bson.E{"id", bson.D{bson.E{"$in", []any{123, 456}}}}}
	in := bson.D{bson.E{Key: "id", Value: bson.M{"$in": []any{123, 456}}}}
	inRes, err := col.Find(ctx, in)
	ars = []Article{}
	assert.NoError(t, err)
	err = inRes.All(ctx, &ars)
	assert.NoError(t, err)
	fmt.Println(inRes)
	inRes, err = col.Find(ctx, in, options.Find().SetProjection(bson.M{
		"id":    1,
		"title": 1,
	}))
	ars = []Article{}
	assert.NoError(t, err)
	err = inRes.All(ctx, &ars)
	assert.NoError(t, err)

	//查询特定字段
	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.Println(idxRes)
	//删除
	del, err := col.DeleteMany(ctx, filter)
	assert.NoError(t, err)
	fmt.Println("affected:", del.DeletedCount)

}

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"`
}

五、总结​

本文从 ​​环境搭建​​ 到 ​​CRUD 实战​​,详细演示了 Go 语言操作 MongoDB 的核心技术,并提供了生产级的代码示例和优化建议。通过掌握这些技能,开发者可以快速构建高可用、易扩展的 MongoDB 应用。