Языковая операция Go mongodb

MongoDB

предисловие

Недавно я научился работать с mongodb в go и узнал, что в основном есть две библиотеки: сторонняя mgo и официальный mongo-драйвер, которые используются чаще всего. mgo прекратил обслуживание, поэтому выбран mongo-driver. В этой статье записаны некоторые часто используемые примечания к операциям с кодом для справки в любое время.

Установить

# 初始化go模块,取名为mongo-notes
mkdir goMongo && cd goMongo
go mod init mongo-notes

# 获取go mongo模块依赖
go get go.mongodb.org/mongo-driver/mongo

Подключение клиента

Существует два основных способа подключения клиента:

  1. Сначала создайте экземпляр клиента, а затем подключитесь;
  2. Получите экземпляр клиента при прямом подключении.

Обычно используется второй способ, код более лаконичен.

правильноmongoлюбая операция, в том числеConnect,CURD,Disconnectи т. д. неотделимы от контекста операцииContextсреды, требуетContextЭкземпляр в качестве первого параметра операции.

Подключиться с помощью экземпляра NewClient

package main

import (
  "context"
  "fmt"
  "time"

  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
  client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
  if err != nil {
    fmt.Errorf("client establish failed. err: %v", err)
  }
  // ctx
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  defer cancel()

  // connect
  if err = client.Connect(ctx); err == nil {
    fmt.Println("connect to db success.")
  }

  // 实例化client后,延迟调用断开连接函数
  defer func() {
    if err = client.Disconnect(ctx); err != nil {
      panic(err)
    }
  }()
}

Подключиться с помощью функции mongo.Connect()

прямая связь

package main

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

func main() {
  clientOpts := options.Client().ApplyURI("mongodb://localhost:27017/?connect=direct")
  client, err := mongo.Connect(context.TODO(), clientOpts)
  if err != nil {
      log.Fatal(err)
  }
}

Соединение с аутентификацией по имени пользователя и паролю

package main

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

func main() {
  credential := options.Credential{

      Username: "username",
      Password: "password",
  }
  clientOpts := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(credential)
  // 上述可以直接使用带用户名和密码的uri连接
  // clientOpts := options.Client().ApplyURI("mongodb://username:password@localhost:27017")
  client, err := mongo.Connect(context.TODO(), clientOpts)
  if err != nil {
      log.Fatal(err)
  }
}

подключение набора реплик replicaSet

package main

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

func main() {
  clientOpts := options.Client().ApplyURI("mongodb://localhost:27017,localhost:27018/?replicaSet=replset")
  client, err := mongo.Connect(context.TODO(), clientOpts)
  if err != nil {
      log.Fatal(err)
  }
}

осколочное соединение осколка

package main

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

func main() {
  clientOpts := options.Client().ApplyURI("mongodb://localhost:27017,localhost:27018")
  client, err := mongo.Connect(context.TODO(), clientOpts)
  if err != nil {
      log.Fatal(err)
  }
}

подключение конфигурации модуля

В реальных проектах обычно есть конфигурационный модуль, поэтому наиболее часто используемое соединение — запись конфигурации соединения в специализированный файл конфигурационного модуля:

package config

import (
  "time"

  "go.mongodb.org/mongo-driver/mongo/options"
  "go.mongodb.org/mongo-driver/mongo/readpref"
)

// MONGO SETTINGS
var (
  credentials = options.Credential{
    AuthMechanism: "SCRAM-SHA-1",
    AuthSource:    "anquan",
    Username:      "ysj",
    Password:      "123456",
  }
  // direct                = true
  connectTimeout        = 10 * time.Second
  hosts                 = []string{"localhost:27017", "localhost:27018"}
  maxPoolSize    uint64 = 20
  minPoolSize    uint64 = 5
  readPreference        = readpref.Primary()
  replicaSet            = "replicaSetDb"

  // ClientOpts mongoClient 连接客户端参数
  ClientOpts = &options.ClientOptions{
    Auth:           &credentials,
    ConnectTimeout: &connectTimeout,
    //Direct:         &direct,
    Hosts:          hosts,
    MaxPoolSize:    &maxPoolSize,
    MinPoolSize:    &minPoolSize,
    ReadPreference: readPreference,
    ReplicaSet:     &replicaSet,
  }
)

Хотя в написанном выше нет ничего плохого, оно кажется слишком раздутым, и можно использовать более элегантный и лаконичный цепной вызов:

package config

import (
	"time"

	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
)
// ClientOpts mongoClient 连接客户端参数
var ClientOpts = options.Client().
  SetAuth(options.Credential{
      AuthMechanism: "SCRAM-SHA-1",
      AuthSource:    "anquan",
      Username:      "ysj",
      Password:      "123456",
  }).
  SetConnectTimeout(10 * time.Second).
  SetHosts([]string{"localhost:27017"}).
  SetMaxPoolSize(20).
  SetMinPoolSize(5).
  SetReadPreference(readpref.Primary()).
  SetReplicaSet("replicaSetDb")

Затем импортируйте конфигурацию в основной модуль

package main

import (
  "context"
  "log"
  "mongo-notes/config" // 引入配置模块

  "go.mongodb.org/mongo-driver/mongo"
)

func main(){
  client, err := mongo.Connect(context.TODO(), config.ClientOpts)
  if err != nil {
    log.Fatal(err)
  }
}

Творожная операция

Познакомьтесь с Бсоном первым

использоватьmongo-driverдействоватьmongodbНеобходимо использовать предоставленный этим модулемbson. Он в основном используется для записи фильтра условия фильтра запроса, построения записи документа и получения значения декодирования запроса, то есть между go и mongo.序列化. Среди них в основном используются только следующие три структуры данных:

  • bson.D{}: для документа (Документ)аккуратныйОписание, ключ-значение через запятую;
  • bson.M{}: структура карты, ключ-значение, разделенное двоеточием,беспорядок, максимально удобный в использовании;
  • bson.A{}: Структура массива, требования к элементамаккуратныйОписание документа, то есть элементbson.D{}Типы.

получить дб

// 获取db
db := client.Database("test")
collectionNames, err := db.ListCollectionNames(ctx, bson.M{})
fmt.Println("collectionNames:", collectionNames)

получить коллекцию

// 获取collecton
collection := db.Collection("person")
fmt.Printf("collection: %s \n", collection.Name())

создать индекс

// 创建索引
indexView := collection.Indexes()
indexName := "index_by_name"
indexBackground := true
indexUnique := true

indexModel := mongo.IndexModel{
  Keys: bson.D{{"name", 1}},
  Options: &options.IndexOptions{
    Name:       &indexName,
    Background: &indexBackground,
    Unique:     &indexUnique,
  },
}
index, err := indexView.CreateOne(ctx, indexModel)
if err != nil {
  log.Fatalf("index created failed. err: %v \n", err)
  return
}
fmt.Println("new index name:", index)

индекс просмотра

// 查看索引
indexCursor, err := collection.Indexes().List(ctx)
var indexes []bson.M
if err = indexCursor.All(ctx, &indexes); err != nil {
  log.Fatal(err)
}
fmt.Println(indexes)

Insert

InsertOne

collection := client.Database("test").Collection("person")
// InsertOne
insertOneResult, err := collection.InsertOne(ctx, bson.M{"name": "虎子", "gender": "男", "level": 1})
if err != nil {
  log.Fatal(err)
}
fmt.Println("id:", insertOneResult.InsertedID)

InsertMany

// InsertMany
docs := []interface{}{
  bson.M{"name": "5t5", "gender": "男", "level": 0},
  bson.M{"name": "奈奈米", "gender": "男", "level": 1},
}
// Ordered 设置为false表示其中一条插入失败不会影响其他文档的插入,默认为true,一条失败其他都不会被写入
insertManyOpts := options.InsertMany().SetOrdered(false)
insertManyResult, err := collection.InsertMany(ctx, docs, insertManyOpts)
if err != nil {
  log.Fatal(err)
}
fmt.Println("ids:", insertManyResult.InsertedIDs)

Find

Find

// Find
findOpts := options.Find().SetSort(bson.D{{"name", 1}})
findCursor, err := collection.Find(ctx, bson.M{"level": 0}, findOpts)
var results []bson.M
if err = findCursor.All(ctx, &results); err != nil {
  log.Fatal(err)
}
for _, result := range results {
  fmt.Println(result)
}

FindOne

// FindOne

// 可以使用struct来接收解码结果

// var result struct {
// 	Name   string
// 	Gender string
// 	Level  int
// }

// 或者更好的是直接使用bson.M
var result bson.M

// 按照name排序并跳过第一个, 且只需返回name、level字段
findOneOpts := options.FindOne().SetSkip(1).SetSort(bson.D{{"name", 1}}).SetProjection(bson.D{{"name", 1}, {"level", 1}})

singleResult := collection.FindOne(ctx, bson.M{"name": "5t5"}, findOneOpts)
if err = singleResult.Decode(&result); err == nil {
  fmt.Printf("result: %+v\n", result)
}

FindOneAndDelete

// FindOneAndDelete
findOneAndDeleteOpts := options.FindOneAndDelete().SetProjection(bson.D{{"name", 1}, {"level", 1}})
var deletedDoc bson.M
singleResult := collection.FindOneAndDelete(ctx, bson.D{{"name", "虎子"}}, findOneAndDeleteOpts)
if err = singleResult.Decode(&deletedDoc); err != nil {
  if err == mongo.ErrNoDocuments {
      return
  }
  log.Fatal(err)
}
fmt.Printf("deleted document: %+v \n", deletedDoc)

FindOneAndReplace

// FindOneAndReplace
// 注意: 返回的是被替换前的document,满足条件的doc将会被完全替换为replaceMent
_id, err := primitive.ObjectIDFromHex("5fde05b9612cb3d19c4b25e8")
findOneAndReplaceOpts := options.FindOneAndReplace().SetUpsert(true)
replaceMent := bson.M{"name": "5t5", "skill": "六眼"}
var replaceMentDoc bson.M
err = collection.FindOneAndReplace(ctx, bson.M{"_id": _id}, replaceMent , findOneAndReplaceOpts).Decode(&replaceMentDoc)
if err != nil {
  if err==mongo.ErrNoDocuments {
    return
  }
  log.Fatal(err)
}
fmt.Printf("document before replacement: %v \n", replaceMentDoc)

FindOneAndUpdate

// FindOneAndUpdate
// 注意:返回的结果仍然是更新前的document
_id, err := primitive.ObjectIDFromHex("5fde05b9612cb3d19c4b25e8")
findOneAndUpdateOpts := options.FindOneAndUpdate().SetUpsert(true)
update := bson.M{"$set": bson.M{"level": 0, "gender": "男"}}
var toUpdateDoc bson.M
err = collection.FindOneAndUpdate(ctx, bson.M{"_id": _id}, update, findOneAndUpdateOpts).Decode(&toUpdateDoc)
if err != nil {
  if err == mongo.ErrNoDocuments {
    return
  }
  log.Fatal(err)
}
fmt.Printf("document before updating: %v \n", toUpdateDoc)
}

Update

UpdateOne

//UpdateOne
updateOneOpts := options.Update().SetUpsert(true)
updateOneFilter := bson.M{"name": "娜娜明"}
updateOneSet := bson.M{"$set": bson.M{"skill": "三七分"}}
updateResult, err := collection.UpdateOne(ctx, updateOneFilter, updateOneSet, updateOneOpts)
fmt.Printf(
  "matched: %d  modified: %d  upserted: %d  upsertedID: %v\n",
  updateResult.MatchedCount,
  updateResult.ModifiedCount,
  updateResult.UpsertedCount,
  updateResult.UpsertedID,
)

UpdateMany

// UpdateMany
updateManyFilter := bson.M{"name": "虎子"}
updateManySet := bson.M{"$set": bson.M{"level": 1}}
updateManyResult, err := collection.UpdateMany(ctx,updateManyFilter, updateManySet)
fmt.Printf(
  "matched: %d  modified: %d  upserted: %d  upsertedID: %v\n",
  updateManyResult.MatchedCount,
  updateManyResult.ModifiedCount,
  updateManyResult.UpsertedCount,
  updateManyResult.UpsertedID,
)

ReplaceOne

// ReplaceOne
// 就影响数据层面和FindOneAndReplace没有差别
replaceOpts := options.Replace().SetUpsert(true)
replaceMent := bson.M{"name": "小黑", "level": 2, "gender": "男"}
updateResult, err := collection.ReplaceOne(ctx, bson.M{"name": "虎子"}, replaceMent, replaceOpts)
fmt.Printf(
  "matched: %d  modified: %d  upserted: %d  upsertedID: %v\n",
  updateResult.MatchedCount,
  updateResult.ModifiedCount,
  updateResult.UpsertedCount,
  updateResult.UpsertedID,
)

Delete

DeleteOne

// DeleteOne
deleteOneOpts := options.Delete().SetCollation(&options.Collation{
  // 忽略大小写
  CaseLevel: false,
})
deleteResult, err := collection.DeleteOne(ctx, bson.D{{"name", "虎子"}}, deleteOneOpts)
fmt.Println("deletet count:", deleteResult.DeletedCount)

DeleteMany

// DeleteMany
deleteManyOpts := options.Delete().SetCollation(&options.Collation{
  // 忽略大小写
  CaseLevel: false,
})
deleteManyResult, err := collection.DeleteMany(ctx, bson.D{{"name", "虎子"}}, deleteOneOpts)
fmt.Println("deletet count:", deleteManyResult.DeletedCount)

Пакетная операция BulkWrite

// BulkWrite
names := []string{"5t5", "娜娜明", "小黑", "蔷薇", "虎子"}
models := []mongo.WriteModel{}
updateOperation := bson.M{"$set": bson.M{"Animation": "咒术回战"}}
for _, name := range names {
  updateOneModel := mongo.NewUpdateOneModel().SetFilter(bson.M{"name": name}).SetUpdate(updateOperation).SetUpsert(true)
  models = append(models, updateOneModel)
}
bulkWriteOpts := options.BulkWrite().SetOrdered(false)
bulkWriteResults, err := collection.BulkWrite(ctx, models, bulkWriteOpts)
if err != nil {
  log.Fatal(err)
}
fmt.Printf(
  "matched: %d  modified: %d  upserted: %d  upsertedIDs: %v\n",
  bulkWriteResults.MatchedCount,
  bulkWriteResults.ModifiedCount,
  bulkWriteResults.UpsertedCount,
  bulkWriteResults.UpsertedIDs,
)

транзакция транзакция

Транзакции гарантируют, что наши операции либо завершатся успешно, либо потерпят неудачу. Самое большое преимущество использования транзакций заключается в том, чтобы избежать влияния сбоя определенной операции на существующую среду базы данных.Однажды я слышал, как друг жаловался, что, поскольку транзакции не используются, после сбоя операции данные могут быть удалены только один за другим в производственная база данных. Таким образом, рекомендуется использовать транзакции, хотя код будет выглядеть немного раздутым.

mongo-driverтранзакции необходимо использоватьsessionВключено, любая транзакционная операция должна быть вsession contextсередина. Вот сравнение транзакционных операций с использованием разных API:

использовать другой API Активное управление сессиями Активный сеансКонтекстное управление Прерывание/фиксация активной транзакции удобство
mongo.NewSessionContext да да да *
mongo.WithSession да нет да **
client.UseSessionWithOptions нет нет да ***
session.WithTransaction да нет нет ****

mongo.NewSessionContext

package main

import (
  "context"
  "fmt"
  "log"
  "mongo-notes/config"
  "time"

  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
)

func main() {
  // ctx
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  defer cancel()

  // client
  client, err := mongo.Connect(ctx, config.ClientOpts)
  if err != nil {
    log.Fatal(err)
  }

  // collection
  collection := client.Database("test").Collection("person")

  // session
  session, err := client.StartSession()
  if err != nil {
    panic(err)
  }
  defer session.EndSession(context.TODO())

  // session context
  sessionCtx := mongo.NewSessionContext(ctx, session)

  // session开启transaction
  if err = session.StartTransaction(); err != nil {
      panic(err)
  }
  // transaction: insertOne in sessionCtx
  insertOneResult, err := collection.InsertOne(sessionCtx, bson.D{{"name", "大boss"}})
  if err != nil {
    // 使用context.Background()可以保证abort能够成功,即使mongo的ctx已经超时
    _ = session.AbortTransaction(context.Background())
    panic(err)
  }

  // transaction: findOne in sessionCtx
  var result bson.M
  if err = collection.FindOne(sessionCtx, bson.D{{"_id", insertOneResult.InsertedID}}).Decode(&result); err != nil {
    //使用context.Background()可以保证abort能够成功,即使mongo的ctx已经超时
    _ = session.AbortTransaction(context.Background())
    panic(err)
  }
  fmt.Printf("result: %v\n", result)

  // 使用context.Background()可以保证commit能够成功,即使mongo的ctx已经超时
  if err = session.CommitTransaction(context.Background()); err != nil {
    panic(err)
  }
}

mongo.WithSession

package main

import (
  "context"
  "fmt"
  "log"
  "mongo-notes/config"
  "time"

  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"
  "go.mongodb.org/mongo-driver/mongo/readconcern"
)

func main() {
  // ctx
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  defer cancel()

  // client
  client, err := mongo.Connect(ctx, config.ClientOpts)
  if err != nil {
      log.Fatal(err)
  }

  // collection
  collection := client.Database("test").Collection("person")

  // session
  sessionOpts := options.Session().SetDefaultReadConcern(readconcern.Majority())
  session, err := client.StartSession(sessionOpts)
  if err != nil {
      log.Fatal(err)
  }
  defer session.EndSession(context.TODO())

  // transaction
  err = mongo.WithSession(ctx, session, func(sessionCtx mongo.SessionContext) error {

    if err := session.StartTransaction(); err != nil {
        return err
    }

    insertOneResult, err := collection.InsertOne(sessionCtx, bson.D{{"name", "小boss"}})
    if err != nil {
        // 使用context.Background()可以保证abort能够成功,即使mongo的ctx已经超时
        _ = session.AbortTransaction(context.Background())
        return err
    }

    var result bson.M
    if err = collection.FindOne(sessionCtx, bson.D{{"_id", insertOneResult.InsertedID}}).Decode(&result); err != nil {
        // 使用context.Background()可以保证abort能够成功,即使mongo的ctx已经超时
        _ = session.AbortTransaction(context.Background())
        return err
    }
    fmt.Println(result)

    // 使用context.Background()可以保证commit能够成功,即使mongo的ctx已经超时
    return session.CommitTransaction(context.Background())
  })
}

client.UseSessionWithOptions

package main

import (
  "context"
  "fmt"
  "log"
  "mongo-notes/config"
  "time"

  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"
  "go.mongodb.org/mongo-driver/mongo/readconcern"
)

func main() {
  // ctx
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  defer cancel()

  // client
  client, err := mongo.Connect(ctx, config.ClientOpts)
  if err != nil {
    log.Fatal(err)
  }

  // collection
  collection := client.Database("test").Collection("person")

  // session 
  sessionOpts := options.Session().SetDefaultReadConcern(readconcern.Majority())
  // transaction
  err = client.UseSessionWithOptions(ctx, sessionOpts, func(sessionCtx mongo.SessionContext) error {

    if err := sessionCtx.StartTransaction(); err != nil {
        return err
    }

    insertOneResult, err := collection.InsertOne(sessionCtx, bson.D{{"name", "战五渣"}})
    if err != nil {
        // 使用context.Background()可以保证abort能够成功,即使mongo的ctx已经超时
        _ = sessionCtx.AbortTransaction(context.Background())
        return err
    }

    var result bson.M
    if err = collection.FindOne(sessionCtx, bson.D{{"_id", insertOneResult.InsertedID}}).Decode(&result); err != nil {
       _ = sessionCtx.AbortTransaction(context.Background())
       return err
    }
    fmt.Println(result)

    // 使用context.Background()可以保证commit能够成功,即使mongo的ctx已经超时
    return sessionCtx.CommitTransaction(context.Background())
  })
  if err != nil {
      log.Fatal(err)
  }

}

session.WithTransaction

package main

import (
  "context"
  "fmt"
  "log"
  "mongo-notes/config"
  "time"

  "go.mongodb.org/mongo-driver/bson"
  "go.mongodb.org/mongo-driver/mongo"
  "go.mongodb.org/mongo-driver/mongo/options"
  "go.mongodb.org/mongo-driver/mongo/readconcern"
  "go.mongodb.org/mongo-driver/mongo/readpref"
)

func main() {
  // ctx
  ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  defer cancel()

  client, err := mongo.Connect(ctx, config.ClientOpts)
  if err != nil {
    log.Fatal(err)
  }
  // collection
  collection := client.Database("test").Collection("person")

  // session 读取策略
  sessOpts := options.Session().SetDefaultReadConcern(readconcern.Majority())
  session, err := client.StartSession(sessOpts)
  if err != nil {
    log.Fatal(err)
  }
  defer session.EndSession(context.TODO())

  // transaction 读取优先级
  transacOpts := options.Transaction().SetReadPreference(readpref.Primary())
  // 插入一条记录、查找一条记录在同一个事务中
  result, err := session.WithTransaction(ctx, func(sessionCtx mongo.SessionContext) (interface{}, error) {
    // insert one
    insertOneResult, err := collection.InsertOne(sessionCtx, bson.M{"name": "无名小角色", "level": 5})
    if err != nil {
      log.Fatal(err)
    }
    fmt.Println("inserted id:", insertOneResult.InsertedID)

    // find one
    var result struct {
      Name  string `bson:"name,omitempty"`
      Level int    `bson:"level,omitempty"`
    }
    singleResult := collection.FindOne(sessionCtx, bson.M{"name": "无名小角色"})
    if err = singleResult.Decode(&result); err != nil {
      return nil, err
    }

    return result, err

  }, transacOpts)

  if err != nil {
      log.Fatal(err)
  }
  fmt.Printf("find one result: %+v \n", result)
}

Примечание. Если база данных не существует,nameдля"无名小角色"записи, вышеуказанные операции завершатся ошибкой, несмотря на то, что запись вставляется первой в транзакциюnameТа же запись, но транзакция представляет собой целое, поиск записей до начала транзакции, поэтому поиск не удастся, и вставка также не удастся.

Отличительный и счет

Distinct

// Distinct
distinctOpts := options.Distinct().SetMaxTime(2 * time.Second)
// 返回所有不同的人名
distinctValues, err := collection.Distinct(ctx, "name", bson.M{}, distinctOpts)
if err != nil {
  log.Fatal(err)
}
for _, value := range distinctValues {
  fmt.Println(value)
}

Count

// EstimatedDocumentCount
totalCount, err := collection.EstimatedDocumentCount(ctx)
if err != nil {
  log.Fatal(err)
}
fmt.Println("totalCount:", totalCount)

// CountDocuments
count, err := collection.CountDocuments(ctx, bson.M{"name": "5t5"})
if err != nil {
  log.Fatal(err)
}
fmt.Println("count:", count)

Совокупность

// Aggregate
// 按性别分组求和,统计出现次数
groupStage := bson.D{
  {"$group", bson.D{
    {"_id", "$gender"},
    {"numTimes", bson.D{
      {"$sum", 1},
    }},
  }},
}
opts := options.Aggregate().SetMaxTime(2 * time.Second)
aggCursor, err := collection.Aggregate(ctx, mongo.Pipeline{groupStage}, opts)
if err != nil {
  log.Fatal(err)
}

var results []bson.M
if err = aggCursor.All(ctx, &results); err != nil {
  log.Fatal(err)
}
for _, result := range results {
  fmt.Printf("gender %v appears %v times\n", result["_id"], result["numTimes"])
}

Мониторинг событий

мониторинг клиентов

// 监控所有db中的所有collection的插入操作
matchStage := bson.D{{"$match", bson.D{{"operationType", "insert"}}}}
opts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)
changeStream, err := client.Watch(ctx, mongo.Pipeline{matchStage}, opts)
if err != nil {
  log.Fatal(err)
}

for changeStream.Next(ctx) {
  fmt.Println(changeStream.Current)
}

// 向test.person插入一篇document
{"_id": {"_data": "825FDE28AE000000022B022C0100296E5A10046470407E872C47AFB61ECB12299F90D646645F69640
0645FDE28AE4D0D028CFA4B6B140004"},"operationType": "insert","clusterTime":
 {"$timestamp":{"t":"1608394926","i":"2"}},"fullDocument": {"_id": 
{"$oid":"5fde28ae4d0d028cfa4b6b14"},"name": "蔷薇","level": 
{"$numberInt":"2"},"gender": "女"},"ns": {"db": "test","coll": 
"person"},"documentKey": {"_id": {"$oid":"5fde28ae4d0d028cfa4b6b14"}}}

мониторинг базы данных

// db监控所有collection的插入操作
matchStage := bson.D{{"$match", bson.D{{"operationType", "insert"}}}}
opts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)
changeStream, err := db.Watch(context.TODO(), mongo.Pipeline{matchStage}, opts)
if err != nil {
  log.Fatal(err)
}

for changeStream.Next(ctx) {
  fmt.Println(changeStream.Current)
}

мониторинг коллекции

// 只监控当前collection的插入操作
matchStage := bson.D{{"$match", bson.D{{"operationType", "insert"}}}}
opts := options.ChangeStream().SetMaxAwaitTime(2 * time.Second)
changeStream, err := collection.Watch(context.TODO(), mongo.Pipeline{matchStage}, opts)
if err != nil {
  log.Fatal(err)
}

for changeStream.Next(ctx) {
  fmt.Println(changeStream.Current)
}

Суммировать

С точки зрения использования, это правда, чтоpythonизpymongoЭто более удобно в использовании.

использованная литература

  1. GitHub.com/MongoDB/Мока…
  2. pkg.go.Dev/go.MongoDB. …