一、前言
在上一篇文章《为什么选择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 应用。