MongoDB简单CRUD操作(含GO中的库操作)

MongoDBCRUD操作(含GO中的库操作)

这周开始尝试做新项目,涉及到了文章的存储,查了查MongoDB在这方面用的比较多,因此对MongoDB和他在Golang中的用法进行了学习,以下是我的整理

文章目录

简介

MongoDB官网:入口

MongoDB是一种文档数据库,它以其可扩展性和灵活性而闻名,能够满足各种查询和索引的需求。

它将数据存储在类似JSON的灵活文档中,这意味着字段可能因文档而异,并且数据结构可以随时间变化。

MongoDB的文档模型易于开发者学习和使用,同时具备处理任何规模复杂要求的能力。它是分布式的,因此高可用性、横向扩展和地理分布都是内置且易于使用的特性

MongoDB的特点包括:

  • 面向集合存储,易于存储对象类型的数据。
  • 模式自由,不需要预定义模式。
  • 支持动态查询,可以在任意属性上建立索引。
  • 支持复制和故障恢复,确保数据的安全性和可靠性。
  • 高效的二进制数据存储,包括大型对象(如视频)。
  • 自动处理碎片,支持云计算层次的扩展性。

MongoDB适用于需要高性能、易部署、易使用和存储数据方便的场景,如网站实时数据处理、缓存层、高伸缩性场景等。不适用于要求高度事务性的系统、传统的商业智能应用或复杂的跨文档级联查询。

安装

可参考这篇文章,不过多赘述:入口

NoSQL文档数据库

MongoDB中的结构是:db-collection-document

MongoDB将数据记录存储为BSON文档。BSON是JSON的二进制表现形式。文档由键值对构成

对字段名称有以下限制:

  1. 字段名称_id保留用作主键;它的值在集合中必须是唯一的,不可变的,并且可以是数组以外的任何类型。
  2. 字段名称不能 包含null字符。
  3. 顶级字段名称不能 以美元符号($)字符开头。否则,从MongoDB 3.6开始,服务器允许存储包含点(即.)和美元符号(即 $)的字段名称。

使用文档的优点是:

文档(即对象)对应于许多编程语言中的内置数据类型。

嵌入式文档和数组减少了对昂贵连接的需求。

动态模式支持流畅的多态性。

主要操作

索引

即使从集合中删除所有文档,删除操作也不会删除索引。

原子性

MongoDB中的所有写操作都是单个文档级别的原子操作。

高性能

MongoDB提供高性能的数据持久化。特别是,

对嵌入式数据模型的支持减少了数据库系统上的I / O操作。

索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。

切换数据库

在shell中,db是指当前的数据库。键入db以显示当前数据库。

sql 复制代码
db

要切换数据库,请键入 use <db>。例如,要切换到 examples 数据库:

sql 复制代码
use examples

创建数据库

切换之前您无需创建数据库。当您第一次在数据库中存储数据时(例如在数据库中创建第一个集合),MongoDB会创建数据库。

sql 复制代码
mongosh> show dbs
admin   40.00 KiB
config  72.00 KiB
local   72.00 KiB
mongosh> use test
switched to db test
test> db.createCollection("test1")
{ ok: 1 }
test> show dbs
admin   40.00 KiB
config  72.00 KiB
local   72.00 KiB
test     8.00 KiB
test> db.dropDatabase()
{ ok: 1, dropped: 'test' }
test> show dbs
admin   40.00 KiB
config  72.00 KiB
local   72.00 KiB
test> use local
switched to db local

查找数据

sql 复制代码
test> db.test1.find()
[
  {
    _id: ObjectId('65f049c8c43bf33edd779a6c'),
    name: 'rx',
    age: 30,
    gpa: 3.2
  }
]

插入数据

sql 复制代码
test> db.test1.insertMany([{name:"rx2", age:30, gpa:3.2}, {name:"patrick", age:12, gpa:4.0}])
{
  acknowledged: true,
  insertedIds: {
    '0': ObjectId('65f04afec43bf33edd779a6d'),
    '1': ObjectId('65f04afec43bf33edd779a6e')
  }
}

test> db.test1.find()
[
  {
    _id: ObjectId('65f049c8c43bf33edd779a6c'),
    name: 'rx',
    age: 30,
    gpa: 3.2
  },
  {
    _id: ObjectId('65f04afec43bf33edd779a6d'),
    name: 'rx2',
    age: 30,
    gpa: 3.2
  },
  {
    _id: ObjectId('65f04afec43bf33edd779a6e'),
    name: 'patrick',
    age: 12,
    gpa: 4
  }
]
test> db.test1.insertOne({name:"Larry", age:"12", gpa:1.0, fullTime: false, registerDate: new Date()})
{
  acknowledged: true,
  insertedId: ObjectId('65f04cb0c43bf33edd779a6f')
}
test> db.test1.insertOne({name:"Larry", age:"12", gpa:1.0, fullTime: false, registerDate: new Date(), gradutionData:null, courses:["Bio", "Chem", "Math"], address:{street:"123 Fake st.", city:"Ontario", zip :"123"}})
{
  acknowledged: true,
  insertedId: ObjectId('65f04e02c43bf33edd779a70')
}
test>
sql 复制代码
test> db.test1.insertOne({name:"Larry", age:"12", gpa:1.0, fullTime: false, registerDate: new Date(), gradutionData:null, courses:["Bio", "Chem", "Math"], address:{street:"123 Fake st.", city:"Ontario", zip :"123"}})
{
  acknowledged: true,
  insertedId: ObjectId('65f04e02c43bf33edd779a70')
}

查找数据

sql 复制代码
test> db.test1.find()
[
  {
    _id: ObjectId('65f049c8c43bf33edd779a6c'),
    name: 'rx',
    age: 30,
    gpa: 3.2
  }
]

test> db.students.find({name:"rx"})

test> db.test1.find({name:"rx"})
[
  {
    _id: ObjectId('65f049c8c43bf33edd779a6c'),
    name: 'rx',
    age: 30,
    gpa: 3.2
  }
]
test> db.test1.find({}, {_id: false, name:true})
[
  { name: 'rx' },
  { name: 'rx2' },
  { name: 'patrick' },
  { name: 'Larry' },
  { name: 'Larry' }
]
test> db.test1.find({}, {name:true})
[
  { _id: ObjectId('65f049c8c43bf33edd779a6c'), name: 'rx' },
  { _id: ObjectId('65f04afec43bf33edd779a6d'), name: 'rx2' },
  { _id: ObjectId('65f04afec43bf33edd779a6e'), name: 'patrick' },
  { _id: ObjectId('65f04cb0c43bf33edd779a6f'), name: 'Larry' },
  { _id: ObjectId('65f04e02c43bf33edd779a70'), name: 'Larry' }
]

更新/替换数据

删除数据时,第一个参数是条件,第二个是更改字段,用$set,还可以自行添加其他选项

sql 复制代码
db.test.update(
   { name: "John" },
   { $set: { age: 31 } }
)

替换数据时,id不可替换,但是可以添加其他字段

sql 复制代码
db.inventory.replaceOne(
   { item: "paper" },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

删除

删除所有数据

sql 复制代码
db.inventory.deleteMany({})

删除对应状态的所有数据

sql 复制代码
db.inventory.deleteMany({ status : "A" })

删除对应状态下的第一个数据:

sql 复制代码
db.inventory.deleteOne( { status: "D" } )

在Golang当中的操作

我们以文章的CRUD为例

安装

go 复制代码
go get go.mongodb.org/mongo-driver/mongo

连接

go 复制代码
func init() {
	// Initialize MongoDB client
	client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))
	if err != nil {
		log.Fatal(err)
	}

	// Ping the primary
	if err := client.Ping(context.TODO(), nil); err != nil {
		log.Fatal(err)
	}

	// Get a handle for your collection
	collection = client.Database("article").Collection("article")
}

基础

  • bson.A:表示一个有序的BSON数组,类似于Go中的切片(slice)。
  • bson.E :表示**bson.D类型中的单个元素,通常用于构建 bson.D**类型的文档。
  • bson.D:是一个有序的文档表示,它是一个切片,其中的元素按照定义的顺序排列。这在需要保持字段顺序的情况下非常有用,比如在创建索引或执行排序操作时。
  • bson.M:是一个无序的文档表示,它是一个映射(map),其中的元素顺序是不确定的。这在顺序不重要的情况下使用起来更简洁。

CRUD

go 复制代码
type Article struct {
	ID      primitive.ObjectID `bson:"_id,omitempty"`
	Title   string             `bson:"title"`
	Content string             `bson:"content"`
	Author  string             `bson:"author"`
}

var (
	ctx        context.Context
	collection *mongo.Collection
)
...

func main() {
	router := gin.Default()

	router.POST("/articles", createArticle)
	router.GET("/articles/:id", getArticle)
	router.GET("/articles", listArticles)
	router.PUT("/articles/:id", updateArticle)
	router.DELETE("/articles/:id", deleteArticle)

	router.Run(":8080")
}

func createArticle(c *gin.Context) {
	var article Article
	if err := c.BindJSON(&article); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// 自己创建
	// article := bson.D{{"name", "John"}, {"age", 30}, {"city", "New York"}}
	// doc := bson.D{{"name", "John"}, {"age", 30}, {"city", "New York"}, {"hobbies", bson.A{"reading", "gaming", "hiking"}}}

	result, err := collection.InsertOne(ctx, article)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"id": result.InsertedID})
}

func getArticle(c *gin.Context) {
	id := c.Param("id")

	objID, _ := primitive.ObjectIDFromHex(id)
	var article Article
	err := collection.FindOne(ctx, bson.M{"_id": objID}).Decode(&article)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "Article not found"})
		return
	}

	c.JSON(http.StatusOK, article)
}

func listArticles(c *gin.Context) {
	// 设置投影,排除_id字段
	projection := bson.D{{"_id", 0}}
	opts := options.Find().SetProjection(projection)

	cursor, err := collection.Find(context.Background(), bson.M{}, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	defer cursor.Close(context.Background())

	var articles []bson.M
	if err = cursor.All(context.Background(), &articles); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	// 返回结果时不包含_id字段
	c.JSON(http.StatusOK, articles)
}

func updateArticle(c *gin.Context) {
	id := c.Param("id")

	objID, _ := primitive.ObjectIDFromHex(id)
	var article Article
	if err := c.BindJSON(&article); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result, err := collection.UpdateOne(ctx, bson.M{"_id": objID}, bson.M{"$set": article})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"updated": result.ModifiedCount})
}

func deleteArticle(c *gin.Context) {
	id := c.Param("id")

	objID, _ := primitive.ObjectIDFromHex(id)
	result, err := collection.DeleteOne(ctx, bson.M{"_id": objID})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"deleted": result.DeletedCount})
}

下周更新更多操作

相关推荐
倔强的石头_10 小时前
kingbase备份与恢复实战(二)—— sys_dump库级逻辑备份与恢复(Windows详细步骤)
数据库
jiayou642 天前
KingbaseES 实战:深度解析数据库对象访问权限管理
数据库
李广坤2 天前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
爱可生开源社区4 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
AI全栈实验室4 天前
MongoDB迁移金仓踩了5个坑,最后一个差点回滚
mongodb
随逸1774 天前
《从零搭建NestJS项目》
数据库·typescript
花酒锄作田4 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
加号34 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏4 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐4 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端