Golang MongoDB扩展包之CR...

胡大大 2020-07-30 13:58:30 91 0 comments

前言

上篇文章主要写了下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()) } }

image.png
然后查看数据库结果,你会发现有两条记录,它们的类型不一样,第一条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的代码,发现只插入了一条数据,第二条报错了,就是因为格式不对。
image.png
有什么办法能避免这个错误吗?当然有解决方案,那就是不效验数据格式,指定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) }

image.png
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) }

image.png
上面代码查询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的高级特性,敬请期待。
转载请注明出处,谢谢。



标签
评论一下

评论列表

暂时没有评论,快来评论吧..