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)
    }
}
示例1
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)
}
示例2
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{}) 是删除所有文档,危险!

posted on 2023-08-03 09:41  自然洒脱  阅读(305)  评论(0编辑  收藏  举报