Hatena::ブログ(Diary)

ursmの日記

2010-09-22

Tailable Cursors を試してみた

http://www.mongodb.org/display/DOCS/Tailable+Cursors

tail -f みたいなカーソル」だそうな。

Tailable Cursor が使えるのは Capped Collection だけらしい。Capped Collection はサイズに上限があるコレクションで、上限に達すると古いドキュメントから消えていく。挿入順で読み出せることが保証されている (普通のコレクションは保証されない)。

まず Capped Collection を作る。MongoDB だと明示的にコレクションを作る場面はほとんどないんだけど、Capped Collection を作るときは必要になる。

>> require 'mongo'
>> col = Mongo::Connection.new.db('test').create_collection('capped', :capped => true, :size => 10000)

Tailable Cursor を作る。

>> cur = Mongo::Cursor.new(col, :tailable => true)

適当に書いたり読んだりしてみる。

>> col.insert(:i => 1)
>> col.insert(:i => 2)
>> cur.next_document
=> {"_id"=>BSON::ObjectId('4c98da42ad7d212878000001'), "i"=>1}
>> cur.next_document
=> {"_id"=>BSON::ObjectId('4c98da45ad7d212878000002'), "i"=>2}
>> cur.next_document
=> nil
>> col.insert(:i => 3)
>> cur.next_document
=> {"_id"=>BSON::ObjectId('4c98da53ad7d212878000003'), "i"=>3}

カーソルがコレクションの終端に達しても、新しいドキュメントが挿入されると続けて読めるということみたい。終端で next_document したときにブロックしてくれればキューとして使えそうなんだけどな。

2010-08-18

mongoid_rails_migrations

MongoDB はスキーマレスなのでマイグレーションが不要かというとそんなことはない。スキーマ変更に伴うデータのマイグレーションは当然必要になる。

Rails 3 + Mongoid 2 の環境なら mongoid_rails_migrations が便利。generator と各種 rake タスクを提供してくれる。

デプロイ時はインデックスの作成も一緒にやってしまえば良さそう。

# config/deploy.rb

after 'deploy:migrate' do
  run "cd #{current_release} && RAILS_ENV=production rake db:create_indexes"
end

See Also

2010-02-16

Ruby Freaks Lounge に MongoDB の記事を寄稿しました

第31回 RubyistのためのMongoDB入門(1) :Ruby Freaks Lounge|gihyo.jp … 技術評論社

お仕事で MongoDB を使ってみたらとても面白かったので紹介記事を書きました。次回は2週間後の予定です。

東京Ruby会議03のワークショップも MongoDB をやりますのでよろしくお願いいたします。

2009-12-11

MongoDB ハマり道

NoSQL というビッグウェーブに乗りたくて MongoDB を使いはじめました。いくつかハマった点をメモしておきます。バージョンは 1.0.1 です。

Embedded Document のリストを使った検索問題

点を複数持つ多角形クラスがあるときに

class Polygon
  include MongoMapper::Document
  many :points
end

class Point
  include MongoMapper::EmbeddedDocument
  key :x, Integer
  key :y, Integer
end

Polygon.create!(:points => [{:x => 10, :y => 20}, {:x => 30, :y => 40}])

1つのキーによる検索は問題ない。

Polygon.all('points.x' => 10)  #=> hit
Polygon.all('points.y' => 40)  #=> hit
Polygon.all('points.x' => 100) #=> not hit

複数組み合わせると変な動きになる。

Polygon.all('points.x' => 10, 'points.y' => 40) #=> hit???

これは「points のいずれかが x == 10 かつ points のいずれかが y == 40 の Polygon」と解釈されるため。x の比較と y の比較が1つの Point に対して同時に行われるわけではない。

今のところ MongoDB の機能では何ともならないので、自分でクエリを書く必要がある。インデックスが効かないので遅いはず。

Polygon.all(:$where => <<-JS)
  return this.points.some(function(p) {
    return p.x == 10 && p.y == 40;
  });
JS

一応チケットは上がってる。http://jira.mongodb.org/browse/SERVER-377

追記 (2010-01-22)

上記チケットは MongoDB 1.3.1 で close されました。$elemMatch オペレータが追加されたようです。

正規表現の AND 検索問題

「名前に A と B を両方含むユーザ」を検索する場合、mongo shell からはこう書ける。

db.users.find({name: /A/, name: /B/})

MongoDB の Ruby driver は同じキーに対する複数の条件を指定できない。仕方がないのでやっぱりクエリを書く。

User.all(:$where => <<-JS)
  return ['A', 'B'].every(function(s) {
    return this.name.match(s);
  }, this);
JS

close されてるけど結局どうしたらいいの? http://jira.mongodb.org/browse/RUBY-20