====== Mroonga9.12 検索チュートリアル ====== Version 9.12 (MariaDB 10.4.12) --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-11-02// [[mroonga:top|Mroonga に戻る]] 関連記事 * [[mroonga:10.7:install|Mroonga9.12 インストール]] * Mroonga9.12 検索チュートリアル * [[mroonga:10.7:config|Mroonga9.12 全文検索の構成]] リンク * https://mroonga.org/ja/docs/tutorial.html --- Mroongaチュートリアル * https://mroonga.org/ja/docs/reference/full_text_search.html --- Mroonga全文検索 * https://dev.mysql.com/doc/refman/5.6/ja/fulltext-search.html --- MySQL全文検索関数 本編では、Mroonga公式サイトの[[https://mroonga.org/ja/docs/tutorial.html|チュートリアル]]を参考にしてMroongaについて使い方を検索SQLを中心に説明したいと思います。 \\ ----- ===== チュートリアル用データベースの作成 ===== ここでは phpmyadmin を使ってチュートリアルを進めます。 * http://localhost/phpmyadmin/ 最初にMroongaのテスト用のデータベースを作ります。[SQL]メニューを選択して以下を実行します。 -- Database : fulltext_test CREATE DATABASE IF NOT EXISTS `fulltext_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; -- Privileges GRANT SELECT, INSERT, DELETE, UPDATE, ALTER ON `fulltext_test`.* TO 'fulltext_test'@localhost; phpmyadmin の表示更新して、データベース ''[fulltext_test]'' の[SQL]メニューを選択します。以降、この状態でSQL文を入力しながら本章のチュートリアルを進めていきます。 \\ ===== ストレージモードとラッパーモード ===== Mroonga には以下の2つの動作モードがあります。 * ストレージモード --- 全文検索機能だけではなく全てのデータストア機能で Mroonga 利用します。InnoDB、MyISAMなど他のエンジンと同等に機能します。但し、ストレージモードではnull値に対する制限やドランザクションがサポートされていないなどの制限があります。\\ \\ * ラッパーモード --- 全文検索機能のみ Mroonga を利用し、他のデータストア機能は別のストレージエンジン(InnoDB、MyISAMなど)を利用します。 詳しくは、Mroonga のドキュメントをご覧ください。 * https://mroonga.org/ja/docs/tutorial/mode.html \\ ==== ストレージモードの利用 ==== 通常のストレージエンジンを指定する構文( ''ENGINE = Mroonga'' )を使って以下のようにSQLを書きます。 CREATE TABLE diaries ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) ) ENGINE = Mroonga COLLATE utf8_unicode_ci; INSERT INTO diaries (content) VALUES ("今日の天気は晴れでしょう。"); INSERT INTO diaries (content) VALUES ("今日の天気は雨でしょう。"); INSERT INTO diaries (content) VALUES ("明日の東京都の天気は晴れでしょう。"); INSERT INTO diaries (content) VALUES ("明日の京都の天気は雨でしょう。"); \\ ==== ラッパーモードの利用 ==== ラップする対象となるストレージエンジン(全文検索機能以外のデータストア機能を使用するエンジン)は、コメントを利用して ''COMMENT = 'engine "InnoDB"''' のように指定します。 CREATE TABLE diaries_wrapper ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) ) ENGINE = Mroonga COMMENT = 'engine "InnoDB"' COLLATE utf8_unicode_ci; INSERT INTO diaries_wrapper (content) VALUES ("今日の天気は晴れでしょう。"); INSERT INTO diaries_wrapper (content) VALUES ("今日の天気は雨でしょう。"); INSERT INTO diaries_wrapper (content) VALUES ("明日の東京都の天気は晴れでしょう。"); INSERT INTO diaries_wrapper (content) VALUES ("明日の京都の天気は雨でしょう。"); \\ ==== トランザクションの利用 ==== ストレージモードで作成した diaries テーブルに対して以下のトランザクションを実行します。 START TRANSACTION; INSERT INTO diaries (content) VALUES ("明後日は全国的に晴れるでしょう。"); ROLLBACK; ストレージモードのテーブルは、トランザクションが効かないので以下の警告が出力され、diaries テーブルに新しい行がインサートされます。 Warning: #1196 トランザクション対応ではない表への変更はロールバックされません。 同じことをラッパーモードで作成した diaries_wrapper テーブルに対して行います。 START TRANSACTION; INSERT INTO diaries_wrapper (content) VALUES ("明後日は全国的に晴れるでしょう。"); ROLLBACK; この場合、トランザクションはロールバックされ結果的に新しい行はインサートされません。 diaries_wrapper テーブルは全文検索機能のみ Mroonga を利用し、その他の機能は create 時に指定したストレージエンジンである innoDB を使っているのでトランザクションが有効に機能しています。 \\ ===== 全文検索のSQL構文 ===== ==== MATCH AGAINST 関数 ==== 全文検索のSQLには、''MATCH AGAINST'' 関数を使います。 MATCH (col1,col2,...) AGAINST (検索文字列 [search_modifier]) search_modifier: { IN NATURAL LANGUAGE MODE // 自然言語検索 | IN BOOLEAN MODE // ブール検索 } 全文検索の対象は FULLTEXT型 のカラムです。このカラムを ''MATCH( col1,col2,... )'' のように指定し、AGAINST には、検索する文字列を指定します。 本来、MySQL(MariaDB)の全文検索には次の3種類の ''search_modifier'' があり AGAINST の中で指定します。''search_modifier'' が指定されていない場合は、自然言語検索になります。 * 自然言語検索 --- ''IN NATURAL LANGUAGE MODE'' * ブール検索 --- ''IN BOOLEAN MODE'' * クエリー拡張した自然言語検索 --- ''IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION'' または ''WITH QUERY EXPANSION'' 本章では、自然言語検索とブール検索について説明します。クエリー拡張した自然言語検索についてはMroongaのドキュメントに記載がなく、ここでは割愛します。詳しくは、[[https://dev.mysql.com/doc/refman/5.6/ja/fulltext-search.html|MySQL]]のドキュメントを参照して下さい。 \\ ==== 自然言語検索 ==== 自然言語検索では、 検索文字列が自然言語でのフレーズ (フリーテキスト内に含まれるフレーズ) として解釈されます。特別な演算子はありません。ストップワードリストがあれば適用されます。 自然言語検索では検索文字列との類似度が評価され、類似度の高いものだけが出力されます。詳しくはMroongaの[[https://mroonga.org/ja/docs/reference/full_text_search/scoring.html|ドキュメント]]をご覧ください。 例1 SELECT * FROM diaries WHERE MATCH(content) AGAINST('明日の天気'); デフォルトパーサーは 2Gram なので、検索文字列が1文字おき2文字毎に機械的に分解されます。結果的に、検索文字列中の '''明日'''、'''日の'''、'''の天'''、'''天気''' にヒットする次の行が出力されます。 明日の東京都の天気は晴れでしょう。 明日の京都の天気は雨でしょう。 例2 SELECT * FROM diaries WHERE MATCH(content) AGAINST('明日の京都の天気'); 例1と同様に、検索文字列中の '''明日'''、'''日の'''、'''の京'''、'''京都'''、'''都の''' 、'''の天'''、'''天気''' にヒットする次の行が出力されます。 明日の京都の天気は雨でしょう。 それでは、次の例は、どうなるでしょう? 例3 SELECT * FROM diaries WHERE MATCH(content) AGAINST('明日 京都 天気'); 例2の名詞だけを空白文字で区切って検索文字列にしています。結果は以下のようになります。 明日の東京都の天気は晴れでしょう。 明日の京都の天気は雨でしょう。 「東京都」も「京都」を含んでいるのでこのような結果になります。例2で「明日の東京都の天気は晴れでしょう。」が出力されなかったのは、この文章には検索文字列内の「の京」が含まれていないからです。 これはパーサーが 2Gram の時に発生する現象で、検索ノイズと呼ばれるものです。この例の場合は、格助詞「の」が検索結果に影響を与えているのが分かります。この例の場合は形態素解析パーサー(MeCab)を使用することにより回避できます。それは形態素解析パーサーが日本語の文法を解釈して単語分けをするからです。いずれのパーサーを使用するかは、両者には一長一短があるので、適用する分野により良く吟味して決める必要があります。 \\ ==== ブール検索 ==== ブール検索では、検索文字列が全文検索用の特別な演算子を伴う文字列として解釈されます。文字列には、検索対象の単語(検索ワード)の前に演算子を含めることができます。ストップワードリストがあれば適用されます。 ブール検索では類似度による評価はありません。MySQL(MariaDB)の規則に従い自然言語検索(IN NATURAL LANGUAGE MODE)がデフォルトですが、ブール検索(IN BOOLEAN MODE)の方がWeb検索エンジンのクエリーと似ているので親しみやすいかもしれません。 Mrrongaのブール検索についての詳細は、こちらの[[https://mroonga.org/ja/docs/reference/full_text_search/boolean_mode.html|ドキュメント]]をご覧ください。 代表的なブール演算子は以下の通りです: * 演算子が無い場合は、暗黙的に OR を表します * ''+'' は AND を表します * ''-'' は NOT を表します 例1 SELECT * FROM diaries WHERE MATCH(content) AGAINST('明日 天気 東京' IN BOOLEAN MODE); 3つの検索ワード(明日,天気,東京)の何れかを含む以下の行が出力されます。 明日の東京都の天気は晴れでしょう。 明日の京都の天気は雨でしょう。 今日の天気は晴れでしょう。 今日の天気は雨でしょう。 例2 SELECT * FROM diaries WHERE MATCH(content) AGAINST('+明日 +天気 +東京' IN BOOLEAN MODE); 3つの検索ワード(明日,天気,東京)の全てを含む以下の行が出力されます。 明日の東京都の天気は晴れでしょう。 例3 SELECT * FROM diaries WHERE MATCH(content) AGAINST('+明日 +天気 -東京' IN BOOLEAN MODE); 2つの検索ワード(明日,天気)を含み、(東京)を含まない行が出力されます。 明日の京都の天気は雨でしょう。 例4 SELECT * FROM diaries WHERE MATCH(content) AGAINST('+明日 +天気 +京都' IN BOOLEAN MODE); 3つの検索ワード(明日,天気,東京)の全てを含む以下の行が出力されます。 明日の東京都の天気は晴れでしょう。 明日の京都の天気は雨でしょう。 自然言語検索と同様に、「東京都」も「京都」を含んでいるので「明日の東京都の天気は晴れでしょう。」が検索ノイズとして出力されます。 \\ ==== 検索スコアの取得方法 ==== 検索スコアを SELECT リストや ORDER BY 句の中で使用したい場合があります。この場合でも、WHERE句と同様に ''MATCH AGAINST'' 関数が利用できます。 例1 SELECT *, MATCH(content) AGAINST('明日 東京 天気') as score FROM diaries WHERE MATCH(content) AGAINST('明日 東京 天気') 自然言語検索での例です。結果は以下のようになります。 content score ------------------------------------------ 明日の東京都の天気は晴れでしょう。 1048577 自然言語検索での検索スコアの計算方法についてはMroongaのドキュメント「[[https://mroonga.org/ja/docs/reference/full_text_search/scoring.html|Mroongaにおける検索スコア]]」をご覧ください。 例2 SELECT *, MATCH(content) AGAINST('明日 東京 天気' IN BOOLEAN MODE) as score FROM diaries WHERE MATCH(content) AGAINST('明日 東京 天気' IN BOOLEAN MODE) ブール検索での例です。結果は以下のようになります。 content score ------------------------------------------ 明日の東京都の天気は晴れでしょう。 3 明日の京都の天気は雨でしょう。   2 今日の天気は晴れでしょう。     1 今日の天気は雨でしょう。      1 この例で見る限り、ブール検索での検索スコアはヒットした単語数のように思われますが、実際は単語数に重みを乗じた加重和になります。詳しくはMroongaのドキュメント「[[https://mroonga.org/ja/docs/reference/full_text_search/boolean_mode.html#w-pragma|w プラグマ]]」をご覧ください。 例3 SELECT *, MATCH(content) AGAINST('明日 東京 天気' IN BOOLEAN MODE) as score FROM diaries WHERE MATCH(content) AGAINST('明日 東京 天気' IN BOOLEAN MODE) ORDER BY MATCH(content) AGAINST('明日 東京 天気' IN BOOLEAN MODE) ORDER BY 句の例です。結果は以下のようになります。 content score ------------------------------------------ 今日の天気は雨でしょう。      1 今日の天気は晴れでしょう。     1 明日の京都の天気は雨でしょう。   2 明日の東京都の天気は晴れでしょう。 3 \\ ===== 様々な検索機能 ===== ==== 検索語のハイライト ==== 次のユーザ定義関数を SELECT リスト内で使って、検索語をハイライトするHTMLを出力できます。 mroonga_highlight_html(text, query AS query) -- または mroonga_highlight_html(text, keyword1, ..., keywordN) * ''text'' はカラム名やイミディエイトでも可能です。 * ''query'' には ''MATCH AGAINST'' 関数で使用する検索条件を指定します(''AS query'' は必須) * ''keyword1'' ~ ''keywordN''にはハイライトするキーワードを指定します。 例 SELECT mroonga_highlight_html(content, '明日 天気 東京' AS query) as '検索結果' FROM diaries WHERE MATCH(content) AGAINST('明日 天気 東京' IN BOOLEAN MODE) または、以下でも同様の結果になります: SELECT mroonga_highlight_html(content, '明日','天気','東京') as '検索結果' FROM diaries WHERE MATCH(content) AGAINST('明日 天気 東京' IN BOOLEAN MODE) 検索結果は以下のようになります: 明日東京都の天気は晴れでしょう。 明日の京都の天気は雨でしょう。 今日の天気は晴れでしょう。 今日の天気は雨でしょう。 検索結果内の指定されたキーワードを強調表示します。各キーワードを'''' と '''' で囲み、HTMLの特殊文字はエスケープされるので、結果をそのままHTMLで出力できます。 詳しくは、Mroongaの[[https://mroonga.org/ja/docs/reference/udf/mroonga_highlight_html.html|ドキュメント]]をご覧ください。 \\ ==== スニペット ==== スニペット(snippet)とは「切れ端」という意味で、検索結果として表示されるページへの短い説明文のことを指します。次のユーザ定義関数を SELECT リスト内で使って、検索語周辺のテキストを検索語をハイライトした状態でHTML出力できます。 mroonga_snippet_html(text, keyword1, ..., keywordN) * ''text'' はカラム名やイミディエイトを指定します。 * ''keyword1'' ~ ''keywordN''にはハイライトするキーワードを指定します。 例 SELECT mroonga_snippet_html(content, '明日','天気','東京') AS '検索結果' FROM diaries WHERE MATCH(content) AGAINST('明日 天気 東京' IN BOOLEAN MODE) 検索結果は以下のようになります:
明日東京都の天気は晴れでしょう。
明日の京都の天気は雨でしょう。
今日の天気は晴れでしょう。
今日の天気は雨でしょう。
検索結果内の指定されたキーワードを強調表示して、キーワード周辺のテキストを表示します。全体を ''
'' と ''
'' で、各キーワードを '''' と '''' で囲み、HTMLの特殊文字はエスケープされるので、結果をそのままHTMLで出力できます。 より詳細な設定をしたい場合は、次の ''mroonga_snippet'' 関数を使用します。 例 SELECT mroonga_snippet( content -- カラム名やイミディエイト , 100 -- スニペットの最大長(バイト単位) , 3 -- スニペットの最大要素数 , 'utf8_unicode_ci' -- エンコーティング(照合順序) , 1 -- 先頭の空白を無視する(1)か否(0)か , 1 -- HTMLエスケープを行うか(1)か否(0)か , '...' -- スニペットの開始テキスト , '...' -- スニペットの終了テキスト , '明日', '', '' -- キワード,キワードの開始テキスト,ワードの終了テキスト , '天気', '', '' -- 同上 , '東京', '', '' -- 同上 ) as snippet FROM diaries WHERE MATCH(content) AGAINST('明日 天気 東京' IN BOOLEAN MODE) 検索結果は以下のようになります: ...明日東京都の天気は晴れでしょう。... ...明日の京都の天気は雨でしょう。... ...今日の天気は晴れでしょう。... ...今日の天気は雨でしょう。... 詳しくは、以下のMroongaの[[https://mroonga.org/ja/docs/reference/udf/mroonga_snippet.html|ドキュメント]]をご覧ください。 \\ ==== クエリ展開 ==== Mroongaのクエリ展開機能は、同義語や関連語を展開して検索する場合に利用できます。 まずは、以下のような関連語テーブルを準備します。以下の例では、検索対象周辺の都道府県を持つ関連語テーブルを例として使用しますが、同義語テーブルなどが一般的かもしれません。 CREATE TABLE area ( name varchar(255), pref varchar(255), INDEX (name) ) ENGINE = Mroonga COLLATE utf8_unicode_ci; INSERT INTO area VALUES ('東京', '東京 神奈川 埼玉'); INSERT INTO area VALUES ('神奈川', '神奈川 東京 静岡'); INSERT INTO area VALUES ('大阪', '大阪 京都 兵庫'); INSERT INTO area VALUES ('京都', '京都 大阪 滋賀'); このようなテーブルを準備することによって、「東京」、「神奈川」、「埼玉」の何れが検索語として指定されても「東京」がヒットするようになります。尚、この例のように、''pref'' には自分自身の都道府県の名前も含める必要があります。 === mroonga_query_expand関数 === 次のユーザ定義関数を ''MATCH AGAINST'' 関数内で使って、クエリ展開機能(この例では関連語展開検索)が利用できます。 mroonga_query_expand(table, term, synonyms, query); * ''table'' には 同義語や関連語を定義するテーブル名を指定します。 * ''term'' には 検索対象語句を保持する ''table'' 内のカラム名を指定します。 * ''synonyms'' には 同義語や関連語を保持する ''table'' 内のカラム名を指定します。 * ''query'' には 通常の ''MATCH AGAINST'' 関数で使用する検索条件を指定します。 例 SELECT * FROM diaries WHERE MATCH(content) AGAINST(mroonga_query_expand("area", "name", "pref", "+明日 +天気 +東京") IN BOOLEAN MODE) -- または SELECT * FROM diaries WHERE MATCH(content) AGAINST(mroonga_query_expand("area", "name", "pref", "+明日 +天気 +神奈川") IN BOOLEAN MODE) 上の2つは以下の同じ検索結果を表示します。 明日の東京都の天気は晴れでしょう。 詳しくは、Mroongaの[[https://mroonga.org/ja/blog/2017/10/12/mroonga-7.07.html|ドキュメント]]を参照して下さい。 \\