Groongaでインデックスを定義するときはどうしてテーブル定義から始まるのか

## はじめに

データベース管理システムでデータベースにインデックスを定義するときは、どのようなインデックスにするかを指定します。例えば、MySQLでは次のようにインデックスを定義します。

```sql
CREATE TABLE memos (
  content TEXT,
  FULLTEXT INDEX (content) -- インデックス定義
);
```

この定義は「`content`カラムに全文検索用のインデックスを作る」という意味です。

一方、Groongaでは次のような定義になります。

```text
table_create Memos TABLE_NO_KEY
column_create Memos content COLUMN_SCALAR Text

# ↓インデックス用のテーブル定義
table_create Terms TABLE_PAT_KEY \
  --default_tokenizer TokenBigram \
  --normalizer NormalizerAuto
# ↓インデックスの定義
column_create Terms memos_content COLUMN_INDEX|WITH_SECTION Memos content
```

インデックスを定義する前にインデックス用のテーブル（`Terms`）から定義しています。どうしてGroongaではインデックスだけでなくインデックス用のテーブルも定義しないといけないのでしょうか。

## インデックス用のテーブルとはなにか

そもそも「インデックス用のテーブル」とはなんでしょうか。これは他のデータベース管理システムのインデックスでは[B-tree](http://ja.wikipedia.org/wiki/B%E6%9C%A8)で実装されている部分に相当します。つまり、キーから該当レコードを高速に検索するための補助データです。

ピンとこないですよね。もう少し噛み砕いて説明します。

次の3つのレコードがあるとします。

| id | tag  |
|:--:|:----:|
| 1  | tag1 |
| 2  | tag2 |
| 3  | tag1 |

この中から`tag`が「tag1」のレコードを検索することを考えます。

タグに対するインデックスは次のようになります。ヒットするレコードを事前に調べて格納しています。

| tag  | ヒットするレコード |
|:----:|:-----------------:|
| tag1 | 1, 3              |
| tag2 | 2                 |

このインデックスを使って検索するには次のようにします。

  1. `tag`が「tag1」の行を見つける（1番目の行）
  2. ↑で見つけた行の`ヒットするレコード`の列にあるレコードを返す

ポイントは、1番目の『`tag`が「tag1」の行を見つける』ところです。ここが速く見つけられれば検索処理全体が速くなります。なぜなら、2番目の「レコードを返す」ところは単にデータを返すだけだからです。

多くのデータベース管理システムでは、『`tag`が「tag1」の行を見つける』をB-treeの特性を使って高速に実行します。そして、Groongaで『`tag`が「tag1」の行を見つける』ところを高速に実行しているのが「インデックス用のテーブル」です。

多くのデータベース管理システムでは、「インデックス」には次の2つが含まれています。

  * キーで高速に検索する機能
  * キーに対応する値としてヒットするレコードの情報を格納する機能

一方、Groongaはこの2つをわけて指定できるようにしています。そして、それぞれ次のように呼んでいます。

  * キーで高速に検索する機能→語彙表（lexicon、インデックス用のテーブル）
  * キーに対応する値としてヒットするレコードの情報を格納する機能→インデックスカラム（`COLUMN_INDEX`を指定して作ったカラム）

つまり、インデックス用のテーブルとは語彙表のことです。なお、Groongaでは語彙表は通常のテーブルと同じです。インデックス用のテーブルを語彙表と呼んでいるだけです。

## どうして語彙表とインデックスカラムをわけるのか

他のデータベース管理システムでいう「インデックス」を、Groongaでは「語彙表」と「インデックスカラム」にわけていることはわかりました。では、どうしてわけているのでしょうか？わけているメリットがなければ単に使いにくいだけです。

語彙表をわけるメリットは次の通りです。

  * 複数のインデックスカラムで語彙表を共有することによりデータベース全体の空間効率がよくなる（データ量が減る）

実は、わけることだけにメリットがあるわけではありません。語彙表を通常のテーブルと同じように扱えることにもメリットがあります。

語彙表を通常のテーブルと同じように扱えると次のようなメリットがあります。

  * 語彙表にカラムを追加することで語彙毎にメタデータを付与できる

たとえば、どの語彙をストップワードにするかといったメタデータを付与しておけば、通常のテーブル操作でストップワードを管理することができます。あわせて、どうしてストップワード扱いにしたかという理由を記録するカラムも用意すればあとで確認したときにわかりやすくなります。

| tag  | ヒットするレコード | ストップワード？ | ストップワードにした理由                                       |
|:----:|:-----------------:|:---------------:|:--------------------------------------------------------------:|
| tag1 | 1, 3              | true            | ほとんどのレコードにこのタグがついているので検索効果が小さいから |
| tag2 | 2                 | false           |                                                                |

他にも、語彙にラベルを付与するという使い方ができます。タグ検索をするときはタグが語彙に対応します。タグは検索時のゆらぎを吸収するために正規化します。たとえば、「Groonga」でも「GROONGA」でも検索できるように、内部ではすべて小文字の「groonga」に正規化して扱うということです。検索するときは多少ゆらぎがあっても見つかって欲しいですが、表示するときは正式表記を使いたいものです。そんなときは、語彙表に`label`カラムを追加して、「groonga」という語彙（タグ）に「Groonga」というラベルを付加します。こうしておけば、表示するときは`label`カラムを使って正式表記を使えます。

| tag     | ヒットするレコード | label   |
|:-------:|:-----------------:|:-------:|
| groonga | 1, 3              | Groonga |
| mroonga | 2                 | Mroonga |

## まとめ

Groonga以外のデータベース管理システムでは、インデックスを定義するときは事前にインデックス用のテーブルを定義する必要はありませんが、Groongaではインデックス用のテーブルを定義する必要があります。これは、はじめてGroongaを触った人にとってわかりにくいポイントなのですが、この仕組みにはメリットがあるということを説明しました。

この仕組みがピンとくると一気にGroongaと仲良くなれます。ぜひ、この仕組みをわかって、Groongaと仲良くしてください。

なお、GroongaをMySQLから使うことができるMroongaでは、この仕組みを知らなくても使えるようになっています。つまり、ユーザーがインデックスを定義したらMroongaが内部で自動でインデックス用のテーブルを作ってくれます。Groongaの細かいことは知りたくないけど、Groongaの速度は欲しい！という方はMroongaをオススメします。ちなみに、MySQLらしさが減っていくのですが、MroongaからGroongaの細かいことを操作する方法が用意されています。興味のある方は[MroongaでGroongaの機能を使いこなす高度なテーブル設計をする方法 - CreateField Blog](http://blog.createfield.com/entry/2014/10/29/084941)も読んでみてください。

## おしらせ

### Groongaドキュメント読書会6

2014/12/11（木）に第6回目の[Groongaドキュメント読書会](http://groonga.doorkeeper.jp/events/17229)が開催されます。ドキュメント読書会はドキュメントを読みながらGroongaの理解を深める会です。参加者から疑問の声があがれば、ここで説明しているようなGroongaのより詳細な情報を解説しながらGroongaの理解を深めます。

ここで説明しているような話に興味のある方はぜひご参加ください。

### 全文検索エンジンGroongaを囲む夕べ5

2014/11/29（土）、年に一度のいい肉の日にGroongaイベント「[全文検索エンジンGroongaを囲む夕べ5](http://groonga.doorkeeper.jp/events/15816)」が開催されます。Groongaに興味のある方はぜひご参加ください。

### Groonga Advent Calendar 2014

[Groonga Advent Calendar 2014](http://qiita.com/advent-calendar/2014/groonga)を実施します。Groonga関連情報を持っている方はぜひご参加ください。

## 関連情報

  * [Groongaのチュートリアル](http://groonga.org/ja/docs/tutorial.html)
  * [Mroongaのチュートリアル](http://mroonga.org/ja/docs/tutorial.html)
  * [隔週連載Groonga](http://gihyo.jp/dev/clip/01/groonga)