Hatena::ブログ(Diary)

ursmの日記

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

asosa01asosa01 2010/11/17 16:23 AND 検索問題で困ってたどり着きました。mongo shell では、書けることは書けていますが name キーが重複しているので結局 { name: /B/} で検索されますね。ruby に限らず他の言語でも同じですが、どのような解決方法が適切なのでしょうかね・・・?

ursmursm 2010/11/17 16:54 > name キーが重複しているので結局 {name: /B/} で検索されますね。
おーなんと。ご指摘ありがとうございます。1.0 のころはこれで動いた記憶があるのですが…。

1.3 以降なら $all を使えば良さそうですね。

db.users.find({name: {$all: [/A/, /B/]}})

asosa01asosa01 2010/11/17 17:06 > 1.3 以降なら $all を使えば良さそうですね。
おおっ! ありがとうございます。解決しました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/ursm/20091211/1260467008