目录
初始
GitHub:https://github.com/mongodb/mongo-go-driver
安装
1 |
go get -u go.mongodb.org/mongo-driver/mongo |
初始化MongoDB实例
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 |
import ( "context" "fmt" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" "time" ) func Init() *mongo.Client { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // 设置连接参数 // construct url: mongodb://username:password@127.0.0.1:27017/dbname mongoUrl := "mongodb://" + user + ":" + password + "@" + url + "/" + dbname clientOptions := options.Client().ApplyURI(mongoUrl) // 连接到 MongoDB client, err := mongo.Connect(ctx, clientOptions) if err != nil { fmt.Printf("connect to mongodb error: %v", err) } // 测试 err = client.Ping(context.TODO(), readpref.Primary()) if err != nil { fmt.Printf("check the connection to mongo error: %v", err) } // 返回 MongoDB 实例 return client } |
在Go中使用BSON对象
在我们发送查询给数据库之前, 很重要的一点是,理解Go Driver是如何和BSON对象协同工作的。JSON文档在MongoDB里面以二进制形式存储, 被称作BSON(二进制编码的JSON)。不像其他的数据库保存JSON数据为简单的字符串和数字, BSON扩展了JSON的保存形式, 包括额外的类型, 比如int, long, date, floating point以及decimal128。这使得它让应用程序更容易来可靠地处理、排序和比较数据。Go Driver有两个系列的类型表示BSON数据:D系列类型和Raw系列类型。
D系列的类型使用原生的Go类型简单地构建BSON对象。这可以非常有用的来创建传递给MongoDB的命令。 D系列包含4种类型:
– D:一个BSON文档。这个类型应该被用在顺序很重要的场景, 比如MongoDB命令。
– M: 一个无需map。 它和D是一样的, 除了它不保留顺序。
– A: 一个BSON数组。
– E: 在D里面的一个单一的子项。
这里有一个使用D类型构建的过滤文档的例子, 它可能被用在查询name字段匹配“Alice”或者“Bob”的文档:
1 2 3 4 5 6 7 |
bson.D{{ "name", bson.D{{ "$in", bson.A{"Alice", "Bob"} }} }} |
Raw系列类型被用来验证bytes类型的slice, 你也可以从Raw类型使用Lookup()获得单个的子项, 这在你不想要unmarshall某个BSON到另一个类型的时候很有用。这篇帮助文档会只使用D系列类型。
CRUD操作(不使用struct)
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 |
// 增 func MongoInsert(c *gin.Context) { // 构建 context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 选择集合 collection := mongo.Client.Database("testing").Collection("numbers") // 待新增数据 insertData := bson.D{{"name", "pi"}, {"value", 3.14159}} // 新增一条数据 res, err := collection.InsertOne(ctx, insertData) if err != nil { fmt.Printf("Mongo新增数据错误:%s \n",err.Error()) return } // 获取新增数据的id id := res.InsertedID fmt.Printf("新增成功,id:%v \n",id) } // 查询 func (u *userController) MongoFind(c *gin.Context) { // 构建 context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 选择集合 collection := mongo.Client.Database("testing").Collection("numbers") // 筛选条件,为空,则查询所有数据 filter := bson.D{} // 设置查找选项 findOptions := options.Find() findOptions.SetLimit(5) // 查询5条数据 // 查找,得到一个游标 cur, err := collection.Find(ctx, filter, findOptions) if err != nil { fmt.Printf("Mongo查询数据错误:%s \n",err.Error()) return } // 关闭游标 defer cur.Close(ctx) // 遍历游标,取得数据 for cur.Next(ctx) { var result bson.D // 对游标解析出正确的数据 err := cur.Decode(&result) if err != nil { fmt.Printf("Mongo cur解码数据错误:%s \n",err.Error()) return } // do something with result.... fmt.Printf("result的数据:%#v \n",result) } if err := cur.Err(); err != nil { fmt.Printf("Mongo遍历已查询的数据错误:%s \n",err.Error()) return } } // 删除 func (u *userController) MongoDel(c *gin.Context) { // 构建 context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 选择集合 collection := mongo.Client.Database("testing").Collection("numbers") // 筛选条件,为空,则查询所有数据 filter := bson.D{} // 批量删除 deleteResult, err := collection.DeleteMany(ctx, filter) if err != nil { fmt.Printf("Mongo删除数据错误:%s \n",err.Error()) return } fmt.Printf("删除数据%v条",deleteResult.DeletedCount) } |
GO Mongodb时间处理
go在操作monogdb时间的时候如果使用time.Time时间格式,则前后端的时间显示是这样的:2021-01-28T06:38:43.622Z;这种格式很不友好(时区还不对),更多时候我们可能需要的是这种格式:2021-01-28 14:38:43;在网上查了一些资料,发现解决方案都不太好,有的是要手动做转换,有的是改变了mongodb的存储内容;我这里综合了一些资料给出以下解决方法(通过申明time.Time的扩展,重写json和bson序列化代码实现):
序列化处理代码:
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 |
package common import ( "errors" "fmt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/bsontype" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/x/bsonx/bsoncore" "time" ) type JsonTime time.Time const ( timeFormart = "2006-01-02 15:04:05" ) //实现json反序列化,从传递的字符串中解析成时间对象 func (t *JsonTime) UnmarshalJSON(data []byte) (err error) { now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local) *t = JsonTime(now) return } //实现json序列化,将时间转换成字符串byte数组 func (t JsonTime) MarshalJSON() ([]byte, error) { b := make([]byte, 0, len(timeFormart)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, timeFormart) b = append(b, '"') return b, nil } //mongodb是存储bson格式,因此需要实现序列化bsonvalue(这里不能实现MarshalBSON,MarshalBSON是处理Document的),将时间转换成mongodb能识别的primitive.DateTime func (t *JsonTime) MarshalBSONValue() (bsontype.Type, []byte, error) { targetTime := primitive.NewDateTimeFromTime(time.Time(*t)) return bson.MarshalValue(targetTime) } //实现bson反序列化,从mongodb中读取数据转换成time.Time格式,这里用到了bsoncore中的方法读取数据转换成datetime然后再转换成time.Time func (t *JsonTime) UnmarshalBSONValue(t2 bsontype.Type, data []byte) error { v, _, valid := bsoncore.ReadValue(data, t2) if valid == false { return errors.New(fmt.Sprintf("%s, %s, %s", "读取数据失败:", t2, data)) } *t = JsonTime(v.Time()) return nil } |
在需要进行时间转换的struct中申明common.JsonTime
1 2 3 4 5 |
type User struct { Name string `bson:"name" json:"name"` Type int `bson:"type" json:"type"` Createtime common.JsonTime `bson:"createtime" json:"createtime"` } |
CRUD操作(使用struct)
model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package mongoModel import ( "go.mongodb.org/mongo-driver/bson/primitive" "gspeed/app/model/base" ) type Book struct { Id primitive.ObjectID `bson:"_id" json:"id"` Name string `json:"name"` Category string `json:"category"` Weight int `json:"weight"` CreatedAt common.JsonTime `json:"created_at" bson:"created_at"` Author AuthorInfo `json:"author"` } type AuthorInfo struct { Name string `json:"name"` Country string `json:"country"` } |
curd
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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
// mongo model 新增数据 func MongoModelInsert(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 获取数据库和集合 collection := mongo.Client.Database("testing").Collection("book") // 清空文档 err := collection.Drop(ctx) if err != nil { fmt.Printf("Mongo新增数据前的清空错误:%s \n",err.Error()) return } // 设置索引 idx := mongo.IndexModel{ Keys: bsonx.Doc{{"name", bsonx.Int32(1)}}, Options: options.Index().SetUnique(true), } idxRet, err := collection.Indexes().CreateOne(ctx, idx) if err != nil { fmt.Printf("Mongo新增数据-索引设置错误:%s \n",err.Error()) return } fmt.Printf("collection.Indexes().CreateOne:%v \n",idxRet) // 数据 books := []interface{}{ &mongoModel.Book{ Id: primitive.NewObjectID(), Name: "深入理解计算机操作系统", Category: "计算机", Weight: 1, CreatedAt: common.JsonTime(time.Now()), Author: mongoModel.AuthorInfo{ Name: "兰德尔 E.布莱恩特", Country: "美国", }, }, &mongoModel.Book{ Id: primitive.NewObjectID(), Name: "深入理解Linux内核", Category: "计算机", Weight: 1, CreatedAt: common.JsonTime(time.Now()), Author: mongoModel.AuthorInfo{ Name: "博韦,西斯特", Country: "美国", }, }, &mongoModel.Book{ Id: primitive.NewObjectID(), Name: "三体", Category: "科幻", Weight: 1, CreatedAt: common.JsonTime(time.Now()), Author: mongoModel.AuthorInfo{ Name: "刘慈欣", Country: "中国", }, }, } // 插入一条数据 insertOneResult, err := collection.InsertOne(ctx, books[0]) if err != nil { fmt.Printf("Mongo新增单条数据错误:%s \n",err.Error()) return } fmt.Printf("collection.InsertOne: %v \n", insertOneResult.InsertedID) // 插入多条数据 insertManyResult, err := collection.InsertMany(ctx, books[1:]) if err != nil { fmt.Printf("Mongo新增多条数据错误:%s \n",err.Error()) return } fmt.Printf("collection.InsertMany: %v \n", insertManyResult.InsertedIDs) } // mongo model 获取总数 func MongoModelTotal(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 获取数据库和集合 collection := mongo.Client.Database("testing").Collection("book") // 获取数据总数 count, err := collection.CountDocuments(ctx, bson.D{}) if err != nil { fmt.Printf("Mongo获取总数错误:%s \n",err.Error()) return } } // mongo model 获取一条数据 func MongoModelFindOne(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 查询单条数据 var one mongoModel.Book err := collection.FindOne(ctx, bson.M{"name": "三体"}).Decode(&one) if err != nil { fmt.Printf("Mongo查询单条数据错误:%s \n",err.Error()) return } fmt.Printf("Mongo查询单条数据:%v \n",one) } // mongo model 获取多条数据(方式一) func (u *userController) MongoModelFindMany1(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") cur, err := collection.Find(ctx, bson.D{}) if err != nil { fmt.Printf("Mongo查询多条数据(方式一)错误:%s \n",err.Error()) return } if err := cur.Err(); err != nil { fmt.Printf("Mongo查询多条数据(方式一) cur错误:%s \n",err.Error()) return } var all []*mongoModel.Book err = cur.All(ctx, &all) if err != nil { fmt.Printf("Mongo查询多条数据(方式一) cur.All错误:%s \n",err.Error()) return } cur.Close(ctx) fmt.Printf("Mongo查询数据:%v \n",all) } // mongo model 获取多条数据(方式二) func MongoModelFindMany2(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 查询多条数据(方式二) cur, err := collection.Find(ctx, bson.D{}) if err != nil { fmt.Printf("Mongo查询多条数据(方式二)错误:%s \n",err.Error()) return } if err := cur.Err(); err != nil { fmt.Printf("Mongo查询多条数据(方式二) cur错误:%s \n",err.Error()) return } books := []*mongoModel.Book{} for cur.Next(ctx) { var b mongoModel.Book if err = cur.Decode(&b); err != nil { fmt.Printf("Mongo查询多条数据(方式二) cur.Decode错误:%s \n",err.Error()) return } books = append(books, &b) } cur.Close(ctx) fmt.Printf("Mongo查询数据:%v \n",all) } // mongo model 模糊查询 func MongoModelFindLike(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 模糊查询 cur, err := collection.Find(ctx, bson.M{"name": primitive.Regex{Pattern: "深入"}}) if err != nil { fmt.Printf("Mongo模糊查询错误:%s \n",err.Error()) return } if err := cur.Err(); err != nil { fmt.Printf("Mongo模糊查询 cur错误:%s \n",err.Error()) return } books := []*mongoModel.Book{} for cur.Next(ctx) { var b mongoModel.Book if err = cur.Decode(&b); err != nil { fmt.Printf("Mongo模糊查询 cur.Decode错误:%s \n",err.Error()) return } books = append(books, &b) } cur.Close(ctx) fmt.Printf("Mongo查询数据:%v \n",books) } // mongo model 二级结构体查询 func MongoModelFindSubStruct(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 二级结构体查询 cur, err := collection.Find(ctx, bson.M{"author.country": "中国"}) // cur, err := collection.Find(ctx, bson.D{bson.E{"author.country", "中国"}}) if err != nil { fmt.Printf("Mongo二级结构体查询错误:%s \n",err.Error()) return } if err := cur.Err(); err != nil { fmt.Printf("Mongo二级结构体查询 cur错误:%s \n",err.Error()) return } books := []*mongoModel.Book{} for cur.Next(ctx) { var b mongoModel.Book if err = cur.Decode(&b); err != nil { fmt.Printf("Mongo二级结构体查询 cur.Decode错误:%s \n",err.Error()) return } books = append(books, &b) } cur.Close(ctx) fmt.Printf("Mongo查询数据:%v \n",books) } // mongo model 修改一条数据 func (u *userController) MongoModelUpdateOne(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 修改一条数据 filter := bson.M{"name": "深入理解计算机操作系统"} // 那条数据 update := bson.M{"$set": bson.M{"weight":2}} // 更新的数据 updateResult, err := collection.UpdateOne(ctx, filter, update) if err != nil { fmt.Printf("Mongo update one 错误:%s \n",err.Error()) return } fmt.Printf("collection.UpdateOne:%v \n", updateResult) fmt.Printf("Mongo更新数据:%v \n",updateResult) } // mongo model 修改一条数据,如果不存在则插入 func MongoModelUpdateOrInsert(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") // 修改一条数据,如果不存在则插入 updateData := &mongoModel.Book{ Id: primitive.NewObjectID(), Name: "球状闪电", Category: "科幻", Author: mongoModel.AuthorInfo{ Name: "刘慈欣", Country: "中国", }, } update := bson.M{"$set": updateData} updateOpts := options.Update().SetUpsert(true) updateResult, err := collection.UpdateOne(ctx, bson.M{"_id": updateData.Id}, update, updateOpts) if err != nil { fmt.Printf("Mongo update错误:%s \n",err.Error()) return } fmt.Printf("collection.UpdateOne:%v \n", updateResult) fmt.Printf("Mongo更新数据:%v \n",updateResult) } // mongo model 删除 func (u *userController) MongoModelDel(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() collection := mongo.Client.Database("testing").Collection("book") filter := bson.D{} deleteResult, err := collection.DeleteMany(ctx, filter) if err != nil { fmt.Printf("Mongo删除数据错误:%s \n",err.Error()) return } fmt.Printf("删除数据%v条",deleteResult.DeletedCount) } |
分页查询
当集合中存储了大量的数据后,一般情况下我们需要实现分页查询的功能,我们可以通过设置options的SetLimit和SetSkip来实现。
以下程序查询第5页,每页显示20条记录:
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 |
// 查询 func MongoFind(c *gin.Context) { // 构建 context ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // 选择集合 collection := mongo.Client.Database("testing").Collection("numbers") // 筛选条件,为空,则查询所有数据 filter := bson.D{} // 分页查询选项设置 // Pass these options to the Find method findOptions := options.Find() // use setlimit and setskip to implement pageable query findOptions.SetLimit(20) // specifies the number of documents to skip before returning. findOptions.SetSkip(20 * (5 - 1)) // 查找,得到一个游标 cur, err := collection.Find(ctx, filter, findOptions) if err != nil { fmt.Printf("Mongo查询数据错误:%s \n",err.Error()) return } // 关闭游标 defer cur.Close(ctx) // 遍历游标,取得数据 for cur.Next(ctx) { var result bson.D // 对游标解析出正确的数据 err := cur.Decode(&result) if err != nil { fmt.Printf("Mongo cur解码数据错误:%s \n",err.Error()) return } // do something with result.... fmt.Printf("result的数据:%#v \n",result) } if err := cur.Err(); err != nil { fmt.Printf("Mongo遍历已查询的数据错误:%s \n",err.Error()) return } } |
参考
mongo-go-driver使用简介
mongo-go-driver详细使用示例
MongoDB Go Driver使用帮助文档
GO Mongodb时间处理