概要
mongoDB公式のGo用のDriver触ったので、そのまとめ
環境
go 1.12.5
go.mongodb.org/mongo-driver v1.1.2
mongoDB 3.1
用語
- table → collection
- row → document
- column → field
接続
公式ドキュメントのUsage見た方が早い気がしやすが一応
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
}
}
ApplyURI("mongodb://username:password@localhost:27017")
ここで接続先指定してる感じ
FindOne
1ドキュメントの取得
検索条件を構造体で作成した場合
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
タグ使ってドキュメントで使用している項目名を指定すればいい感じにマッピングしてくれる
ドキュメント作ると勝手に生成してくれるObjectIDはprimitive.ObjectID
型を使えばよき
Find
複数ドキュメントの取得
今回は検索条件を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
型が返ってくるのでここらへんの扱いが独特だと感じた次第
InsertOne
1ドキュメントの追加
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した構造体を使う感じになりますかね
InsertMany
複数ドキュメントの登録
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
集計はAggregateってやつでできるらしい
mongoDBにはOperatorsってのがあってCollectionに対して、このOperatorsを使ってく感じですね
Operatorsの中でもAggregateで使うのがPipeline
公式ドキュメントに比較的Exampleが書いてるのでそれとにらめっこするといつの間にか実装できる
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_name
ってコレクションのtargetField
フィールドの値がvalue
のドキュメントたちをtargetFieldID
フィールドでGroupByしてtargetFieldID
ごとのドキュメント数をcount
フィールドに算出させた感じです
多分・・・
OperatorとかPiplineのStageとかはもっとドキュメント読み込む必要がありやす
所感
InsertやFindするときの引数や返り値は構造体でしっかり型定義してたけども、結局ドキュメントの構造は同一のコレクションでも異なる可能性が出てくるのでbson.Mとかみたくしっかり型定義しないほうがいい気もした