今日は出張中にたまっていた日記をまとめて書いていた。昨日筋トレしたから運動はおやすみ。

mongodb における外部結合

出張中のふりかえりやプロジェクトマネジメントの合間に mongodb の $lookup (aggregation) 機能を調査していた。一言で言えば、rdbms で言うところの外部結合 (join) を mongodb で実現する機能と言える。しかし、aggregation の機能として実装されていることから rdbms の外部結合とはパフォーマンスの側面で大きく違うのでは?と推測する。mongodb のような kvs では基本的に外部結合のようなことは行わず、結合済みのコレクションを定義するやり方がプラクティスとされる。一方で整合性を保証するには外部結合は有効なデザインパターンの1つなので使いたい場面があってもおかしくない。実際に mongodb に $lookup が実装されているのだから、そのニーズは大きいのだと思われる。

ちょうどいまやっている開発でその要件があったので mongo-go-driver の次のチュートリアルをみながら実装してみた。

例えば、次のような2つのコレクションがある。

type Group struct {
	ID          string      `bson:"_id"`
	Name        string      `bson:"name"`
	UpdatedAt   time.Time   `bson:"updatedAt"`
}

type MyData struct {
	ID           string    `bson:"_id"`
	GroupID      string    `bson:"groupID"`
	RegisteredAt time.Time `bson:"registeredAt"`
}

MyData の GroupID フィールドが Group コレクションの ID を保持して外部参照している。MyData コレクションに対する aggregate に渡すパラメーターとして pipeline を渡す。

myPipeline = mongo.Pipeline{
	{{
		Key: "$lookup", Value: bson.D{
			{Key: "from", Value: "group"},
			{Key: "localField", Value: "groupID"},
			{Key: "foreignField", Value: "_id"},
			{Key: "as", Value: "foreignGroup"},
		},
	}},
	{{
		Key: "$unwind", Value: bson.D{
			{Key: "path", Value: "$foreignGroup"},
			{Key: "preserveNullAndEmptyArrays", Value: false},
		},
	}},
}

$lookup の操作の後に $unwind で配列を flatten する処理がセットになる。result set にマッピングするためには flatten が必要になるのだと推測する。aggregate() を呼び出すと cursor が返ってきて、その cursor に外部結合される値の配列を渡すことで result set を取得できる。デバッグはややこしいし、コード上もちょっとわかりにくいけど、これは mongodb のお作法だと割り切ってユーティリティ化してしまえばよいものだと思う。

type joinedMyData struct {
	ID           string    `bson:"_id"`
	Group        Group     `bson:"foreignGroup"`
	RegisteredAt time.Time `bson:"registeredAt"`
}
...

cursor, err := collection.Aggregate(ctx, myPipeline)
if err != nil {
	return nil, err
}
var joined []joinedMember
if err = cursor.All(ctx, &joined); err != nil {
	return nil, err
}

また時間があるときにある程度のデータ量でどのぐらいの負荷に対してパフォーマンスが出るのかを測ってみたい。

ストレッチ

昨日1週間ぶりに筋トレをしたせいか、今朝から筋肉痛になっていてカラダが痛い。その影響もあって今日の開脚幅は開始前146cmで、ストレッチ後152cmとカラダが硬かった。足も腰も肩周りも全身にわたって軽い筋肉痛だった。それだけよい感じの負荷をかけて筋トレができていたと言えるのかもしれない。あちこち筋肉痛はあったものの、ストレッチを受けていての辛さのようなものは感じなかった。ちょうどよい感じにほぐされているような印象を受けた。もしかしたらトレーナーさんが気を遣っていつもよりは弱めにストレッチしてくれたのかもしれない。トレーナーさんが立候補していた店長選挙は落選してしまったらしい。もし店長になったらよそのお店へ転勤になって担当者が変わってしまうのかなとも思っていた。トレーナーさんは私が通っているお店の副店長を務める方向で調整しているらしい。待遇をあげていくにはキャリアアップをしていかないといけないといった背景もあるようにみえる。