Go进阶系列

3 Go 数据库IO(三):MongoDB 操作

2019-07-08  本文已影响0人  GoFuncChan

一、Mongodb概述

我们都知道各种基于SQL的关系型数据库管理系统,如Mysql、Postgresql、sqlite。在现实世界中,结构化数据存储是网络和商务应用的主导技术,通过设计严格的关系模型构建业务系统是基本的设计方法。但是除了关系型数据库外,还有反其道而行的NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL"。

这类非结构化的数据库系统对超大规模数据的存储有着非常好的实践效果,其中Mongodb便是NoSql的热门应用,基于JavaScript引擎的bson结构和类似SQL的查询语法让其对大多数开发人员使用十分友好。Mongodb基于文档的结构能够灵活的存储非结构化的各种数据,如果你对json较熟悉,便会更容易入手,因为其文档内部的数据结构和json别无二致,操作mongodb文档和操作json一样简单。

这里提供中文的Mongodb使用手册,感兴趣可学习:https://www.mongodb.org.cn/tutorial/

二、Go 使用Mongodb

1.mgo

在Go中操作Mongodb需使用第三方库,之前github上比较热门的三方库为mgo,其作者已经暂停维护,但因其易用性使用的人还是相当多的,下面给出使用示例:

首先导入相关包:
import (
    "fmt"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)

预先定义一个存储的文档结构:
//定义一个mongodb collection:student的文档结构
type MongoStudent struct {
    Id_   bson.ObjectId `bson:"_id"`
    Name  string        `bson:"name"`
    Phone string        `bson:"phone"`
    Email string        `bson:"email"`
    Sex   string        `bson:"sex"`
}

连接mongodb数据库并执行CRUD:要巧用 bson.M{} 这个查询构造struct

//拨号连接mongodb
session, err := mgo.Dial("localhost:27017/godemo")
ErrorHandler(err, "mgo.Dial()")
defer session.Close()
fmt.Println("连接mongodb成功...")

//取得一个collection操作句柄
stuCol := session.DB("godemo").C("student")

//查看student表的文档数
n, err := stuCol.Count()
ErrorHandler(err, "stuCol.Count()")
fmt.Println("Student文档数:", n)

增删改操作:

//1.1 新增文档 For Struct
stu1 := MongoStudent{bson.NewObjectId(), "Fun", "132xxxxxxxx", "924035827@qq.com", "Male"}
stu2 := MongoStudent{bson.NewObjectId(), "John", "186xxxxxxxx", "1012910232@qq.com", "Female"}
stu3 := MongoStudent{bson.NewObjectId(), "Jack", "150xxxxxxxx", "1012910232@qq.com", "Female"}
stu4 := MongoStudent{bson.NewObjectId(), "KKK", "133xxxxxxxx", "123456768@qq.com", "Male"}
err = stuCol.Insert(&stu1, &stu2, &stu3, &stu4)
ErrorHandler(err,"插入文档错误!!!")
fmt.Println("插入结构体数据成功!")

//1.2 新增文档 For map
stuMap := make(map[string]interface{},0)
stuMap["name"] = "fun map"
stuMap["age"] = 18
stuMap["phone"] = "156xxxxxxxx"
stuMap["email"] = "1234456324@qq.com"
stuMap["gender"] = "male"
err = stuCol.Insert(&stuMap)
ErrorHandler(err,"插入文档错误!!!")
fmt.Println("插入结构体数据成功!")

//2.1 修改文档
selector := bson.M{"_id": bson.ObjectIdHex("5c860a823ca58305282be41b")} //构造更新选择器
updator := bson.M{"$set": bson.M{"phone": "1111111111"}}    构造更新数据集
err = stuCol.Update(selector, updator)
ErrorHandler(err,"stuCol.Update")
fmt.Println("stuCol.Update Success!")

err = stuCol.UpdateId(bson.ObjectIdHex("5c860a823ca58305282be41b"), bson.M{"$set": bson.M{"age": 30}})
ErrorHandler(err,"stuCol.Update")
fmt.Println("stuCol.UpdateId Success!")

changeInfo, err := stuCol.UpdateAll(bson.M{"sex": "Female"}, bson.M{"$set": bson.M{"sex": "Male"}})
ErrorHandler(err,"stuCol.UpdateAll")
fmt.Printf("更新情况:匹配%d,删除%d,更新%d,更新插入Id:%s",changeInfo.Matched,changeInfo.Removed,changeInfo.Updated,changeInfo.UpsertedId)

//2.2 更新没有则插入文档
changeInfo, err := stuCol.Upsert(bson.M{"sex": "Female"}, bson.M{"name": "Simee", "phone": "15014064202", "sex": "Female"})
ErrorHandler(err,"stuCol.Upsert")
fmt.Printf("更新情况:匹配%d,删除%d,更新%d,更新插入Id:%s",changeInfo.Matched,changeInfo.Removed,changeInfo.Updated,changeInfo.UpsertedId)

//3.删除文档
err = stuCol.RemoveId(bson.ObjectIdHex("5c8610693ca58305282be4a2"))
ErrorHandler(err,"stuCol.RemoveId")
fmt.Println("删除成功!")

stuCol.Remove()
stuCol.RemoveAll()

大容量操作Bulk(),适合大量增删改:

//获得bulk操作句柄,insert/update/remove等操作,然后run()执行
bulk := stuCol.Bulk()
bulk.Insert()
bulk.Update()
bulk.UpdateAll()
bulk.Remove()
bulk.RemoveAll()
bulk.Upsert()

bulk.Run()

//返回集合的所有索引的列表
indexes, err := stuCol.Indexes()
fmt.Println("索引列表:",indexes)

mgo中的查询操作,使用查询构造器,查询构造器方法:

查询用例:

//查询文档 通过collection.Find(),返回一个查询构造器,再用查询构造器取构建查询语句,使用One(&map)或All(&[]map)取接收查询结果
//6.1 根据Id查询
stuQuery := stuCol.FindId(bson.ObjectIdHex("5c8608a7baa5203fc198859f"))

//6.2 根据条件查询,连接One()函数查一个
oneData := make(map[string]interface{}) //定义一个接收器
err = stuCol.Find(bson.M{"_id":bson.ObjectIdHex("5c8608a7baa5203fc198859f")}).One(&oneData)
ErrorHandler(err,"Find().One()")
fmt.Println("查询一个结果:",oneData)

//6.3 根据条件查询,连接All()函数查多个
AllData := make([]map[string]interface{},0)
err = stuCol.Find(bson.M{"sex": "Male"}).All(&AllData)
ErrorHandler(err,"Find().All()")
fmt.Println("查询一个结果:",AllData)

//6.4 条件查询
QueryData := make([]map[string]interface{},0)
stuCol.Find(bson.M{}).Limit(2).All(&QueryData)
stuCol.Find(bson.M{}).Sort("name","-age").Skip(10).Limit(10)
Mongodb连接池

我们知道数据库IO是整个系统的瓶颈,一般我们会在数据层上构建缓存层以缓解数据库压力,除此之外,我们还需要主动限制数据库的连接数,以防访问冲破缓存限制抵达数据库过多的情况,所以生产环境构建连接池非常重要,以下为go编写一个连接池的用法:

mgo连接池是自带的,其每次获取一个连接,都是初始连接的一个Session拷贝,每次用完把拷贝的Session关闭即可。为防止编码过程中忘记关闭的情况,我们编写一个高阶函数M,每次使用mongo时调用M函数即可,其入参函数中编写mongo的操作,M函数自动管理连接的关闭。

//go mongodb 连接池
func BaseMongodbPool() {

    //定义一个数据接收器
    oneData := make(map[string]interface{}) //定义一个接收器

    //每执行一个M函数都从连接池拷贝一个连接,闭包内有集合collection的操作句柄实例,直接在闭包里mongo操作
    M("student", func(collection *mgo.Collection) {
        err := collection.FindId(bson.ObjectIdHex("5c8608a7baa5203fc198859f")).One(&oneData)
        ErrorHandler(err, "Find().One()")
        fmt.Println("查询一个结果:", oneData)
    })

}

/*
mongo连接池实现:
由于mgo连接池是自带的,你只需要使用session.Copy()拿到一个复制的session,用完之后session.Close()即可。

*/
const (
    USER string = "user"
    MSG  string = "msg"
)

var (
    session      *mgo.Session
    databaseName = "godemo"
)

func Session() *mgo.Session {
    if session == nil {
        var err error
        session, err = mgo.Dial("localhost")
        if err != nil {
            panic(err) // no, not really
        }
    }
    return session.Clone()
}

func M(collection string, f func(*mgo.Collection)) {
    //申请一个mongodb连接拷贝
    session := Session()
    //使用完即释放连接
    defer func() {
        session.Close()
        if err := recover(); err != nil {
            ErrorHandler(err.(error), "M")
        }
    }()

    //返回一个collection连接闭包
    c := session.DB(databaseName).C(collection)
    f(c)
}

2.mongo-go-driver

如果你需要一个长期维护的mongo驱动,目前mongo官方推出go驱动 https://github.com/mongodb/mongo-go-driver, 其已经有了用例说明。

至此Mongodb的操作专题就介绍到这,关于Mongo的实战我会在实战专题推出爬虫项目,敬请关注吧!

上一篇下一篇

猜你喜欢

热点阅读