Skip to content

MongoDB

MongoDB 是一個文檔數據庫,它的基本數據單位就是文檔,存儲格式是 BSON(Binary JSON)一種類似 JSON 的結構,松散的結構可以存儲不同類型的數據,相較於關系數據庫更為靈活,並且使用 js 作為腳本語言,可以通過腳本來完成組合操作。本文主要介紹使用在 Go 中使用官方的 mongo 驅動操作 mongodb 數據庫,並不是 mongodb 教程,如果你沒有 mongo 基礎,請先自行了解和學習。

mongodb 文檔:Introduction to MongoDB — MongoDB Manual

驅動

mongodb 在 go 這方面的庫比較少,早期有些社區維護的庫,後面都停止維護了,不過官方的 mongo 驅動庫已經完全足夠使用了。

開源倉庫:mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

文檔地址:mongodb/mongo-go-driver: The Official Golang driver for MongoDB (github.com)

安裝

下載依賴的話使用下面的地址就行了。

sh
$ go get go.mongodb.org/mongo-driver/mongo

連接

下面是一個簡單的 mongo 客戶端與服務端建立連接的例子。

go
package main

import (
   "context"
   "fmt"
   "go.mongodb.org/mongo-driver/mongo"
   "go.mongodb.org/mongo-driver/mongo/options"
   "go.mongodb.org/mongo-driver/mongo/readpref"
   "log"
)

func main() {
   ctx := context.Background()
   // 使用URI建立連接
   client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://admin:123456@192.168.48.138:27017/"))
   if err != nil {
      log.Panicln(err)
   }
   // 關閉連接
   defer client.Disconnect(ctx)
   // ping測試連接是否可用
   fmt.Println(client.Ping(ctx, readpref.Primary()))
}

bson

mongodb 在 go 裡面使用了以下幾種類型來映射數據庫中的文檔,位於bson/bson.go

go
// BSON文檔的有序表示
type D = primitive.D

// 一對鍵值,BSON文檔的有序表示的基本單位
type E = primitive.E

// BSON文檔的無序表示
type M = primitive.M

// BSON數據的有序表示
type A = primitive.A

它們的實際類型如下

go
// BSON文檔的有序表示
type D []E

// 一對鍵值,BSON文檔的有序表示的基本單位
type E struct {
  Key   string
  Value interface{}
}

// BSON文檔的無序表示
type M map[string]interface{}

// BSON數據的有序表示
type A []interface{}

通過以上幾種類型,即可以構造查詢 SQL,也可以用來映射數據。

TIP

驅動 examples 目錄下有著相當多的使用示例,官方非常詳細的演示了如何使用上述四種類型。

地址:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

查詢文檔

官方查詢示例:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

首先創建 user 數據庫,向集合 users 插入如下數據

sql
> use user
> db.users.insertMany([
    {
        name: "mike",
        age: 12,

    },
    {
        name: "jenny",
        age: 14,

    },
    {
        name: "jack",
        age: 18,
        address: "usa"
    }
])

查詢單個

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var user User

result := client.Database("user"). // 選中數據庫
                    Collection("users").                     // 選中集合
                    FindOne(ctx, bson.D{{"address", "usa"}}) // 過濾條件

// 反序列化
if err := result.Decode(&user); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", user)

上面那段查詢代碼等價於

sql
db.users.findOne({
    address: "usa"
})

輸出結果

{Name:jack Age:18 Address:usa}

查詢多個

go
type User struct {
   Name    string `bson:"name"`
   Age     int    `bson:"age"`
   Address string `bson:"address"`
}

var users []User

cursor, err := client.Database("user"). // 選中數據庫
               Collection("users"). // 選中集合
               Find(ctx, bson.D{})  // 過濾條件

if err != nil {
   log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
   log.Panicln(err)
}

fmt.Printf("%+v\n", users)

等價於

sql
db.users.find({})

輸出

[{Name:jack Age:18 Address:usa} {Name:mike Age:12 Address:} {Name:jenny Age:14 Address:}]

在構造查詢條件的時候,也可以使用 options

go
type User struct {
    Name    string `bson:"name"`
    Age     int    `bson:"age"`
    Address string `bson:"address"`
}

var users []User

find := options.Find()
find.SetSort(bson.M{"age": 1})
find.SetLimit(1)

cursor, err := client.Database("user"). // 選中數據庫
                    Collection("users").      // 選中集合
                    Find(ctx, bson.D{}, find) // 過濾條件

if err != nil {
    log.Panicln(err)
}

if err := cursor.All(ctx, &users); err != nil {
    log.Panicln(err)
}

fmt.Printf("%+v\n", users)

等價於

db.users.find({}).sort({age:1}).limit(1)

輸出

[{Name:mike Age:12 Address:}]

創建文檔

官方創建實例:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

下面是創建一個文檔的例子

go
one, err := client.Database("user").Collection("users").InsertOne(ctx, User{
    Name:    "lili",
    Age:     20,
    Address: "china",
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedID)

創建成功後會返回文檔的 ObjectID

ObjectID("64c60fa01e2548d9e4de6cf4")

下面是創建多個文檔的例子

go
users := []any{User{
    Name:    "john",
    Age:     10,
    Address: "usa",
}, User{
    Name:    "pop",
    Age:     30,
    Address: "uk",
}}

one, err := client.Database("user").Collection("users").InsertMany(ctx, users)
if err != nil {
    log.Panicln(err)
}
fmt.Println(one.InsertedIDs)

創建成功後返回返回一組 ObjectID

[ObjectID("64c610d5aec2618d6ca0b515") ObjectID("64c610d5aec2618d6ca0b516")]

上面兩段代碼就等價於db.users.insertOnedb.users.insertMany

更新文檔

官方更新示例:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

下面是更新單個文檔的示例,將名為 lili 人更名為 mark

go
upres, err := client.Database("user").Collection("users").UpdateOne(ctx, bson.D{
    {"name", "mark"},
},
    bson.D{
       {"$set", bson.D{
          {"name", "lili"},
       }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

等價於

sql
db.users.updateOne({
    name: "lili"
}, {
    $set: {
        name: "mark"
    },
})

輸出

&{MatchedCount:1 ModifiedCount:1 UpsertedCount:0 UpsertedID:<nil>}

下面是更新多個文檔的示例,將年齡為 10 的人地址更新為 cn

go
upres, err := client.Database("user").Collection("users").UpdateMany(ctx, bson.D{
    {"age", 10},
},
    bson.D{
        {"$set", bson.D{
            {"address", "cn"},
        }},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

除了使用Update,mongo 還提供了Replace,兩者的區別在於前者是更新文檔字段,後者是直接替換文檔。例如下面的代碼,就不再需要操作符了。

go
upres, err := client.Database("user").Collection("users").ReplaceOne(ctx, bson.D{
    {"age", 10},
},
    bson.D{
       {"address", "cn"},
    })
if err != nil {
    log.Panicln(err)
}
fmt.Printf("%+v", upres)

同時 mongo 還提供了FindOneAndUpdateFindOneAndReplace來獲取文檔和更新文檔。如下

go
res := client.Database("user").Collection("users").FindOneAndReplace(ctx, bson.D{
    {"address", "cn"},
},
    bson.D{
       {"address", "uk"},
    })
if err := res.Err(); err != nil {
    log.Panicln(err)
}

var user User

res.Decode(&user)

fmt.Printf("%+v", user)

輸出

Name: Age:0 Address:cn}

此操作會先查詢文檔再進行修改文檔。

刪除文檔

官方刪除示例:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

下面是刪除一個文檔的例子

go
result, err := client.Database("user").Collection("users").DeleteOne(ctx, bson.D{
    {"name", "jack"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

下面是刪除多個文檔的例子

go
result, err := client.Database("user").Collection("users").DeleteMany(ctx, bson.D{
    {"age", "10"},
})
if err != nil {
    log.Panicln(err)
}
fmt.Println(result.DeletedCount)

聚合

官方聚合示例:mongo-go-driver/examples/documentation_examples/examples.go at master · mongodb/mongo-go-driver (github.com)

聚合操作會用到mongo.Pipeline類型,它的本質是[]bson.D

go
type Pipeline []bson.D
go
pipline := mongo.Pipeline{
    {
       {"$match", bson.D{
          {"address", "uk"},
       }},
    },
    {
       {"$sort", bson.D{
          {"age", 1},
       }},
    },
}
aggregate, err := client.Database("user").Collection("users").Aggregate(ctx, pipline)
if err != nil {
    log.Panicln(err)
}
var users []User
if err := aggregate.All(ctx, &users); err != nil {
    log.Panicln(err)
}
log.Println(users)

輸出

[{lili 20 uk} {kak 30 uk}]

這段聚合操作就是匹配所有 address 為 uk 的用戶,然後按照年齡排序。

Golang學習網由www.golangdev.cn整理維護