oranie's blog

旧:iをgに変えると・・・なんだっけ・・・

Go + mgo (MongoDB)でauto increment的な事をやる。

自分メモなので簡単に。

  1. MongoDB側にcounter用コレクション作る
  2. Go側でcounter用のstructを定義しておく。
  3. Apply()を利用してcounterをインクリメントし、その値を使ってドキュメントをINSERTする。

という流れになる。
mgoのドライバ公式にApplyの代表的な使い方として書かれているが初め良く分からずハマったのでメモ。
http://godoc.org/labix.org/v2/mgo#Query.Apply

MongoDB側にcounter用コレクション作る

とりあえずここを読みましょう。
http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/
コンソールからauto incrementする方法がオフィシャルドキュメントに書いてあります。

Go側でcounter用のstructを定義しておく。

こんな感じ。

	type  Counter struct {
		Object_Id  bson.ObjectId`json:"_id" bson:"_id,omitempty"`
		Id string `json:"id"`
		Seq int64`json:"seq"`
	}

これはなんで定義するかというと、インクリメント処理が終わった際にそのドキュメントを受け取るインターフェイスが必要で、そこに格納された値を用いてINSERTする時にインクリメントされた値をセットする。なので、MySQLのテーブルスキーマに設定するauto incrementとかと一緒かというと、別に採番テーブルを作成してその値を使った用いた処理と一緒になる。
DeNAさんが前に技術ブログで書いた
http://engineer.dena.jp/2010/11/mysql-for-socialgame.html
ログIDの払い出しの時と同じイメージですね。

Apply()を利用してcounterをインクリメントし、その値を使ってドキュメントをINSERTする。

	change := mgo.Change{
		Update: bson.M{"$inc": bson.M{"seq": 1}},
		ReturnNew: true,
	}

こんな感じでまずincrementする処理を書き、

	var result Counter //change処理結果のドキュメントを受け取る変数
	info,err := t.Find(bson.M{"id":"seqNo"}).Apply(change,&result) //change処理が成功したら、&resultに更新したドキュメントが格納される。
	fmt.Println(result.Seq) //resultのseqメンバの値を標準出力してみる

という形でインクリメントした値を標準出力出来るので、これを用いてINSERT処理に使えば良い。

で、さっきも書いたけど、採番テーブルを使うパターン + transactionが無いからincrementは成功したけど、その後INSERTする時にアプリがダウンとかすると、counterは進んだけどその値を持ったドキュメントはいないとかになるので、そのレベルになる事を注意。

あと、公式に書いてあるようなMongoDB側にincrementする関数を設定して、それをクエリ時に使おうと

db.users.insert(
   {
     _id: getNextSequence("userid"),
     name: "Sarah C."
   }
)

こんな感じのクエリを投げようとしても、getNextSequence()を実行したいがそれがただの文字列に扱われてしまったので、こんな感じになった。もしmgoを使ってこれを叩く方法があれば教えて下さい><