Go操作MongoDB数据库

MongoDB的Go驱动包

"go.mongodb.org/mongo-driver/bson"    //BOSN解析包
"go.mongodb.org/mongo-driver/mongo"    //MongoDB的Go驱动包
"go.mongodb.org/mongo-driver/mongo/options"
BSON简介

BSON是一种类json的一种二进制形式的存储格式,简称Binary JSON。MongoDB使用了BSON这种结构来存储数据和网络数据交换。

BSON对应Document这个概念,因为BSON是schema-free的,所以在MongoDB中所对应的Document也有这个特征,这里的一个Document也可以理解成关系数据库中的一条Record,只是Document的变化更丰富一些,Document可以嵌套。

MongoDB以BSON做为其存储结构的一个重要原因是它的可遍历性

BSON编码扩展了JSON表示,使其包含额外的类型,如int、long、date、浮点数和decimal128。

BSON类型

BSON数据的主要类型有:ADEMRaw。其中,A是数组,D是切片,M是映射,DM是Go原生类型。

  • A类型表示有序的BSON数组。

    bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
    
  • D类型表示包含有序元素的BSON文档。这种类型应该在顺序重要的情况下使用。如果元素的顺序无关紧要,则应使用M代替。

    bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
    
  • M类型表示无序的映射。

    bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
    
  • E类型表示D里面的一个BSON元素。

  • Raw类型代表未处理的原始BSON文档和元素,Raw系列类型用于验证和检索字节切片中的元素。当要查找BSON字节而不将其解编为另一种类型时,此类型最有用。

连接到mongoDB

// 设置mongoDB客户端连接信息
param := fmt.Sprintf("mongodb://XXX.XXX.XXX.XXX:27017")
clientOptions := options.Client().ApplyURI(param)

// 建立客户端连接
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
fmt.Println(err)
}

// 检查连接情况
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
fmt.Println(err)
}
fmt.Println("Connected to MongoDB!")

//指定要操作的数据集
collection := client.Database("ccmsensor").Collection("mtr")

//执行增删改查操作

// 断开客户端连接
err = client.Disconnect(context.TODO())
if err != nil {
log.Fatal(err)
}
fmt.Println("Connection to MongoDB closed.")

增查改删

假如数据库中有一些网络连接数据,来自不同的APP,来自不同的ISP(运营商),类型如下:

type CurlInfo struct {
        DNS float64 `json:"NAMELOOKUP_TIME"` //NAMELOOKUP_TIME
        TCP float64 `json:"CONNECT_TIME"`    //CONNECT_TIME - DNS
        SSL float64 `json:"APPCONNECT_TIME"` //APPCONNECT_TIME - CONNECT_TIME
}

type ConnectData struct {
        Latency  float64  `json:"latency"`
        RespCode int      `json:"respCode"`
        Url      string   `json:"url"`
        Detail   CurlInfo `json:"details"`
}

type Sensor struct {
        ISP       string
        Clientutc int64
        DataByAPP map[string]ConnectData
}
增加

使用collection.InsertOne()来插入一条Document记录:

func insertSensor(client *mongo.Client, collection *mongo.Collection) (insertID primitive.ObjectID) {
        apps := make(map[string]ConnectData, 0)
        apps["app1"] = ConnectData{
                Latency:  30.983999967575,
                RespCode: 200,
                Url:      "",
                Detail: CurlInfo{
                        DNS: 5.983999967575,
                        TCP: 10.983999967575,
                        SSL: 15.983999967575,
                },
        }
        
        record := &Sensor{
                Clientutc: time.Now().UTC().Unix(),
                ISP:       "China Mobile",
                DataByAPP: apps,
        }

        insertRest, err := collection.InsertOne(context.TODO(), record)
        if err != nil {
                fmt.Println(err)
                return
        }
        insertID = insertRest.InsertedID.(primitive.ObjectID)
        return insertID
}
查询

这里引入一个filter来匹配MongoDB数据库中的Document记录,使用bson.D类型来构建filter

timestamp := time.Now().UTC().Unix()
start := timestamp - 180
end := timestamp

filter := bson.D{
{"isp", isp},
{"$and", bson.A{
bson.D{{"clientutc", bson.M{"$gte": start}}},
bson.D{{"clientutc", bson.M{"$lte": end}}},
}},
}

使用collection.FindOne()来查询单个Document记录。这个方法返回一个可以解码为值的结果。

func querySensor(collection *mongo.Collection, isp string) {
        //查询一条记录

        //筛选数据
        timestamp := time.Now().UTC().Unix()
        start := timestamp - 1800
        end := timestamp

        filter := bson.D{
                {"isp", isp},
                {"$and", bson.A{
                        bson.D{{"clientutc", bson.M{"$gte": start}}},
                        bson.D{{"clientutc", bson.M{"$lte": end}}},
                }},
        }

        var original Sensor
        err := collection.FindOne(context.TODO(), filter).Decode(&original)
        if err != nil {
                fmt.Printf("%s\n", err.Error())
        }
        fmt.Printf("Found a single document: %+v\n", original)
}

结果为刚刚插入的那一条数据,

Connected to MongoDB!
Found a single document: {ISP:China Mobile Clientutc:1598867346 DataByAPP:map[app1:{Latency:30.983999967575 RespCode:200 Url: Detail:{DNS:5.983999967575 TCP:10.983999967575 SSL:15.983999967575}}]}
Connection to MongoDB closed.

若要提取其中的数据,在querySensor()方法中略作改动,

original := make(map[string]interface{})
var sensorData Sensor
err := collection.FindOne(context.TODO(), filter).Decode(&original)
if err != nil {
        fmt.Printf("%s\n", err.Error())
} else {
        vstr, okstr := original["isp"].(string)
        if okstr {
        sensorData.ISP = vstr
        }
}
更新

这里仍然使用刚才的filter,并额外需要一个update

update := bson.M{
"$set": bson.M{
"isp": ispAfter,
},
}

使用collection.UpdateOne()更新单个Document记录。

func UpdateSensor(collection *mongo.Collection, ispBefore string, ispAfter string) {
        //修改一条数据

        //筛选数据
        timestamp := time.Now().UTC().Unix()
        start := timestamp - 1800
        end := timestamp

        filter := bson.D{
                {"isp", ispBefore},
                {"$and", bson.A{
                        bson.D{{"clientutc", bson.M{"$gte": start}}},
                        bson.D{{"clientutc", bson.M{"$lte": end}}},
                }},
        }

        //更新内容
        update := bson.M{
                "$set": bson.M{
                        "isp": ispAfter,
                },
        }

        updateResult, err := collection.UpdateOne(context.TODO(), filter, update)
        if err != nil {
                log.Fatal(err)
        }
        fmt.Printf("Matched %v documents and updated %v documents.\n", updateResult.MatchedCount, updateResult.ModifiedCount)
}
删除

使用collection.DeleteOne()来删除一条记录,仍然使用刚才的filter。

deleteResult, err := collection.DeleteOne(context.TODO(), filter)
if err != nil {
        fmt.Printf("%s\n", err.Error())
}

更多操作请见官方文档。

参考链接

Mongo-Driver驱动包官方文档

BSON包官方文档

mongo包官方文档

options包官方文档