我尝试使用mongoDB的Go驱动程序
概括
(概要)
我接触了适用于Go的MongoDB官方驱动程序,以下是我的总结。
环境
使用中文将以下内容进行释义,只需要一个选项:
go 1.12.5
go.mongodb.org/mongo-driver v1.1.2
mongoDB 3.1
使用的詞彙
有一位能够简明易懂地总结的人
-
- table → collection
-
- row → document
- column → field
连接
不过,我觉得查看公式文件的使用说明可能更快一些。
import (
"context"
"log"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://username:password@localhost:27017"))
if err != nil {
log.Fatalln(err)
return
}
}
在這裡指定連接的URI為「mongodb://username:password@localhost:27017」。
找一个
如果使用结构体创建搜索条件,获取文档。
import (
"go.mongodb.org/mongo-driver/bson/primitive"
// NOTE: 他に必要なパッケージは省略
)
type FindOneRequest struct {
// NOTE: 検索したいfieldとvalueを適宜定義する
TargetField string `json:"targetField" bson:"targetField"`
}
type FindOneResponse struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Hoge string `json:"hoge" bson:"hoge"`
Fuga string `json:"fuga" bson:"fuga"`
}
func main() {
// NOTE: 接続処理は省きやす 上の見てくだされ
collection := client.Database("db_name").Collection("collection_name")
request := FindOneRequest{
TargetField: "検索内容",
}
var response FindOneResponse
err = collection.FindOne(context.Background(), request).Decode(&response)
if err == mongo.ErrNoDocuments {
log.Println("Documents not found")
} else if err != nil {
log.Fatalln(err)
}
log.Println(res)
}
如果使用结构体,可以使用bson标签来指定在文档中使用的字段名称,从而进行合适的映射。
当创建文档时,使用primitive.ObjectID类型可以自动生成ObjectID,非常方便。
找到
获取多个文档
这次我们使用bson.D来定义搜索条件,并使用bson.M作为响应。
import (
"go.mongodb.org/mongo-driver/bson"
// NOTE: 他に必要なパッケージは省略
)
func main() {
// NOTE: 接続処理は省きやす
collection := client.Database("db_name").Collection("collection_name")
// NOTE: 検索したいfield名とvalueを定義
cur, err := collection.Find(ctx, bson.D{{Key: "field_name", Value: "value"}})
if err != nil {
log.Fatal(err)
}
defer cur.Close(ctx)
for cur.Next(ctx) {
// NOTE: 1ドキュメントずつdecode
// mapが返ってくる
var result bson.M
err := cur.Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
}
if err := cur.Err(); err != nil {
log.Fatal(err)
}
}
当使用Find时,返回值并不是简单的数组等,而是返回了Cursor类型,所以我感觉处理起来很特别。
插入一个
添加文档
import (
"go.mongodb.org/mongo-driver/bson/primitive"
// NOTE: 他に必要なパッケージは省略
)
type InsertOneRequest struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Hoge string `json:"hoge" bson:"hoge"`
Fuga string `json:"fuga" bson:"fuga"`
}
func main() {
// NOTE: 接続処理は省きやす
collection := client.Database("db_name").Collection("collection_name")
request := InsertOneRequest{
ID: primitive.NewObjectID(),
Hoge: "hoge",
Fuga: "fuga",
}
// NOTE: InsertOneの返り値には作成したドキュメントのObjectIDが返ってくる
response, err := collection.InsertOne(context.Background(), request)
if err != nil {
log.Fatalln(err)
}
}
当您希望使用创建的文件进行某些操作时,可以使用request.ID通过FindOne函数获取相应的结构体来实现。
插入多个
注册多个文件
import (
"go.mongodb.org/mongo-driver/bson/primitive"
// NOTE: 他に必要なパッケージは省略
)
type Request struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Hoge string `json:"hoge" bson:"hoge"`
Fuga string `json:"fuga" bson:"fuga"`
}
func main() {
// NOTE: 接続処理は省きやす
collection := client.Database("db_name").Collection("collection_name")
var insertManyRequest []interface{}
for index := 0; index < 10; index++ {
indexString := strconv.Itoa(index)
request := Request{
ID: primitive.NewObjectID(),
Hoge: "hoge",
Fuga: "fuga",
}
insertManyRequest = append(insertManyRequest, question)
}
// NOTE: 今回の場合、一括で10件登録してくれる
// 返り値はinsertしたドキュメントのObjectIDの配列
response, err := collection.InsertMany(context.Background(), insertManyRequest)
if err != nil {
log.Fatalln(err)
}
}
InsertMany方法的第二个参数是[]interface{}类型。
汇总
似乎可以使用Aggregate来进行集合计算。
在mongoDB中,有一种叫作Operators的东西,可以通过使用这些Operators来对Collection进行操作。
在Operators中,Pipeline是用于Aggregate的。
在官方文档中有相对较多的示例,如果参考那些示例,就可以不知不觉地进行实施。
import (
"go.mongodb.org/mongo-driver/bson"
// NOTE: 他に必要なパッケージは省略
)
func main() {
// NOTE: 接続処理は省きやす
collection := client.Database("db_name").Collection("collection_name")
pipeline := []bson.M{
bson.M{
"$match": bson.M{
"targetField": "value",
},
},
bson.M{
"$group": bson.M{
"_id": "$targetFieldID",
"count": bson.M{
"$sum": 1,
},
},
},
}
// NOTE: AggregateはFindと同様に`*Cursor`型を返すので、Cursor型用の扱い方が必要
hogeAggre, err := collection.Aggregate(ctx, pipeline)
if err != nil {
log.Fatalln(err)
}
defer hogeAggre.Close(ctx)
for hogeAggre.Next(ctx) {
var result bson.M
err := answerAggre.Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
}
if err := answerAggre.Err(); err != nil {
log.Fatal(err)
}
}
预期的输出结果应该是返回类似下面所示的结果
由于可能严谨地说会有所不同,请勿见怪
[
{
"_id": "hogehoge",
"count": 15,
},
{
"_id": "fugafuga",
"count": 13,
},
]
有一个带有$符号的是Operator对吧。这次在Pipeline里指定的是通过Collection名为collection_name的targetField字段的值为value的文档们,按照targetFieldID字段进行分组,计算每个targetFieldID的文档数量,并将结果存储在count字段中。大概是这样吧。
操作员和管道阶段等需要更多阅读文档的可能性较高。
感受
当进行Insert或Find操作时,参数和返回值都应当通过结构体进行明确定义。然而,由于文档的结构即使是相同的集合也可能有不同的可能性,所以或许不应该像bson.M那样进行严格的类型定义,这也是一种选项。