null+****@clear*****
null+****@clear*****
2011年 9月 28日 (水) 01:06:53 JST
Kouhei Sutou 2011-09-27 16:06:53 +0000 (Tue, 27 Sep 2011)
New Revision: 9fddbfa6c6d9ab9d49afaa518a7a48c0edee9587
Log:
[doc][storage-mode] add description about new features; parser and geo search.
Modified files:
doc/ja/source/userguide/storage.rst
Modified: doc/ja/source/userguide/storage.rst (+240 -124)
===================================================================
--- doc/ja/source/userguide/storage.rst 2011-09-27 16:06:13 +0000 (d79d882)
+++ doc/ja/source/userguide/storage.rst 2011-09-27 16:06:53 +0000 (5509140)
@@ -8,74 +8,184 @@
全文検索の利用方法
------------------
-インストールが確認できたら、テーブルを1つ作成してみましょう。 ::
+インストールが確認できたら、テーブルを1つ作成してみましょう。 ``ENGINE = groonga`` とgroongaストレージエンジンを指定するところがポイントです。::
- mysql> CREATE TABLE t1 (
- > c1 INT PRIMARY KEY,
- > c2 VARCHAR(255),
- > FULLTEXT INDEX (c2)
- > ) ENGINE = groonga DEFAULT CHARSET utf8;
- Query OK, 0 rows affected (0.22 sec)
+ mysql> CREATE TABLE diaries (
+ -> id INT PRIMARY KEY AUTO_INCREMENT,
+ -> content VARCHAR(255),
+ -> FULLTEXT INDEX (content)
+ -> ) ENGINE = groonga DEFAULT CHARSET utf8;
+ Query OK, 0 rows affected (0.22 sec)
INSERTでデータを投入してみましょう。 ::
- mysql> INSERT INTO t1 VALUES(1, "明日の天気は晴れでしょう。");
- Query OK, 1 row affected (0.01 sec)
-
- mysql> INSERT INTO t1 VALUES(2, "明日の天気は雨でしょう。");
- Query OK, 1 row affected (0.04 sec)
+ mysql> INSERT INTO diaries VALUES ("明日の天気は晴れでしょう。");
+ Query OK, 1 row affected (0.01 sec)
+
+ mysql> INSERT INTO diaries VALUES ("明日の天気は雨でしょう。");
+ Query OK, 1 row affected (0.04 sec)
全文検索を実行してみます。 ::
- mysql> SELECT * FROM t1 WHERE MATCH(c2) AGAINST("晴れ");
- +----+-----------------------------------------+
- | c1 | c2 |
- +----+-----------------------------------------+
- | 1 | 明日の天気は晴れでしょう。 |
- +----+-----------------------------------------+
- 1 row in set (0.02 sec)
+ mysql> SELECT * FROM diaries WHERE MATCH(content) AGAINST("晴れ");
+ +----+-----------------------------------------+
+ | id | content |
+ +----+-----------------------------------------+
+ | 1 | 明日の天気は晴れでしょう。 |
+ +----+-----------------------------------------+
+ 1 row in set (0.02 sec)
おぉぉー。検索できましたね。
-
検索スコアの取得方法
--------------------
+.. note::
+
+ 1.0.0以前のgroongaストレージエンジンではMySQLの標準的な検索スコアの取得方法ではなく、 ``_score`` という専用のカラムを作成するという独自の方法でした。1.0.0からはMySQLの標準的な取得方法になっています。
+
全文検索を行う際、指定したキーワードにより内容が一致するレコードを上位に表示したいというような場合があります。そうしたケースでは検索スコアを利用します。
-検索スコアを取得するためには、テーブル定義時に ``_score`` という名前のカラムを作成して下さい。 ::
+検索スコアはMySQLの標準的な方法で取得できます。つまり、SELECTの取得するカラム名を指定するところやORDER BYのところにMATCH...AGAINSTを指定します。
- mysql> CREATE TABLE t1 (
- > c1 INT PRIMARY KEY,
- > c2 TEXT,
- > _score FLOAT,
- > FULLTEXT INDEX (c2)
- > ) ENGINE = groonga DEFAULT CHARSET utf8;
- Query OK, 0 rows affected (0.22 sec)
+それでは実際にやってみましょう。::
-_scoreカラムのデータ型はFLOATまたはDOUBLEである必要があります。
+ mysql> INSERT INTO diaries VALUES ("今日は晴れました。明日も晴れるでしょう。");
+ Query OK, 1 row affected (0.00 sec)
-INSERTでテーブルにレコードを追加してみましょう。_scoreカラムは仮想カラムとして実装されており、更新は行えません。更新対象から外すか、値に ``null`` を使用する必要があります。 ::
+ mysql> INSERT INTO diaries VALUES ("今日は晴れましたが、明日は雨でしょう。");
+ Query OK, 1 row affected (0.00 sec)
- mysql> insert into t1 values(1, "aa ii uu ee oo", null);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> insert into t1 values(2, "aa ii ii ii oo", null);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> insert into t1 values(3, "dummy", null);
- Query OK, 1 row affected (0.00 sec)
+ mysql> SELECT *, MATCH (content) AGAINST ("晴れ") FROM diaries WHERE MATCH (content) AGAINST ("晴れ") ORDER BY MATCH (content) AGAINST ("晴れ") DESC;
+ +----+----------------+--------+
+ | id | content | MATCH(content) AGAINST("晴れ") |
+ +----+----------------+--------+
+ | 2 | aa ii ii ii oo | 3 |
+ | 1 | aa ii uu ee oo | 1 |
+ +----+----------------+--------+
+ 2 rows in set (0.00 sec)
+
+検索スコアもORDER BYでのソートも効いていますね!
+
+全文検索用パーサの変更
+----------------------
+
+MySQLは全文検索用のパーサ [#parser]_ を指定する以下のような構文を持っています。::
+
+ FULLTEXT INDEX (content) WITH PARSER パーサ名
+
+しかし、この構文を利用する場合は、あらかじめすべてのパーサをMySQLに登録しておく必要があります。一方、groongaはトークナイザー(MySQLでいうパーサ)を動的に追加することができます。そのため、groognaストレージエンジンでもこの構文を採用するとgroonga側に動的に追加されたトークナイザーに対応できなくなります。groongaに動的に追加されるトークナイザーにはMeCabを用いたトークナイザーもあり、この制限に縛られることは利便性を損なうと判断し、以下のようなコメントを用いた独自の構文を採用することにしました。::
+
+ FULLTEXT INDEX (content) COMMENT 'parser "TokenMecab"'
+
+パーサに指定できるのは以下の値です。
+
+TokenBigram
+ バイグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、3文字以上のトークンも存在する。これはノイズを減らすためである。
+
+ デフォルト値。
+
+TokenMecab
+ MeCabを用いてトークナイズする。groongaがMeCabサポート付きでビルドされている必要がある。
+
+TokenBigramSplitSymbol
+ バイグラムでトークナイズする。TokenBigramと異なり、記号が連続していても特別扱いして1つのトークンとして扱わず通常のバイグラムの処理を行う。
+
+ TokenBigramではなくTokenBigramSplitSymbolを利用すると「Is it really!?!?!?」の「!?!?!?」の部分に「!?」でマッチする。TokenBigramの場合は「!?!?!?」でないとマッチしない。
+
+TokenBigramSplitSymbolAlpha
+ バイグラムでトークナイズする。TokenBigramSplitSymbolに加えて、連続したアルファベットも特別扱いせずに通常のバイグラムの処理を行う。
+
+ TokenBigramではなくTokenBigramSplitSymbolAlphaを利用すると「Is it really?」に「real」でマッチする。TokenBigramの場合は「really」でないとマッチしない。
+
+TokenBigramSplitSymbolAlphaDigit
+ バイグラムでトークナイズする。TokenBigramSplitSymbolAlphaに加えて、連続した数字も特別扱いせずに通常のバイグラムの処理を行う。つまり、すべての字種を特別扱いせずにバイグラムの処理を行う。
+
+ TokenBigramではなくTokenBigramSplitSymbolAlphaDigitを利用すると「090-0123-4567」に「567」でマッチする。TokenBigramの場合は「4567」でないとマッチしない。
+
+TokenBigramIgnoreBlank
+ バイグラムでトークナイズする。TokenBigramと異なり、空白を無視して処理する。
+
+ TokenBigramではなくTokenBigramIgnoreBlankを利用すると「み な さ ん 注 目」に「みなさん」でマッチする。TokenBigramの場合は「み な さ ん」でないとマッチしない。
+
+TokenBigramIgnoreBlankSplitSymbol
+ バイグラムでトークナイズする。TokenBigramSymbolと異なり、空白を無視して処理する。
+
+ TokenBigramSplitSymbolではなくTokenBigramIgnoreBlankSplitSymbolを利用すると「! !? ??」に「???」でマッチする。TokenBigramSplitBlankの場合は「? ??」でないとマッチしない。
+
+TokenBigramIgnoreBlankSplitSymbolAlpha
+ バイグラムでトークナイズする。TokenBigramSymbolAlphaと異なり、空白を無視して処理する。
+
+ TokenBigramSplitSymbolAlphaではなくTokenBigramIgnoreBlankSplitSymbolAlphaを利用すると「I am a pen.」に「ama」でマッチする。TokenBigramSplitBlankAlphaの場合は「am a」でないとマッチしない。
-全文検索(MATCH...AGAINSTによる検索)を実行した場合、_scoreカラムを通じて検索スコアを取得できます。_scoreカラムをORDER BYに指定することで結果のソートも行うことができます。 ::
+TokenBigramIgnoreBlankSplitSymbolAlphaDigit
+ バイグラムでトークナイズする。TokenBigramSymbolAlphaDigitと異なり、空白を無視して処理する。
- mysql> select * from t1 where match(c2) against("ii") order by _score desc;
- +----+----------------+--------+
- | c1 | c2 | _score |
- +----+----------------+--------+
- | 2 | aa ii ii ii oo | 3 |
- | 1 | aa ii uu ee oo | 1 |
- +----+----------------+--------+
- 2 rows in set (0.00 sec)
+ TokenBigramSplitSymbolAlphaDigitではなくTokenBigramIgnoreBlankSplitSymbolAlphaDigitを利用すると「090 0123 4567」に「9001」でマッチする。TokenBigramSplitBlankAlphaDigitの場合は「90 01」でないとマッチしない。
+
+TokenDelimit
+ 空白区切りでトークナイズする。
+
+ 「映画 ホラー 話題」は「映画」・「ホラー」・「話題」にトークナイズされる。
+
+TokenDelimitNull
+ null文字(\\0)区切りでトークナイズする。
+
+ 「映画\\0ホラー\\0話題」は「映画」・「ホラー」・「話題」にトークナイズされる。
+
+TokenUnigram
+ ユニグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、2文字以上のトークンも存在する。これはノイズを減らすためである。
+
+TokenTrigram
+ トリグラムでトークナイズする。ただし、連続したアルファベット・連続した数字・連続した記号はそれぞれ1つのトークンとして扱う。そのため、4文字以上のトークンも存在する。これはノイズを減らすためである。
+
+デフォルトのパーサは ``configure`` の ``--with-default-parser`` オプションでビルド時に指定することができます。::
+
+ ./configure --with-default-parser TokenMecab ...
+
+また、my.confまたはSQL内で ``groonga_default_parser`` 変数を指定することでも指定できます。
+
+my.conf::
+
+ ... TODO ...
+
+SQL::
+
+ mysql> SET GLOBAL groonga_default_parser = TokenMecab;
+ ... TODO ...
+
+位置情報検索の利用方法
+----------------------
+
+ストレージモードでは全文検索だけではなく位置情報検索も高速に実行できます。ただし、MyISAMとは異なりデータとして格納できるのはPOINT型のみです。LINEなどの他のデータ型は保存できません。また、インデックスを用いた高速な検索に対応しているのはMBRContainsだけです。MBRDisjointなどには対応していません。
+
+位置情報検索を利用する場合のテーブル定義はMyISAMと同様にPOINT型のカラムを定義し、そのカラムに対してSPATIAL INDEXを指定します。::
+
+ mysql> CREATE TABLE shops (
+ -> id INT PRIMARY KEY AUTO_INCREMENT,
+ -> name VARCHAR(255),
+ -> location POINT NOT NULL,
+ -> SPATIAL INDEX (location)
+ -> ) ENGINE = groonga;
+ ... TODO ...
+
+データの登録方法もMyISAMのときと同様にGeomFromText()関数を使って文字列からPOINT型の値を作成します。::
+
+ mysql> INSERT INTO shops VALUES ('根津のたいやき', GeomFromText('POINT(139.762573 35.720253)'));
+ ... TODO ...
+
+ mysql> INSERT INTO shops VALUES ('浪花家', GeomFromText('POINT(139.796234 35.730061)'));
+ ... TODO ...
+
+ mysql> INSERT INTO shops VALUES ('柳屋 たい焼き', GeomFromText('POINT(139.783981 35.685341)');
+ ... TODO ...
+
+池袋駅(139.7101 35.7292)が左上の点、東京駅(139.7662 35.6815)が右下の点となるような長方形内にあるお店を探す場合は以下のようなSELECTになります。::
+
+ mysql> SELECT * FROM shops WHERE MBRContains(location, GeomFromText('LINE(139.7101 35.7292, 139.7662 35.6815)'));
+ ... TODO ...
+
+位置情報で検索できていますね!
レコードIDの取得方法
--------------------
@@ -86,9 +196,9 @@ groongaストレージエンジンではアプリケーションの開発を容
レコードIDを取得するためには、テーブル定義時に ``_id`` という名前のカラムを作成して下さい。 ::
- mysql> CREATE TABLE t1 (
+ mysql> CREATE TABLE memos (
-> _id INT,
- -> foo INT,
+ > content VARCHAR(255),
-> UNIQUE KEY (_id) USING HASH
-> ) ENGINE = groonga;
Query OK, 0 rows affected (0.01 sec)
@@ -100,43 +210,44 @@ _idカラムのデータ型は整数型(TINYINT、SMALLINT、MEDIUMINT、INT、B
INSERTでテーブルにレコードを追加してみましょう。_idカラムは仮想カラムとして実装されており、また_idの値であるレコードIDはgroongaにより割当てられるため、SQLによる更新時に値を指定することはできません。
更新対象から外すか、値に ``null`` を使用する必要があります。 ::
- mysql> INSERT INTO t1 VALUES (null, 100);
+ mysql> INSERT INTO memo VALUES (null, "今夜はさんま。");
Query OK, 1 row affected (0.00 sec)
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
+ mysql> INSERT INTO memo VALUES (null, "明日のgroongaをアップデート。");
+ Query OK, 1 row affected (0.00 sec)
- mysql> INSERT INTO t1 VALUES (null, 100);
+ mysql> INSERT INTO memo VALUES (null, "帰りにおだんご。");
Query OK, 1 row affected (0.00 sec)
- mysql> INSERT INTO t1 VALUES (null, 100);
+ mysql> INSERT INTO memo VALUES (null, "金曜日は肉の日。");
Query OK, 1 row affected (0.00 sec)
レコードIDを取得するには、_idカラムを含むようにしてSELECTを行います。 ::
- mysql> select * from t1;
- +------+------+
- | _id | foo |
- +------+------+
- | 1 | 100 |
- | 2 | 100 |
- | 3 | 100 |
- | 4 | 100 |
- +------+------+
- 4 rows in set (0.00 sec)
+ mysql> SELECT * FROM memo;
+ ... TODO ...
+ +------+------+
+ | _id | foo |
+ +------+------+
+ | 1 | 100 |
+ | 2 | 100 |
+ | 3 | 100 |
+ | 4 | 100 |
+ +------+------+
+ 4 rows in set (0.00 sec)
また直前のINSERTにより割当てられたレコードIDについては、last_insert_grn_id関数により取得することもできます。 ::
- mysql> INSERT INTO t1 VALUES (null, 100);
- Query OK, 1 row affected (0.00 sec)
-
- mysql> SELECT last_insert_grn_id();
- +----------------------+
- | last_insert_grn_id() |
- +----------------------+
- | 5 |
- +----------------------+
- 1 row in set (0.00 sec)
+ mysql> INSERT INTO memo VALUES (null, "冷蔵庫に牛乳が残り1本。");
+ Query OK, 1 row affected (0.00 sec)
+
+ mysql> SELECT last_insert_grn_id();
+ +----------------------+
+ | last_insert_grn_id() |
+ +----------------------+
+ | 5 |
+ +----------------------+
+ 1 row in set (0.00 sec)
last_insert_grn_id関数はユーザ定義関数(UDF)としてgroongaストレージエンジンに含まれていますが、インストール時にCREATE FUNCTIONでMySQLに追加していない場合には、以下の関数定義DDLを実行しておく必要があります。 ::
@@ -144,7 +255,8 @@ last_insert_grn_id関数はユーザ定義関数(UDF)としてgroongaストレ
ご覧のように_idカラムやlast_insert_grn_id関数を通じてレコードIDを取得することができました。ここで取得したレコードIDは後続のUPDATEなどのSQL文で利用すると便利です。 ::
- mysql> UPDATE t1 SET foo = 200 WHERE _id = 5;
+ mysql> UPDATE memo SET content = "冷蔵庫に牛乳はまだたくさんある。" WHERE _id = 5;
+ ... TODO ...
ログ出力
--------
@@ -155,32 +267,32 @@ groongaストレージエンジンではデフォルトでログの出力を行
以下はログの出力例です。 ::
- 2010-10-07 17:32:39.209379|n|b1858f80|groonga-storage-engine started.
- 2010-10-07 17:32:44.934048|d|46953940|hash get not found (key=test)
- 2010-10-07 17:32:44.936113|d|46953940|hash put (key=test)
+ 2010-10-07 17:32:39.209379|n|b1858f80|groonga-storage-engine started.
+ 2010-10-07 17:32:44.934048|d|46953940|hash get not found (key=test)
+ 2010-10-07 17:32:44.936113|d|46953940|hash put (key=test)
ログのデフォルトの出力レベルはNOTICE(必要な情報のみ出力。デバッグ情報などは出力しない)となっております。
ログの出力レベルは ``groonga_log_level`` というシステム変数で確認することができます(グローバル変数)。またSET文で動的に出力レベルを変更することもできます。 ::
- mysql> SHOW VARIABLES LIKE 'groonga_log_level';
- +-------------------+--------+
- | Variable_name | Value |
- +-------------------+--------+
- | groonga_log_level | NOTICE |
- +-------------------+--------+
- 1 row in set (0.00 sec)
-
- mysql> SET GLOBAL groonga_log_level=DUMP;
- Query OK, 0 rows affected (0.05 sec)
-
- mysql> SHOW VARIABLES LIKE 'groonga_log_level';
- +-------------------+-------+
- | Variable_name | Value |
- +-------------------+-------+
- | groonga_log_level | DUMP |
- +-------------------+-------+
- 1 row in set (0.00 sec)
+ mysql> SHOW VARIABLES LIKE 'groonga_log_level';
+ +-------------------+--------+
+ | Variable_name | Value |
+ +-------------------+--------+
+ | groonga_log_level | NOTICE |
+ +-------------------+--------+
+ 1 row in set (0.00 sec)
+
+ mysql> SET GLOBAL groonga_log_level=DUMP;
+ Query OK, 0 rows affected (0.05 sec)
+
+ mysql> SHOW VARIABLES LIKE 'groonga_log_level';
+ +-------------------+-------+
+ | Variable_name | Value |
+ +-------------------+-------+
+ | groonga_log_level | DUMP |
+ +-------------------+-------+
+ 1 row in set (0.00 sec)
設定可能なログレベルは以下の通りです。
@@ -209,20 +321,20 @@ groongaでは各カラムごとにファイルを分けてデータを格納す
例えば以下のようにカラムが20個定義されているテーブルが存在するものと仮定します。 ::
- CREATE TABLE t1 (
- c1 INT PRIMARY KEY AUTO_INCREMENT,
- c2 INT,
- c3 INT,
- ...
- c11 VARCHAR(20),
- c12 VARCHAR(20),
- ...
- c20 DATETIME
- ) ENGINE = InnoDB DEFAULT CHARSET utf8;
+ CREATE TABLE t1 (
+ c1 INT PRIMARY KEY AUTO_INCREMENT,
+ c2 INT,
+ c3 INT,
+ ...
+ c11 VARCHAR(20),
+ c12 VARCHAR(20),
+ ...
+ c20 DATETIME
+ ) ENGINE = InnoDB DEFAULT CHARSET utf8;
この時、以下のようなSELECT文が発行される場合、groongaストレージエンジンではSELECT句およびWHERE句で参照しているカラムに対してのみデータの読み取りを行ってSQL文を処理します(内部的に不要なカラムに対してはアクセスしません)。 ::
- SELECT c1, c2, c11 FROM t1 WHERE c2 = XX AND c12 = "XXX";
+ SELECT c1, c2, c11 FROM t1 WHERE c2 = XX AND c12 = "XXX";
このケースではc1,c2,c11,c12に対してのみアクセスが行われ、SQL文が高速に処理されることになります。
@@ -237,17 +349,17 @@ groongaストレージエンジンの前身であるTritonn(MySQL+Senna)では
例えば以下のSELECT文では不要なカラムデータの読み取りは省略され、必要最小限のコストで行カウントの結果を返すことができます。 ::
- SELECT COUNT(*) FROM t1 WHERE MATCH(c2) AGAINST("hoge");
+ SELECT COUNT(*) FROM t1 WHERE MATCH(c2) AGAINST("hoge");
行カウント高速化の処理が行われたかどうかはステータス変数で確認することもできます。::
- mysql> show status like 'groonga_count_skip';
- +--------------------+-------+
- | Variable_name | Value |
- +--------------------+-------+
- | groonga_count_skip | 1 |
- +--------------------+-------+
- 1 row in set (0.00 sec)
+ mysql> show status like 'groonga_count_skip';
+ +--------------------+-------+
+ | Variable_name | Value |
+ +--------------------+-------+
+ | groonga_count_skip | 1 |
+ +--------------------+-------+
+ 1 row in set (0.00 sec)
行カウント高速化の処理が行われる度に ``groonga_count_skip`` ステータス変数がインクリメントされます。
@@ -266,17 +378,17 @@ groongaストレージエンジンでも ORDER BY LIMIT を高速化するため
例えば以下のSELECT文では ORDER BY LIMIT は、groonga内で処理され、必要最小限のレコードだけをMySQLに返却しています。 ::
- SELECT * FROM t1 WHERE MATCH(c2) AGAINST("hoge") ORDER BY c1 LIMIT 1;
+ SELECT * FROM t1 WHERE MATCH(c2) AGAINST("hoge") ORDER BY c1 LIMIT 1;
ORDER BY LIMIT 高速化の処理が行われたかどうかはステータス変数で確認することもできます。::
- mysql> show status like 'groonga_fast_order_limit';
- +--------------------------+-------+
- | Variable_name | Value |
- +--------------------------+-------+
- | groonga_fast_order_limit | 1 |
- +--------------------------+-------+
- 1 row in set (0.00 sec)
+ mysql> show status like 'groonga_fast_order_limit';
+ +--------------------------+-------+
+ | Variable_name | Value |
+ +--------------------------+-------+
+ | groonga_fast_order_limit | 1 |
+ +--------------------------+-------+
+ 1 row in set (0.00 sec)
ORDER BY LIMIT 高速化の処理が行われる度に ``groonga_fast_order_limit`` ステータス変数がインクリメントされます。
@@ -285,4 +397,8 @@ ORDER BY LIMIT 高速化の処理が行われる度に ``groonga_fast_order_limi
* where句がmatch...againstのみ
* joinしていない
* limitの指定がある
-* order byの指定がカラムである(_score、_id含む)
+* order byの指定がカラム(_id含む)またはwhere句に指定したmatch...againstである
+
+.. rubric:: 脚注
+
+.. [#parser] groongaではトークナイザーと呼んでいる。