- 2019-11-17
Golang MongoDB扩展包之CR...
前言
上篇文章主要写了下MongoDB的连接,这篇文章写下MongoDB的增删改查。
添加
开始之前,我们先创建一个collection,命名users,这里创建集合的动作不是必须的,你也可以直接插入一条数据,集合会自动创建,我们这里创建集合是为了利用MongoDB的验证规则,类似于mysql建表时指定字段的类型,是否必填等,这样在插入数据时就会效验数据的有效性。如果没有自定义验证规则,默认MongoDB是不会效验字段的。
下面简单写两个例子来验证一下:
1.未定义效验规则
func insertOne1(ctx context.Context, db *mongo.Database) {
if _, err := db.Collection(userCollection).InsertOne(ctx, bson.M{"name": "test", "age": 3}); err != nil {
log.Fatalln(err.Error())
}
if _, err := db.Collection(userCollection).InsertOne(ctx, bson.M{"name": 3, "age": "test"}); err != nil {
log.Fatalln(err.Error())
}
}
然后查看数据库结果,你会发现有两条记录,它们的类型不一样,第一条name是string,第二条name是int32,在生产环境不应该出现这样的错误,所以有必要提前建立好集合的效验规则,保证数据字段属性的统一性。默认inserOne是会效验格式的,但是因为我们没有创建效验规则,所以无效。
2.提前建立效验规则
jsonSchema := bson.M{
"bsonType": "object",
"required": []string{"name", "age"},
"properties": bson.M{
"name": bson.M{
"bsonType": "string",
"description": "the name of the user, which is required and must be a string",
},
"age": bson.M{
"bsonType": "int",
"description": "the age of the user, which is required and must be a int",
"minimum": 0,
"maximum": 200,
},
},
}
validator := bson.M{
"$jsonSchema": jsonSchema,
}
opts := options.CreateCollection().SetValidator(validator)
if err := db.CreateCollection(ctx, userCollection, opts); err != nil {
log.Fatalln(err.Error())
}
注意:CreateCollection方法是golang MongoDB扩展包1.4版本以上才有的方法,请确保你的扩展包版本>=1.4。
上面的代码,我们建立了一个集合,并指定name和age是必填,且name为string类型,age为int类型,而且age指定了范围。这下我们再来插入非法数据就会报错,再次执行示例1的代码,发现只插入了一条数据,第二条报错了,就是因为格式不对。
有什么办法能避免这个错误吗?当然有解决方案,那就是不效验数据格式,指定insertOne的第三个参数,第三个option参数只有一个方法,就是设置不效验,这里要注意,MongoDB的扩展包有很多options选项,下面的CRUD操作所提到的options都是类似的,不同的操作options不一样,具体参考这里:options。代码如下:
func insertOne1(ctx context.Context, db *mongo.Database) {
if _, err := db.Collection(userCollection).InsertOne(ctx, bson.M{"name": "test", "age": 3}); err != nil {
log.Fatalln(err.Error())
}
opts := options.InsertOne().SetBypassDocumentValidation(true)
if _, err := db.Collection(userCollection).InsertOne(ctx, bson.M{"name": 3, "age": "test"}, opts); err != nil {
log.Fatalln(err.Error())
}
}
执行上面的代码,你会发现不会出现刚才的错误了,但是不建议这样做,因为这样做的话,建立效验规则就无意义了。更多效验相关的内容请参考这里:schema-validation
通过上面的代码,我们已经了解了如何插入一条记录,当然这并不是唯一方式,除了使用扩展包的语法bson来包装数据,也可以直接插入我们定义的结构体,代码如下:
type User struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Age byte `json:"age" bson:"age"`
}
func insertOne2(ctx context.Context, db *mongo.Database) {
user := User{
Name: "test",
Age: 25,
}
if _, err := db.Collection(userCollection).InsertOne(ctx, user); err != nil {
log.Fatalln(err.Error())
}
}
这种方式更方便一点,指定id为omitempty可以让MongoDB自动生成主键ID,如果没有设置这个,就需要调用MongoDB扩展包的方法手动生成主键ID,否则主键ID会是一串无意义的0,代码如下:
import "go.mongodb.org/mongo-driver/bson/primitive"
user := User{
ID: primitive.NewObjectID(),
Name: "test",
Age: 25,
}
上面是单条记录的插入,下面贴两个多条记录的插入:
func insertMany(ctx context.Context, db *mongo.Database) {
users := []interface{}{
User{Name: "test1", Age: 10},
User{Name: "test2", Age: 13},
}
if _, err := db.Collection(userCollection).InsertMany(ctx, users); err != nil {
log.Fatalln(err.Error())
}
users2 := []interface{}{
bson.M{"name": "test3", "age": 15},
bson.M{"name": "test4", "age": 16},
}
if _, err := db.Collection(userCollection).InsertMany(ctx, users2); err != nil {
log.Fatalln(err.Error())
}
}
上面会在数据库users集合插入4条数据,当然,InsertMany也有第三个参数options,它有两个方法,一个设置是否效验字段格式,一个设置批量插入时,一个失败,则都失败的情况。具体options请参考这里:InsertManyOptions
修改
1.修改单条记录
func updateOne(ctx context.Context, db *mongo.Database) {
id, _ := primitive.ObjectIDFromHex("5f226a0ef5d91bc30f112dda")
filter := bson.M{"_id": id}
update := bson.D{{
"$set",
bson.M{"name": "dddd"},
}}
if _, err := db.Collection(userCollection).UpdateOne(ctx, filter, update); err != nil {
log.Fatalln(err.Error())
}
}
上面的代码,我把主键id为5f226a0ef5d91bc30f112dda的记录的name修改成立dddd,如果你的数据库没有这条记录,也没关系,可以指定第三个参数来新增一条记录,如下:
opts := options.Update()
opts.SetUpsert(true)
if _, err := db.Collection(userCollection).UpdateOne(ctx, filter, update, opts); err != nil {
log.Fatalln(err.Error())
}
第三个参数的更多方法,请参考这里:UpdateOptions
2.修改多条记录
type User struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name,omitempty"`
Age byte `json:"age" bson:"age,omitempty"`
}
func updateMany(ctx context.Context, db *mongo.Database) {
filter := bson.M{"age": 3}
update := bson.D{{
"$set",
User{
Name: "aaaaa",
},
}}
if _, err := db.Collection(userCollection).UpdateMany(ctx, filter, update); err != nil {
log.Fatalln(err.Error())
}
}
注意:当使用结构体时,必须在结构体bson标签中加上omitempty,如果不加的话,会修改结构体除主键以外的所有字段。
UpdateMany的第三个参数同UpdateOne一致。
查询
1.查询一条记录
func queryOne(ctx context.Context, db *mongo.Database) {
id, _ := primitive.ObjectIDFromHex("5f226a0ef5d91bc30f112dda")
filter := bson.M{"_id": id}
res := db.Collection(userCollection).FindOne(ctx, filter)
if res.Err() != nil {
log.Fatalln(res.Err().Error())
}
user := User{}
if err := res.Decode(&user); err != nil {
log.Fatalln(err.Error())
}
log.Println(user)
}
FindOne的第三个参数有很多方法来设置参数,简单列两个如下:
- MaxTime
本次查询的最大时间,默认未限制查询时间 - Projection
本次查询返回的字段,默认返回全部字段
其他参数请参考这里:FindOneOptions,你可以根据自己的业务设置具体的参数。
2.查询多条记录
func queryMany(ctx context.Context, db *mongo.Database) {
filter := bson.M{"age": 3}
opts := options.Find()
opts.SetMaxTime(time.Second * 10)
opts.SetLimit(1)
cursor, err := db.Collection(userCollection).Find(ctx, filter, opts)
if err != nil {
log.Fatalln(err.Error())
}
users := []User{}
//users := []bson.M{}
// 第一种方式解析数据
if err = cursor.All(ctx, &users); err != nil {
log.Fatalln(err.Error())
}
//// 第二种方式来解析数据,在解析大数据量时,推荐第二种方式,第一种方式更耗性能
//defer cursor.Close(ctx)
//for cursor.Next(ctx) {
// user := User{}
//// user := bson.M{}
// if err = cursor.Decode(&user); err != nil {
// log.Fatalln(err)
// }
// users = append(users, user)
//}
log.Println(users)
}
上面代码查询age为3的数据,并且限制最大执行时间为10秒,最多返回一条数据。更多设置请参考这里:FindOptions
删除
//删除一条记录
func deleteOne(ctx context.Context, db *mongo.Database) {
id, _ := primitive.ObjectIDFromHex("5f226a0ef5d91bc30f112dda")
filter := bson.M{"_id": id}
if _, err := db.Collection(userCollection).DeleteOne(ctx, filter); err != nil {
log.Fatalln(err.Error())
}
}
//删除多条记录
func deleteMany(ctx context.Context, db *mongo.Database) {
filter := bson.M{"age": 3}
if _, err := db.Collection(userCollection).DeleteMany(ctx, filter); err != nil {
log.Fatalln(err.Error())
}
}
删除也支持第三个参数,请参考这里:DeleteOptions
ok,今天的文章就到这里,简单的介绍了下MongoDB的CRUD,文中若有讲解不当的地方请留言讨论,下篇文章讲下MongoDB的高级特性,敬请期待。
转载请注明出处,谢谢。
- 上一篇: Golang MongoDB扩展包之连接篇
- 下一篇: Golang MongoDB扩展包之高级特性
评论一下