S.Hayakawaをフォローする

【Golang】MongoDBを使ってみよう – Gopherへの道 ③

バックエンド

こんにちは、Golangの記事第三弾です。
今回は Golang + MongoDB を使用する機会がありましたので、さっそく調べてみました!
ここでは、MongoDBの簡単な概要と、MongoDBの公式ドライバを使用した簡単なCRUD操作について見ていきます。
今回はReadとCreateのみ紹介ですが、公式のUsageから他も同様に確認できます。

MongoDB

MongoDBは、NoSQLのうち、ドキュメント指向のデータベースです。
RDBMSとの大きな違いとして、テーブルのように事前にデータ構造を決定しておかなくてもよい点などが挙げられます。
あとから自由にスキーマを変更できるので、APIの仕様変更などがあっても柔軟に対応できるようになっています。
シンプルで自由度の高いデータ構造も、静的型付けのGolangとは相性が良いように思います。

ドライバのインストール

公式のドキュメントを参考にします
https://docs.mongodb.com/drivers/go/current/quick-start/

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

接続の例

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"
)

// Connection URI
const uri = "<<mongoDB-URI>>"

func main() {
	// Create a new client and connect to the server
	client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI(uri))

	if err != nil {
		panic(err)
	}
	defer func() {
		if err = client.Disconnect(context.TODO()); err != nil {
			panic(err)
		}
	}()

	// Ping the primary
	if err := client.Ping(context.TODO(), readpref.Primary()); err != nil {
		panic(err)
	}

	fmt.Println("Successfully connected and pinged.")
}

ドキュメントの取得

FindOne

type respData struct {
	Id primitive.ObjectID `json:"id" bson:"_id"`
	Name string `json:"name" bson:"name"`
}

func main() {
	coll := client.Database("<<dbName>>").Collection("<<collectionName>>")
	
	var result respData
	err = coll.FindOne(context.TODO(), bson.D{{"<<fieldName>>", "hoge"}}).Decode(&result)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			// This error means your query did not match any documents.
			return
		}
		panic(err)
	}
}

collection.FindOneの返り値は*mongo.SingleResultの型ですが、
Decodeが構造体にマッピングしてくれます。

Find (Multiple Documents)

type respData struct {
	Id primitive.ObjectID `json:"id" bson:"_id"`
	Name string `json:"name" bson:"name"`
}

func main() {
	coll := client.Database("<<dbName>>").Collection("<<collectionName>>")
	
	filter := bson.D{{"<<fieldName>>", bson.D{{"$eq", "hoge"}}}}

	cur, err := coll.Find(context.TODO(), filter)
	if err != nil {
		panic(err)
	}

	for cur.Next(context.TODO()) {
		var res respData
		cur.Decode(&res)
		fmt.Println(res)
	}
}

複数ドキュメントの取得ができるFindは返り値が*mongo.Cursorです。
こちらもDecodeでマッピング可能。

ドキュメントの追加

InsertOne

func main() {
	coll := client.Database("<<dbName>>").Collection("<<collectionName>>")
	
	doc := bson.D{{"title", "Record of a Shriveled Datum"}, {"text", "No bytes, no problem. Just insert a document, in MongoDB"}}
	result, err := coll.InsertOne(context.TODO(), doc)
	if err != nil {
		panic(err)
	}
}

InsertMany

docs := []interface{}{
	bson.D{{"title", "Record of a Shriveled Datum"}, {"text", "No bytes, no problem. Just insert a document, in MongoDB"}},
	bson.D{{"title", "Showcasing a Blossoming Binary"}, {"text", "Binary data, safely stored with GridFS. Bucket the data"}},
}
result, err := coll.InsertMany(context.TODO(), docs)
if err != nil {
	panic(err)
}

おわりに

MongoDBの操作について簡単にまとめてみました。
MongoDBは、bsonなど独特なデータ構造を使用する、RDBMSのような高度な結合処理が難しい、などわかりにくい部分もありましたが
Golang + MongoDB の組み合わせでは、高速な検索や柔軟なスキーマ変更など、開発に便利な感触がありました。
Aggregationなど高度な操作も、機会があればまとめたいと思います。
ありがとうございました。

備考

bson

高度な結合処理が難しい