Mgoを使った例は結構あるのだけど、MongoDB.comの公式ドライバを使った例があまりないなと思い、未来の自分のために書き残しておく。タグはStackOverflowに合わせて mongo-go
としてみた。
今回のソースコード一式はhttps://github.com/mkiuchi/go-mongo-driver-exampleに置いておきました。
Mgoと公式ドライバの違いとか情報源とか
調べてないのでよくわからない。公式でも若干触れられてるけどあまりこのへんをどうこう言う気はないので気になる人は各自なんとかしてください。
公式ドライバについてはMongoDB.comのドライバページを見る。記事執筆時点の最新バージョンは 1.0.3。たぶん最もよく見るのはGoDoc, チュートリアルだとは思うんだけど、いまいち詳細さに欠けていて、これを見てもわかるようなわからないようなイマイチわかりきらないところがある。この記事を書くことにした理由の一つ。
上でも書いたけど、公式以外で私が見た限りではStackOverflowが情報源としては一番ある。あるけどほとんど会話がなく、正直あてにならない。各自頑張ってください。
インストール
公式のとおりにすれば入る。私の場合は dep
が動かなかったので go get
で入れた。
$ dep ensure -add "go.mongodb.org/mongo-driver/mongo@~1.0.0"
$ go get -u "go.mongodb.org/mongo-driver/mongo"
$ go get "go.mongodb.org/mongo-driver/bsontype"
接続
以下のようにする。以下の例ではデータベースへの接続をチェックして、結果を出力する。
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
func main() {
// コンテキストの作成
// - バックグラウンドで接続する。タイムアウトは10秒
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// 関数を抜けたらクローズするようにする
defer cancel()
// 指定したURIに接続する
c, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
defer c.Disconnect(ctx)
// DBにPingする
err = c.Ping(ctx, readpref.Primary())
if err != nil {
fmt.Println("connection error:", err)
} else {
fmt.Println("connection success:")
}
}
接続オプションはだいたいどの記事でも context.Background() が使用されているけど、例によっては context.TODO() も使用されていることがある。とりあえず context.Background() を使用しておけばいいと思う。
読み出し
単純に検索して1件読み出す
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// MongoDBの接続設定
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
c, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
defer c.Disconnect(ctx)
// 結果を格納する変数の構造定義
type resultType struct {
Rid string
Keyword string
}
// 結果を格納する変数を宣言
var result resultType
// MongoDBのCollectionを取得
col := c.Database("rec").Collection("autorec")
// 検索条件となるprimitive.ObjectID型の変数を指定
objectID, _ := primitive.ObjectIDFromHex("5cffa613c74a91322fc7cbb2")
// 検索を実行し、結果を変数 result に格納
err := col.FindOne(context.Background(), bson.M{"_id": objectID}).Decode(&result)
_ = err
fmt.Println(result)
}
結果は以下のような感じになる
$ go run readone.go
{abcd1234 オリンピック}
複数の検索結果を読み出す
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// MongoDBの接続設定
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
c, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
defer c.Disconnect(ctx)
// 結果を格納する変数の構造定義
type resultType struct {
Start int
Title string
}
// MongoDBのCollectionを取得
col := c.Database("rec").Collection("rec")
// 検索条件となるint型の変数を指定
location, _ := time.LoadLocation("Asia/Tokyo")
start := time.Date(2019, 6, 30, 0, 0, 0, 0, location).Unix() * 1000
end := time.Date(2019, 6, 30, 23, 59, 59, 99, location).Unix() * 1000
fmt.Println(start, end)
// 検索を実行
cur, err := col.Find(context.Background(), bson.M{
"start": bson.M{
"$gte": start,
"$lte": end,
}})
_ = err
// 結果のカーソルをforで回して順番に結果を取得
for cur.Next(context.Background()) {
var ret resultType
cur.Decode(&ret)
fmt.Println(ret)
}
}
結果は以下のようになる
$ go run readmany.go
1561820400000 1561906799000
{1561879320000 プレマップ}
{1561880460000 フラッシュ天気}
ドキュメント(=レコード)の挿入
package main
import (
"context"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// MongoDBの接続設定
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
c, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
defer c.Disconnect(ctx)
// 挿入するデータの構造を定義
// 21, 22行目の末尾の文字はType aliasと呼ぶらしい。MongoDB内でフィールド名として解釈される
type dataType struct {
Rid string `bson:"rid"`
Keyword string `bson:"keyword"`
}
// 挿入するデータを作成
data := dataType{
Rid: "俺のID",
Keyword: "俺のキーワード",
}
// MongoDBのCollectionを取得
col := c.Database("rec").Collection("autorec")
// データを挿入
col.InsertOne(context.Background(), data)
}
上記の例では構造体(struct)を挿入しているが、他の型(例えばmapとか)を直接突っ込んでも割とよろしく取り扱ってくれる
複数データのバルク挿入はまだやったことがないのでスキップ。
ドキュメント(=レコード)の削除
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
// MongoDBの接続設定
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
c, _ := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
defer c.Disconnect(ctx)
// MongoDBのCollectionを取得
col := c.Database("rec").Collection("autorec")
// 検索条件となるprimitive.ObjectID型の変数を指定
objectID, _ := primitive.ObjectIDFromHex("5d1924916a81c3556cf3479b")
// 検索を実行し、結果のドキュメントを削除
_, err := col.DeleteOne(context.Background(), bson.M{"_id": objectID})
if err != nil {
fmt.Println("delete failed:", err)
} else {
fmt.Println("delete success")
}
}
特にコメントなし。