【Go】Go MongoDB 快速入门

embedded/2025/3/15 11:21:36/

1. MongoDB 简介

1.1 MongoDB 介绍

由于我们时常需要存储一些大文本数据(比如文章内容),存储到一些关系型数据库可能不是最好的选择,这个时候就需要引入一些 NoSQL(Not Only SQL),比如 MongoDB 等数据库,NoSQL 主要用于解决大规模数据集合、多种数据格式等大数据相关问题,在实践中可以理解为用关系型数据库不好解决的就可以考虑使用 NoSQL

⭐ MongoDB 官方文档:https://www.mongodb.com/zh-cn/docs/

1.2 MongoDB 基本特性

MongoDB 主要有以下几点特性:

  • 面向集合存储:一个集合可以近似理解为 MySQL 当中的表;集合中存储了很多文档,可以近似理解为 MySQL 当中的数据行
  • 模式自由:MongoDB 采用无模式结构存储数据,意味着可以不用像 MySQL 那样预先定义表结构
  • 扩展性强:MongoDB 内部支持分片功能,便于进行横向扩展

总结下来,为什么我们在技术选型的时候考虑使用 MongoDB,主要是以下两方面原因:

  1. 灵活的文档模型:不需要预先定义文档结构,即可插入数据
  2. 便于横向扩展:可以通过增加 MongoDB 实例来应对数据增长

2. MongoDB 安装

本次我们通过使用 Docker 的方式来启动 MongoDB,在项目中编写如下docker-compose.yaml文件

services:mongo:image: mongo:6.0restart: alwaysenvironment:MONGO_INITDB_ROOT_USERNAME: rootMONGO_INITDB_ROOT_PASSWORD: exampleports:- 27017:27017

然后在命令行中输入docker compose up即可启动

看到如上图所示内容证明 MongoDB 启动成功!

3. MongoDB 快速入门

3.1 初始化客户端

初始化客户端主要分为以下步骤:

  1. 创建配置对象:使用options.Client().ApplyURI()并传入连接地址字符串
  2. 创建客户端连接:使用mongo.Connect方法并传入配置对象
package mainimport ("context""fmt""go.mongodb.org/mongo-driver/v2/event""go.mongodb.org/mongo-driver/v2/mongo""go.mongodb.org/mongo-driver/v2/mongo/options"
)func main() {// 1. 准备命令监视器monitor := &event.CommandMonitor{Started: func(ctx context.Context, startedEvent *event.CommandStartedEvent) {// 输出查询命令fmt.Println(startedEvent.Command)},Succeeded: func(ctx context.Context, succeededEvent *event.CommandSucceededEvent) {},Failed:    func(ctx context.Context, errEvent *event.CommandFailedEvent) {},}// 2. 设置连接地址opts := options.Client().ApplyURI("mongodb://root:example@127.0.0.1:27017").SetMonitor(monitor)client, err := mongo.Connect(opts)if err != nil {panic(err)}// 3. 创建database和collectioncol := client.Database("webook").Collection("articles")fmt.Println(col)
}

3.2 插入文档

在上一小节初始化客户端的基础上,我们就可以使用得到的客户端client对象来操作 MongoDB 了:

插入文档的 API 为:col.InsertOne()

func main() {// 1. 准备命令监视器monitor := &event.CommandMonitor{Started: func(ctx context.Context, startedEvent *event.CommandStartedEvent) {// 输出查询命令fmt.Println(startedEvent.Command)},Succeeded: func(ctx context.Context, succeededEvent *event.CommandSucceededEvent) {},Failed:    func(ctx context.Context, errEvent *event.CommandFailedEvent) {},}// 2. 设置连接地址opts := options.Client().ApplyURI("mongodb://root:example@127.0.0.1:27017").SetMonitor(monitor)client, err := mongo.Connect(opts)if err != nil {panic(err)}// 3. 创建database和collectioncol := client.Database("webook").Collection("articles")// 4. 插入文档ctx := context.Background()result, err := col.InsertOne(ctx, Article{Id:       1,Title:    "我的标题",Content:  "我的内容",AuthorId: 1,Status:   1,Ctime:    123,Utime:    456,})if err != nil {panic(err)}// 这里的InsertedID为文档IDfmt.Println(result.InsertedID)
}// Article 文章结构体
type Article struct {Id       int64Title    stringContent  stringAuthorId int64Status   uint8Ctime    int64Utime    int64
}

程序运行结果如下:

💡 温馨提示:但是 MongoDB 没有自增主键的概念,因此 InsertedID 是文档ID对应的字符串

3.3 查询文档

插入文档的 API 有如下两种方式:

  1. 通过bson结构来构造查询条件:
  2. 通过结构体对象进行查询(需要小心零值问题)
func main() {// ... 省略前面代码// 5.1 通过bson查询文档filter := bson.D{bson.E{Key: "id", Value: 1}}var art Articleerr = col.FindOne(ctx, filter).Decode(&art)if err != nil {panic(err)}fmt.Println(art)// 5.2 通过结构体查询文档var art2 Articleerr = col.FindOne(ctx, Article{Id: 1,}).Decode(&art2)if err == mongo.ErrNoDocuments {fmt.Errorf("文档未找到! %w", err)}fmt.Println(art2)
}// Article 文章结构体
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"`
}

程序运行结果如下:

💡 温馨提示:如果使用 结构体对象作为查询条件,则需要在结构体标签处加上omitempty参数,表示如果为零值不参与到过滤条件中

3.4 修改文档

修改文档的 API 也跟查询一样有如下两种方式:

case1:构造过滤查询条件

case2:构造更新对象

1. 通过`bson`结构来构造修改条件:
2. 通过结构体对象进行修改(需要小心零值问题)
func main() {// 省略上述代码// 6. 修改文档upDoc := bson.D{bson.E{Key: "$set", Value: Article{Title:   "新的标题",Content: "新的内容",Utime:   789,}}}result, err := col.UpdateOne(ctx, filter, upDoc)if err != nil {panic(err)}fmt.Println(result.ModifiedCount) // 修改数量
}

💡 温馨提示:这里的"$set"是 MongoDB 独有的操作符

3.5 删除文档

删除文档的方式和查找是一样的,只需要定义过滤条件即可:

这里直接贴代码:

func main() {// 省略上述代码// 7. 删除文档delFilter := bson.D{bson.E{Key: "id", Value: 1}}delResult, err := col.DeleteOne(ctx, delFilter)if err != nil {panic(err)}fmt.Println(delResult.DeletedCount)
}

程序运行结果:

4. MongoDB 进阶查询

4.1 构造or查询条件

首先我个人的一个结论就是:在使用 MongoDB 构造查询结构体时,最外层一定是 bson.D 的结构

下面为使用 or 查询的方式(对于初学者而言不需要关心如何构造,跟着抄就行)

func main() {// 省略上述代码ctx := context.Background()// 插入id=1,2,3的记录col.InsertOne(ctx, Article{Id: 1,})col.InsertOne(ctx, Article{Id: 2,})col.InsertOne(ctx, Article{Id: 3,})// 构造or查询条件filter := bson.A{bson.D{bson.E{Key: "id", Value: 1}},bson.D{bson.E{Key: "id", Value: 2}},}// 查询id为1或者2的记录find, err := col.Find(ctx, bson.D{bson.E{Key: "$or", Value: filter}})var articles []Articleerr = find.All(ctx, &articles)if err != nil {panic(err)}fmt.Println(articles)
}

程序运行结果:

4.2 构造and查询条件

下面为使用 and 查询的方式(对于初学者而言不需要关心如何构造,跟着抄就行)

func main() {// 构造and查询条件andFilter := bson.A{bson.D{bson.E{Key: "id", Value: 1}},bson.D{bson.E{Key: "title", Value: "我的标题"}},}res, err := col.Find(ctx, bson.D{bson.E{Key: "$and", Value: andFilter}})if err != nil {panic(err)}err = res.All(ctx, &articles)if err != nil {panic(err)}fmt.Println(articles)
}

程序运行结果:

4.3 构造in查询条件

下面为使用 in 查询的方式(对于初学者而言不需要关心如何构造,跟着抄就行)

func main() {// 省略上述代码var articles []Article// 构造in查询条件inFilter := bson.D{bson.E{Key:   "id",Value: bson.D{bson.E{Key: "$in", Value: []int{1, 2, 3}}}},}res, err := col.Find(ctx, inFilter)if err != nil {panic(err)}err = res.All(ctx, &articles)if err != nil {panic(err)}fmt.Println(articles)
}

程序运行结果:


http://www.ppmy.cn/embedded/172746.html

相关文章

python web开发django库安装与使用

下面我将指导您如何安装 Django 库以及基本的使用方法。Django 是一个高级的 Python Web 框架,它鼓励快速开发和干净、实用的设计。以下是详细的步骤: 1. 安装 Django 首先,确保您的系统上已经安装了 Python 和 pip(Python 的包…

C++:类和对象(从底层编译开始)详解[前篇]

目录 一.inline内联的详细介绍 (1)为什么在调用内联函数时不需要建立栈帧: (2)为什么inline声明和定义分离到两个文件会产生链接错误,链接是什么,为什么没有函数地址: 二.类&…

【redis】发布订阅

Redis的发布订阅(Pub/Sub)是一种基于消息多播的通信机制,它允许消息的**发布者(Publisher)向特定频道发送消息,而订阅者(Subscriber)**通过订阅频道或模式来接收消息。 其核心特点如…

DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)

🌟前言: 在软件开发、项目管理和系统设计等领域,图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及,我们现在可以更轻松地创建各种专业图表。 名人说:博观而约取,厚积而薄发。——苏轼《稼说送张琥》 创作者&…

探索DB-GPT:革新数据库交互的AI原生框架

引言 在AI与数据技术快速发展的今天,如何让自然语言与数据库实现无缝交互成为开发者关注的重点。DB-GPT作为一个开源的AI原生数据应用开发框架,通过整合大语言模型(LLM)、多代理协作、检索增强生成(RAG)等前沿技术,为开发者提供了高效构建数据驱动型AI应用的解决方案。…

PowerMock的使用

1. mock私有方法 待测试类 public class Demo {public void publicMethod() {System.out.println("public method invoke");protectedMethod("str");privateMethodA();privateMethodB();System.out.println("public method end");}protected v…

基于RTTR在C++中实现结构体数据的多层级动态读写

文章目录 1.背景2.RTTR2.1.注册结构体2.2.实现读操作2.3.实现写操作 3.读写调用例程4.结语 1.背景 目前有个项目,同一台电脑上的codesys程序将其结构体数据通过共享内存的方式写道了一个“共享内存”上。 我在取得内存数据后,需要对这个数据进行结构体的…

第13章贪心算法

贪心算法 局部最优求得总体最优 适用于桌上有6张纸币,面额为100 100 50 50 50 10,问怎么能拿走3张纸币,总面额最大?—拿单位价值最高的 只关注局部最优----关注拿一张的最大值拆解-----拿三次最大的纸币 不适用于桌面三件物品&am…