mroongaでカラムごとに重み付けして検索するには


いよいよ、[gihyo.jp](http://gihyo.jp/)さんでgroongaの隔週更新連載が始まりました!! 
第4回の記事も公開されたので、一読をおすすめします。

* [「第1回 全文検索エンジンgroongaを紹介します！」](http://gihyo.jp/dev/clip/01/groonga/0001)
* [「第2回 groongaをRuby On Railsでも使ってみた ～chikamap.comの事例から」](http://gihyo.jp/dev/clip/01/groonga/0002)
* [「第3回 すべてのMySQLユーザに高速な全文検索機能を！ - mroongaの紹介」](http://gihyo.jp/dev/clip/01/groonga/0003)
* [「第4回 rroongaを使ったソースコード検索エンジンMilkode」](http://gihyo.jp/dev/clip/01/groonga/0004)

連載は始まりましたが、利用事例をどんどん紹介していきたいです。(利用事例のストックが尽きたら終了してしまいます。)
groongaやmroonga、rroongaを実際に使っていて利用事例記事を書いてもいいよ、という人をまだまだ募集しています。
詳細は[groonga普及のための協力のお願い](http://sourceforge.jp/projects/groonga/lists/archive/dev/2013-February/001186.html)を参照してください。

# はじめに

MySQLで高速に全文検索するためのオープンソースのストレージエンジンとして[mroonga](http://mroonga.org/ja/)を公開しています。
最新のバージョンは2013年5月29日にリリースした[3.04](http://mroonga.org/ja/docs/news.html#release-3-04-2013-5-29)です。

今回は、mroongaでカラムごとに重み付けして検索する方法を紹介します。

# 重み付けと全文検索

例えば、本にコメントをつけているときのことを考えてみましょう。
``books`` テーブルがあって、``books`` テーブルにはタイトルを保持する ``title`` カラムとコメントを記録するための ``comment`` カラムがあるとします。

サンプルのスキーマ定義は以下のようになるでしょう。

```
CREATE TABLE books (
  `id` INTEGER AUTO_INCREMENT,
  `title` text,
  `comment` text,
  PRIMARY KEY(`id`),
  FULLTEXT INDEX content_index (title, comment)
) engine=mroonga default charset utf8;
```

上記サンプルのためのサンプルデータを用意します。

```
INSERT INTO books (title, comment) VALUES ('[groonga入門]', 'groongaの初歩的な内容のみ。');
INSERT INTO books (title, comment) VALUES ('[mroonga入門]', 'mroongaの初歩的な内容のみ。groongaについても触れている。');
INSERT INTO books (title, comment) VALUES ('[実践groonga - groonga入門の次のステップ]', '入門を卒業したら読む本。');
INSERT INTO books (title, comment) VALUES ('[実践rroonga]', '入門の次に読むべき本。すぐに使える実践的な内容が売り。');
INSERT INTO books (title, comment) VALUES ('[mroongaのレシピ]', '入門の次に読むべき実践的な本');
INSERT INTO books (title, comment) VALUES ('[groongaのレシピ]', 'いわゆるレシピブック。');
```

``title``もしくは``comment``カラムからgroongaを検索してみましょう。

```
SELECT * FROM books WHERE
MATCH (title,comment) AGAINST ('+groonga' IN BOOLEAN MODE) ORDER BY
MATCH (title,comment) AGAINST ('+groonga' IN BOOLEAN MODE) DESC;
```

この結果は以下のようになります。

```
id	title	comment
1	[groonga入門]	groongaの初歩的な内容のみ。
3	[実践groonga - groonga入門の次のステップ]	入門を卒業したら読む本。
2	[mroonga入門]	mroongaの初歩的な内容のみ。groongaについても触れている。
6	[groongaのレシピ]	いわゆるレシピブック。
```

確かに``groonga``を含むものを検索できているのですが、``[mroonga入門]``が``[groongaのレシピ]``よりも上位になっています。
groongaを主に検索したい場合はこれではあまり嬉しくありません。

``title``カラムの内容を重視して検索することはできないでしょうか。

3.04以前でも``title``カラムを重視して検索することは、実はできます。

``MATCH (column) AGAINST(...) * (重み付けの値)``をクエリに指定してあげればいいのです。

そこで、次のようなクエリを試してみたくなったかも知れません。

```
SELECT * FROM books WHERE
MATCH (title,comment) AGAINST('+groonga' IN BOOLEAN MODE) ORDER BY
MATCH (title) AGAINST('+groonga' IN BOOLEAN MODE) * 10 DESC, MATCH(comment) AGAINST('+groonga' IN BOOLEAN MODE) DESC;
```

でも、実際に試してみると、以下のような結果になります。なんだかもっと意図しない結果になってしまっています。

```
id	title	comment
1	[groonga入門]	groongaの初歩的な内容のみ。
2	[mroonga入門]	mroongaの初歩的な内容のみ。groongaについても触れている。
3	[実践groonga - groonga入門の次のステップ]	入門を卒業したら読む本。
6	[groongaのレシピ]	いわゆるレシピブック。
```

これは、``title``カラム、``comment``カラムにインデックスを張っていなかったからでした。
インデックスを張るようにスキーマを修正しないといけません。例えば以下のようにします。

```
FULLTEXT INDEX title_index (title),
FULLTEXT INDEX comment_index (comment),
FULLTEXT INDEX content_index (title, comment)
```

個別にインデックスを張るようにしてから、さきほどのクエリを再度実行してみましょう。
以下のように``title``をより重視して検索できていることがわかります。

```
id	title	comment
3	[実践groonga - groonga入門の次のステップ]	入門を卒業したら読む本。
1	[groonga入門]	groongaの初歩的な内容のみ。
6	[groongaのレシピ]	いわゆるレシピブック。
2	[mroonga入門]	mroongaの初歩的な内容のみ。groongaについても触れている。
```

でも、このためだけにカラムごとにインデックスを張っておかないといけないのはなんだか無駄ですね。
良い方法はないでしょうか。

# Wプラグマとは

mroonga 3.04では 'W'プラグマというものがサポートされました。

'W'プラグマというのは [Tritonn](http://qwik.jp/senna/query.html)に由来するマルチカラムインデックスのセクションごとに重み付けを指定するための書式です。

'W'プラグマの書式は以下の通りです。

```
*W[数値[:重み][,数値[:重み]]...
```

では、'W'プラグマを使って検索してみましょう。

``title``カラムに重み10を与え、``comment``カラムに重み1を与えるクエリは以下のようになります。

Mroonga 3.11まではこうなります。

```
SELECT * FROM books WHERE
MATCH (title,comment) AGAINST('*W0:10,1:1 +groonga' IN BOOLEAN MODE) ORDER BY
MATCH (title,comment) AGAINST('*W0:10,1:1 +groonga' IN BOOLEAN MODE) DESC;
```

Mroonga 3.12以降(セクションの指定はTritonn同様に1オリジンになりました。)だとこうなります。

```
SELECT * FROM books WHERE
MATCH (title,comment) AGAINST('*W1:10,2:1 +groonga' IN BOOLEAN MODE) ORDER BY
MATCH (title,comment) AGAINST('*W1:10,2:1 +groonga' IN BOOLEAN MODE) DESC;
```

'W'プラグマを使う場合、``title``や``comment``カラムごとにインデックスを張る必要はありません。
最初のスキーマで指定したマルチカラムインデックスさえあれば十分です。

クエリの実行結果は以下のようになります。

```
id	title	comment
3	[実践groonga - groonga入門の次のステップ]	入門を卒業したら読む本。
1	[groonga入門]	groongaの初歩的な内容のみ。
6	[groongaのレシピ]	いわゆるレシピブック。
2	[mroonga入門]	mroongaの初歩的な内容のみ。groongaについても触れている。
```

個別のカラムにインデックスを張ったとき同様の結果がマルチカラムインデックスのみでも得られました。

# まとめ

今回は、mroongaでカラムごとに重み付けして検索する方法を紹介しました。

mroongaに興味を持ったなら、まずは[インストール](http://mroonga.org/ja/docs/install.html)して試してみてください。

mroongaの基本的な動作を知るための[ユーザガイド](http://mroonga.org/ja/docs/userguide.html)もあります。インストールしたら試してみてください。