MongoDBCRUD操作(含GO中的库操作) 这周开始尝试做新项目,涉及到了文章的存储,查了查MongoDB在这方面用的比较多,因此对MongoDB和他在Golang中的用法进行了学习,以下是我的整理
简介 MongoDB官网:入口
MongoDB是一种文档数据库,它以其可扩展性和灵活性而闻名,能够满足各种查询和索引的需求。
它将数据存储在类似JSON的灵活文档中,这意味着字段可能因文档而异,并且数据结构可以随时间变化。
MongoDB的文档模型易于开发者学习和使用,同时具备处理任何规模复杂要求的能力。它是分布式的,因此高可用性、横向扩展和地理分布都是内置且易于使用的特性
MongoDB的特点包括:
面向集合存储 ,易于存储对象类型的数据。
模式自由 ,不需要预定义模式。
支持动态查询 ,可以在任意属性上建立索引。
支持复制和故障恢复 ,确保数据的安全性和可靠性。
高效的二进制数据存储 ,包括大型对象(如视频)。
自动处理碎片 ,支持云计算层次的扩展性。
MongoDB适用于需要高性能、易部署、易使用和存储数据方便的场景,如网站实时数据处理、缓存层、高伸缩性场景等。不适用于要求高度事务性的系统、传统的商业智能应用或复杂的跨文档级联查询。
安装 可参考这篇文章,不过多赘述:入口
NoSQL文档数据库 MongoDB中的结构是:db-collection-document
MongoDB将数据记录存储为BSON文档。BSON是JSON的二进制表现形式。文档由键值对构成
对字段名称有以下限制:
字段名称_id
保留用作主键;它的值在集合中必须是唯一的,不可变的,并且可以是数组以外的任何类型。
字段名称不能 包含null
字符。
顶级字段名称不能 以美元符号($
)字符开头。否则,从MongoDB 3.6开始,服务器允许存储包含点(即.
)和美元符号(即 $
)的字段名称。
使用文档的优点是:
文档(即对象)对应于许多编程语言中的内置数据类型。
嵌入式文档和数组减少了对昂贵连接的需求。
动态模式支持流畅的多态性。
主要操作 索引
即使从集合中删除所有文档,删除操作也不会删除索引。
原子性
MongoDB中的所有写操作都是单个文档级别的原子操作。
高性能
MongoDB提供高性能的数据持久化。特别是,
对嵌入式数据模型的支持减少了数据库系统上的I / O操作。
索引支持更快的查询,并且可以包含来自嵌入式文档和数组的键。
切换数据库
在shell中,db
是指当前的数据库。键入db
以显示当前数据库。
要切换数据库,请键入 use <db>
。例如,要切换到 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 KiBmongosh> 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 KiBtest 8.00 KiB test> db.dropDatabase() { ok: 1 , dropped: 'test' } test> show dbs admin 40.00 KiB config 72.00 KiB local 72.00 KiBtest> 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 () { client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017" )) if err != nil { log.Fatal(err) } if err := client.Ping(context.TODO(), nil ); err != nil { log.Fatal(err) } 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 } 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) { 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 } 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}) }
下周更新更多操作