MongoDB属于非关系型数据库,它是由C++编写的分布式文档数据库。内部使用类似于Json的bson二 进制格式。
官方文档:https://www.mongodb.com/docs/drivers/go/current/fundamentals/
相关文档:https://www.w3cschool.cn/mongodb/mongodb-1uxs37ih.html
连接
package main import ( "context" "fmt" "log" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const mongo_uri = "mongodb://172.24.233.212:27017/?maxPollSize=20&w=majority&connectTimeoutMS=1000" var ( mongc *mongo.Client err error ) func init() { opts := options.Client().ApplyURI(mongo_uri) // 返回一个客户端配置选项类型 mongc, err = mongo.Connect(context.TODO(), opts) if err != nil { log.Fatal(err) } err = mongc.Ping(context.TODO(), nil) if err != nil { log.Fatal(err) } } func main() { defer func() { if err = mongc.Disconnect(context.TODO()); err != nil { log.Fatal(err) } }() fmt.Println(mongc) }
基本概念
MongoDB中可以创建使用多个库,但有一些数据库名是保留的,可以直接访问这些有特殊作用的数据 库。
admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数 据库或者关闭服务器。
local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
数据封装
结构体定义:https://www.mongodb.com/docs/drivers/go/current/usage-examples/findOne/#find-a-document
type User struct { ID primitive.ObjectID `bson:"_id,omitempty"` Name string Age int } func (u User) String() string { return fmt.Sprintf("<%s: %s,%d>", u.ID, u.Name, u.Age) }
tag参考:https://www.mongodb.com/docs/drivers/go/current/fundamentals/bson/#struct-tags
User结构体中ID一定要使用omitempty,新增时结构体,如果ID不设置则为零值提交,数据库中_id字段 就是一串0。如果设置忽略零值,ID为0提交时会被忽略,数据库则自动生成_id中的id。
ObjectId有12字节组成,参考 bson/primitive/objectid.go/NewObjectID()函数:4字节时间戳 + 5字节进程唯一值 + 3字节随机数,每次加1
package main import ( "context" "fmt" "log" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const mongo_uri = "mongodb://172.24.233.212:27017/?maxPollSize=20&w=majority&connectTimeoutMS=1000" var ( mongc *mongo.Client err error ) type Student struct { Id int `bson:"_id,omitempty"` Name string Age uint8 Address string } func init() { opts := options.Client().ApplyURI(mongo_uri) // 返回一个客户端配置选项类型 mongc, err = mongo.Connect(context.TODO(), opts) if err != nil { log.Fatal(err) } err = mongc.Ping(context.TODO(), nil) if err != nil { log.Fatal(err) } } func main() { defer func() { if err = mongc.Disconnect(context.TODO()); err != nil { log.Fatal(err) } }() u0 := mongc.Database("gopher").Collection("students") // 单条插入 // s0 := Student{Name:"jerry", Age: 13, Address: "美国纽约"} // result, err := u0.InsertOne(context.TODO(), s0) // 多条插入 s1 := []interface{}{ Student{Name: "lily", Age: 23, Address: "西班牙"}, Student{Name: "hellen", Age: 22}, Student{Name: "lucy", Address: "德国慕尼黑"}, } result, err := u0.InsertMany(context.TODO(), s1) if err != nil { fmt.Println(err) } fmt.Println(result) }
BSON
https://www.mongodb.com/docs/drivers/go/current/fundamentals/bson/
MongoDB的Go库提供的构建BSON的数据类型分为4种
D : An ordered representation of a BSON document (slice),表示有序的,切片且元素是二元的
M : An unordered representation of a BSON document (map),表示无序的,map且元素是kv 对
A : An ordered representation of a BSON array
E : A single element inside a D type
查询
bson.D{{"name", "tom"}}
- bson.D是切片,D后的{}表示切片字面量定义
- {"name", "tom"}表示一个结构体实例字面量定义, "name"是结构体的Key属性,类型是string, "tom"是结构体的Value属性,类型是any
bson.M{"name": "tom"}
- bson.M是map,M后的{}表示该map的字面量定义
- map类型为map[string]interface{}
单条查询
package main import ( "context" "fmt" "log" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) const mongo_uri = "mongodb://172.24.233.212:27017/?maxPollSize=20&w=majority&connectTimeoutMS=1000" var ( mongc *mongo.Client err error ) type Student struct { Id primitive.ObjectID `bson:"_id,omitempty"` Name string Age uint8 Address string } func init() { opts := options.Client().ApplyURI(mongo_uri) // 返回一个客户端配置选项类型 mongc, err = mongo.Connect(context.TODO(), opts) if err != nil { log.Fatal(err) } err = mongc.Ping(context.TODO(), nil) if err != nil { log.Fatal(err) } } func main() { defer func() { if err = mongc.Disconnect(context.TODO()); err != nil { log.Fatal(err) } }() u0 := mongc.Database("gopher").Collection("students") // 查询条件,利用bson来构建,bson.D是类型 var r Student err = u0.FindOne(context.TODO(), bson.D{}).Decode(&r) if err != nil { // 查无此文档错误类型判断 if err == mongo.ErrNoDocuments { return } fmt.Println(err) } fmt.Println("@@@", r) }
返回结果:
多条查询
type Student struct { Id primitive.ObjectID `bson:"_id,omitempty"` Name string Age uint8 Address string } func findMany() { // bson.D可以理解为切片,bson.M可以理解为map // filtd := bson.D{{Key: "name", Value: "tom"}, {Key: "age", Value: 29}} // filtm := bson.M{"name": "tom", "age": 29} // 根据id来查询时,需要先将字符串类型的id转换成objectid类型 id, err := primitive.ObjectIDFromHex("64c75a6770ed7b94a7960212") if err != nil { log.Fatal(err) } filtd := bson.D{{"_id", id}} cursor, err := stds.Find(context.TODO(), filtd) if err != nil { log.Fatal(err) } var s0 Student defer cursor.Close(context.TODO()) for cursor.Next(context.TODO()) { err = cursor.Decode(&s0) if err != nil { log.Fatal(err) } fmt.Println(s0) } }
type Student struct { Id primitive.ObjectID `bson:"_id,omitempty"` Name string Age uint8 Address string } func findMany2() { filtd := bson.D{{"name", "tom"}} cursor, err := stds.Find(context.TODO(), filtd) if err != nil { log.Fatal(err) } var s0 []Student err = cursor.All(context.TODO(), &s0) if err != nil { log.Fatal(err) } fmt.Println(s0) }
func findByFilter(filter any) { cursor, err := stds.Find(context.TODO(), filter) if err != nil { log.Fatal(err) } // 关闭游标 defer cursor.Close(context.TODO()) var s0 []Student err = cursor.All(context.TODO(), &s0) if err != nil { log.Fatal(err) } fmt.Println(s0) } func main() { //多条查询,条件前用$,gt/lt/eq/ne/gte/lte,多个条件为and关系 // filtd := bson.D{{"age", bson.M{"$gt": 20}}} // filtm := bson.M{"age": bson.D{{"$ne", 23}}} // in和notin逻辑 // filtm := bson.M{"name": bson.M{"$in": []string{"tom", "lily"}}} // filtd := bson.D{{"name", bson.D{{"$nin", []string{"lucy", "hellen"}}}}} // and、or逻辑,注意array类型 // filtd := bson.D{{"$or", []bson.M{{"name": "tom"}, {"age": bson.M{"$gt": 20}}}}} // 查询时,字段的类型与值要对应,如果下面的age的值29为string,将查找为空 // filtm := bson.M{"$and": []bson.M{{"name": "tom"}, {"age": 29}}} // not逻辑,写法有点绕 filtm := bson.M{"name": bson.M{"$not": bson.M{"$eq": "tom"}}} findByFilter(filtm) }
比较符号 | 含义 | filter示例 |
$lt | 小于 | bson.M{"age": bson.M{"$lt": 20}} |
$gt | 大于 | bson.M{"age": bson.M{"$gt": 20}} |
$lte | 小于等于 |
bson.M{"age": bson.M{"$lte": 20}} bson.D{{"age", bson.D{{"$lte", 20}}}} |
$gte | 大于等于 | bson.M{"age": bson.M{"$gte": 20}} |
$ne | 不等于 | bson.M{"age": bson.M{"$ne": 20}} |
$eq | 等于,默认为等于 |
bson.M{"age": bson.M{"$eq": 20}} bson.M{"age": 20} |
$in | 在范围内 | bson.M{"age": bson.M{"$in": []int{16, 33}}} |
$nin | 不在范围内 | bson.M{"age": bson.M{"$nin": []int{16, 33}}} |
https://www.mongodb.com/docs/manual/reference/operator/query/and/
逻辑符号 | 含义 | 示例 |
$and | 与 |
bson.M{"$and": []bson.M{{"name": "tom"}, {"age": 33}}} bson.M{"$and": []bson.M{{"name": "tom"}, {"age": bson.M{"$gt": 40}}}} |
$or | 或 | bson.M{"$or": []bson.M{{"name": "tom"}, {"age": bson.M{"$lt": 20}}}} |
$not | 非 |
bson.M{"age": bson.M{"$not": bson.M{"$gte": 20}}} bson.M{"age": bson.M{"$gte": 20}} 取反为 bson.M{"age": bson.M{"$not": bson.M{"$gte": 20}}} |
元素 | 含义 | 示例 |
$exists | 文档中是否有这个字段 | bson.M{"Name": bson.M{"$exists": true}} |
$type | 字段是否是指定的类型 | bson.M{"age": bson.M{"$type": 16}} |
bson.M{"name": bson.M{"$exists": true}} 标识所有具有Name字段的文档,注意Name和name 不一样。
常用类型,参考 https://docs.mongodb.com/manual/reference/operator/query/type/#op._S_type
- 字符串类型编码为2,别名为string
- 整型编码为16,别名为int
- 长整型编码为18,别名为long
投影
func findByOpts(filter any, opts *options.FindOptions) { cursor, err := stds.Find(context.TODO(), filter, opts) if err != nil { log.Fatal(err) } var s0 Student defer cursor.Close(context.TODO()) for cursor.Next(context.TODO()) { err = cursor.Decode(&s0) if err != nil { log.Fatal(err) } fmt.Println(s0) } } func main() { // limit+投影,true、1为-1、false相反 opts := options.Find().SetLimit(3).SetProjection(bson.M{"name": true, "age": 1}) // 排序,1为升序,-1为降序,其他报错 opts.SetSort(bson.M{"age": -1}) filtm := bson.M{"age": bson.M{"$gt": 23}} findByOpts(filtm, opts) }
分页
opt.SetSkip(1) // offset opt.SetLimit(1) // limit
更新
更新操作符 | 含义 | 示例 |
$inc | 对给定字段数字值增减 | bson.M{"$inc": bson.M{"age": -5}} |
$set | 设置字段值,如果字段不存在则创 建 | bson.M{"$set": bson.M{"gender": "M"}} |
$unset | 移除字段 | {'$unset':{'Name':""}} |
// 更新一条 func updateOne(filter any, update any) { // 更新一条,即使条件能匹配多条,也会匹配一条更新 r, err := stds.UpdateOne(context.TODO(), filter, update) if err != nil { log.Fatal(err) } fmt.Printf("匹配到%v条; 更新了%v条\n", r.MatchedCount, r.ModifiedCount) } // 更新多条 func updateMany(filter any, update any) { r, err := stds.UpdateMany(context.TODO(), filter, update) if err != nil { log.Fatal(err) } fmt.Printf("匹配到%v条; 更新了%v条\n", r.MatchedCount, r.ModifiedCount) } func main() { // filter := bson.M{"age": bson.M{"$gt": 20}} // update := bson.M{"$set": bson.M{"address": "北京"}} // filter := bson.M{"name": "lucy", "age": 0} // update := bson.M{"$unset": bson.M{"address": ""}} // updateOne(filter, update) filter := bson.M{"name": "lucy"} // $inc自增减配置 update := bson.M{"$set": bson.M{"address": "上海"}, "$inc": bson.M{"age": -3}} updateMany(filter, update) }
替换
type Stud struct { Name string Age int } func replace(filter any, replacement any) { // 更新并返回更新前的文档 // r := stds.FindOneAndReplace(context.TODO(), filter, replacement) // o := Student{} // r.Decode(&o) // fmt.Println(o) r, err := stds.ReplaceOne(context.TODO(), filter, replacement) if err != nil { log.Fatal(err) } fmt.Printf("匹配到%v条; 替换了%v条\n", r.MatchedCount, r.ModifiedCount) } func main() { // 替换,其实是用实例来更新 s0 := &Stud{Name: "kaluo", Age: 99} filter := bson.M{"name": "lily"} replace(filter, s0) }
删除
// 删除一个 func deleteOne() { filter := bson.M{} // 没有条件,匹配所有文档 dr, err := users.DeleteOne(context.TODO(), filter) if err != nil { log.Fatal(err) } fmt.Println(dr.DeletedCount) } // 删除多个 func deleteMany() { filter := bson.M{} // 没有条件,匹配所有文档 dr, err := users.DeleteMany(context.TODO(), filter) if err != nil { log.Fatal(err) } fmt.Println(dr.DeletedCount) }
users.DeleteMany(context.TODO(), bson.M{}) 是删除所有文档,危险!