PHPとMongoDBで部分一致検索をやってみた
先日ドットインストールに MongoDB の基礎レッスンを追加したこともあって、最近よく MongoDB をさわるようになりました。
PHP から MongoDB を扱うには、PECL の Mongo というライブラリを使います。
<?php
$mongo = new MongoClient();
$cursor = $mongo->test->foo->find( array( 'is_new' => 1 ) );
foreach ($cursor as $doc) {
print_r($doc);
}
これは is_new の値が 1 のデータを取得する例。PHPなので find()の取得条件を構築するには配列を使います。
同じ処理を MongoDB 自身の書き方で書くと、
$db.foo.find( { is_new: 1 } );
このようになります。find() の引数はオブジェクトです。このように本来はオブジェクトで記述する場合であっても、PHP Mongoでは配列を使うことになります。
部分一致検索をやってみた
ここでは DailyFeed の検索機能で実装した、
「feedsコレクションの title または url に、指定されたキーワードがすべて含まれているデータを取得する。」
という処理を例に、PHP Mongo での書き方を見てみます。
まず MySQL で部分一致検索をすると、
SELECT * FROM feeds WHERE (title LIKE '%Google%' AND title LIKE '%blog%') OR (url LIKE '%Google%' AND url LIKE '%blog%');
こんな感じのSQLになります。これはキーワードに「Google」と「blog」を指定した場合で、LIKEを使ったいわゆる文字列の部分一致検索です。
これを MongoDB の書き方に直すと、こうなります。
db.feeds.find({
$or: [
{
$and: [
{ title: /Google/i },
{ title: /blog/i }
]
},
{
$and: [
{ link: /Google/i },
{ link: /blog/i }
]
}
]
});
部分一致検索には正規表現を使います。MySQLで大文字小文字を区別していなかったので、正規表現にも「i」フラグをつけています。
さて、これをPHPのMongoライブラリでの書き方に直すと、次のようになります。(ここではデータベース名を「test」としています。)
<?php
$mongo = new MongoClient();
$mongo->test->feeds->find(array(
'$or' => array(
array(
'$and' => array(
array( 'title' => new MongoRegex('/Google/i') ),
array( 'title' => new MongoRegex('/blog/i') ),
)
),
array(
'$and' => array(
array( 'url' => new MongoRegex('/Google/i') ),
array( 'url' => new MongoRegex('/blog/i') ),
)
),
),
));
array多すぎw というのは置いといて、PHP Mongoで正規表現を使おうと思ったら、このように MongoRegex クラスを使う必要があります。
なお、この例はキーワードを「Google」と「blog」で固定した例なので、実際の実装ではこのキーワード指定の部分、すなわち $and の値である配列の要素が、キーワードの数だけ追加されます。
/**
* $keywords にキーワードが配列で格納されているとして
*/
foreach ($keywords as $keyword) {
$andConds[] = array(
'title' => new MongoRegex('/' . preg_quote($keyword, '/') . '/i'),
);
}
変数を利用して正規表現を組み立てることになるので、preg_quoteを使うことにも注意。
若干めんどくさい感はありますが、MongoDBはSQLで記述するよりも論理的にクエリを構築できるので、実際コードの見通しはよくなったと感じます。
とはいえ複雑なので、PHPでMongoDBを扱う際には、まず MongoDB のコンソールで正しいクエリを構築してから、PHP Mongo の書き方に書き直すという手順を踏むと、スムーズに正解に辿り着けるかなと思います。
(追記: 2012-11-29) new Mongo(); は非推奨となっていたようですので、 new MongoClient(); に修正しました。
コメントを残す