Lithium と MongoDB でフリーワード検索
最近は朝飯を、家を出る前に摂っている、tanaka です。
さて、Lithium というPHPのフレームワークを触ってみました。 Lithium はPHP5.3以上専用のフレームワークで、構造がCakePHPに似ています。そして非RDBのMongoDBが標準で使えるようになっています。今回は勉強を兼ねて簡単なブログ作成チュートリアルの内容に、キーワード検索の機能を付けてみましょう。
チュートリアルが終わったら
Lithium のサイトにはよくあるブログ作成チュートリアルのドキュメントがあります。ブログ記事の追加・一覧の作成までが解説されています。この解説に沿った日本語の説明もいくつかのサイトで読むことが出来ます。
このドキュメントを読んで、最低限の使い方は身につくでしょう。しかし、普通のチュートリアルとしてはいろいろ足りてない感じがします。そこで今回は、チュートリアルで作ったブログにフリーワード検索の機能を追加してみました。
チュートリアル終了直後のコード
app/controllers/PostsController.php
<?php
namespace app\controllers;
use app\models\Post;
class PostsController extends \lithium\action\Controller {
public function index() {
$posts = Post::all();
return compact('posts');
}
public function add() {
$success = false;
if ($this->request->data) {
$post = Post::create($this->request->data);
$success = $post->save();
}
return compact('success');
}
}
app/views/posts/index.html.php
<?php foreach($posts as $post): ?>
<article>
<h1><?php echo $post->title ?></h1>
<p><?php echo $post->body ?></p>
</article>
<?php endforeach; ?>
<?php echo $this->html->link('add','/posts/add'); ?>
だいたいこんな感じだと思います。(記事追加のビューなどは省略します)
検索フォームを作る
次のコードを、記事一覧の上に追加します。
<?php echo $this->Form->create(null, array('method'=>'get')); ?>
<?php echo $this->Form->text('q');?>
<?php echo $this->Form->end();?>
このようにフォームを用意すれば、検索結果のURLが「http://lithium.localhost/posts?q=クエリ」といった感じになります。
検索ロジックを組み立てる
ブログ記事一覧を表示しているいindexアクションメソッドを次のように書き換えます。
public function index() {
$options = array();
if (isset($this->request->query['q'])) {
$quote_query = preg_quote($this->request->query['q']);
$options['conditions'] = array(
'$or' => array(
array('title'=> new \MongoRegex("/{$quote_query}/i")),
array('body'=> new \MongoRegex("/{$quote_query}/i")),
),
);
}
$posts = Post::find('all', $options);
return compact('posts');
}
ポイントをいくつか解説します。まず、キーワードが指定されない場合は、$options配列は空のままPost::find() に渡されるため、全件取得します。(if文内がスルーされる)
キーワードを入力して送信すると、タイトルか本文から検索します。MongoDBにはSQLのLIKEと等価な構文はないようですが、代わりに正規表現で検索ができます。8,9行目で検索キーワードからMongoRegexインスタンスをつくると、検索クエリに組み込むことができます。
ここで注意しなければならないのが、正規表現が使えると言うことは、エスケープ文字を考慮する必要があります。MongoDBの正規表現にはPCREが使われているということなので、preg_quote関数でエスケープ出来そうです。(5行目)
タイトルもしくは本文のどちらかヒットしたら、という条件にしたい場合は $or オペレータを使います。(これも8, 9行目)
まとめ
Lithiumのブログチュートリアルにキーワード検索機能を追加してみました。まだまだドキュメントが少ないので大変ですが、MongoDBの柔軟そうなところがおもしろいですね。よろしければお試しください。