惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
L
LangChain Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
月光博客
月光博客
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
美团技术团队
S
Security @ Cisco Blogs
人人都是产品经理
人人都是产品经理
L
LINUX DO - 热门话题
S
SegmentFault 最新的问题
D
DataBreaches.Net
C
CXSECURITY Database RSS Feed - CXSecurity.com
Microsoft Security Blog
Microsoft Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
N
News | PayPal Newsroom
W
WeLiveSecurity
F
Fortinet All Blogs
The Hacker News
The Hacker News
The Register - Security
The Register - Security
P
Palo Alto Networks Blog
Engineering at Meta
Engineering at Meta
T
The Exploit Database - CXSecurity.com
Vercel News
Vercel News
G
GRAHAM CLULEY
博客园 - 聂微东
P
Privacy International News Feed
P
Privacy & Cybersecurity Law Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
G
Google Developers Blog
T
Tailwind CSS Blog
V
Vulnerabilities – Threatpost
L
Lohrmann on Cybersecurity
T
Tenable Blog
Cisco Talos Blog
Cisco Talos Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Security Affairs
云风的 BLOG
云风的 BLOG
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
T
Threatpost

博客园 - 自然洒脱

MiniMax 权益码 Token Plan 套餐 9 折优惠 go语言context包 go语言通道 go语言GMP模型 go语言gorm的CRUD go语言gorm go语言mysql驱动 go语言log相关 go语言包管理 go语言时间相关 go语言序列化和反序列化 go语言的"面向对象" go语言结构体排序 go语言异常处理 go语言接口 go语言结构体(二) go语言结构体(一) go语言递归函数及defer go语言函数作用域及匿名函数
go语言mongodb操作
自然洒脱 · 2023-08-03 · via 博客园 - 自然洒脱

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