mongo shellから検索クエリはわかるけど、Rubyドライバでの書き方がわからんときの解決法
BSONなので、BSON経由で変換すればいい話ですね。
例えば、下のようなMongoDBドキュメントがあったとします。
{ "_id" : ObjectId("51b6f5414eba1e94ba5bf762"), "info" : { "category" : "file", "started" : "2013-06-11 18:59:52", "ended" : "2013-06-11 19:00:48", "version" : "0.5", "duration" : "55 seconds", "id" : 3921 } }
info.startedは開始日時を表しており、これを使ってフィルタをかけたいとします。
mongo shell で書くとこんな感じです。
{ "info.started": {$gte: "2013-06-10",$lt: "2013-06-11"}}
当たり前ですが
Rubyドライバで以下のようにすると、データ量にもよりますが死ぬほど時間がかかります。
coll.find().each do |doc| # ここでフィルタ end
find()の第1引数でフィルタをかけるべきですが、複雑なMongoDBドキュメントになればなるほど、
どういうふうに書けばいいかわからなくなります。
ドキュメントには基本的な構文しか載っていません。
MongoDBの検索クエリはBSON形式なので、mongo shellで書いた検索クエリをBSON形式に変換し、
Rubyドライバでデシリアライズすればよさそうです。
mongo shellはJavaScriptなので、JS用のドライバで検索クエリをシリアライズします。
MongoDBにはjs-bsonというライブラリがついていて、ブラウザからも使えます。
js-bson
https://github.com/mongodb/js-bson
browser_build/bson.jsを適当な場所に配置して、同じフォルダに以下のファイルを置きます。
js-bsonのREADMEに書いてあったものを持ってきて、コンソール出力するように修正しただけです。
<head> <script src="./bson.js"> </script> </head> <body onload="start();"> <script> function start() { var BSON = bson().BSON; var Long = bson().Long; var doc = {//ここに検索クエリを書く} // 例えばこんな感じ // var doc = {"info.started": {$gte: "2013-06-10",$lt: "2013-06-11"}} var data = BSON.serialize(doc, false, true, false); console.log(data) } </script> </body>
これでBSON形式の検索クエリがブラウザのコンソールに出力されます。
ChromeであればF12キーを押して、consoleタブに移動するだけです。
BSON形式のバイナリは10進数の配列で表示されます。
16進数でRubyでデシリアライズできる形式に変更したいので、Rubyで成型します。
require 'bson' # ブラウザに表示された配列をコピーしてirbの変数に代入します。 bson = [65, 0, 0, 0, 3, 105, 110, 102, 111, 46, 115, 116, 97, 114, 116, 101, 100, 0, 46, 0, 0, 0, 2, 36, 103, 116, 101, 0, 11, 0, 0, 0, 50, 48, 49, 51, 45, 48, 54, 45, 49, 48, 0, 2, 36, 108, 116, 0, 11, 0, 0, 0, 50, 48, 49, 51, 45, 48, 54, 45, 49, 49, 0, 0, 0] # 16進数に変換します hex = bson.map {|x| "%02X" % x} # 結合します msg << hex.join # Rubyのバイナリ形式に変換します bin = msg.pack("H*") # BSON形式をデシリアライズします BSON.deserialize(bin.unpack("C*")) # => {"info.started"=>{"$gte"=>"2013-06-10", "$lt"=>"2013-06-11"}}
あとはこの検索クエリをRubyドライバから使うだけです。