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

RenXin Lv3

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以显示当前数据库。

1
db

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

1
use examples

创建数据库

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

查找数据

1
2
3
4
5
6
7
8
9
test> db.test1.find()
[
{
_id: ObjectId('65f049c8c43bf33edd779a6c'),
name: 'rx',
age: 30,
gpa: 3.2
}
]

插入数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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>
1
2
3
4
5
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')
}

查找数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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,还可以自行添加其他选项

1
2
3
4
db.test.update(
{ name: "John" },
{ $set: { age: 31 } }
)

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

1
2
3
4
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)

删除

删除所有数据

1
db.inventory.deleteMany({})

删除对应状态的所有数据

1
db.inventory.deleteMany({ status : "A" })

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

1
db.inventory.deleteOne( { status: "D" } )

在Golang当中的操作

我们以文章的CRUD为例

安装

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

连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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})
}

下周更新更多操作

  • Title: MongoDBCRUD操作(含GO中的库操作)
  • Author: RenXin
  • Created at : 2024-03-15 23:38:24
  • Updated at : 2024-03-15 23:38:24
  • Link: https://blog.renxin.space/middleware/MongoDB_crud/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments