幻水のセリフの全文検索がしたい
「幻想水滸伝」シリーズのゲーム内のセリフを全文検索できるシステムがあれば色々と幸せになれる気がします。きっかけはlynx(えcぅしふぇr)さんのこのWebページでした。
このデータを用いて、様々な要素で検索ができ、結果を表示できたらとてもハッピーになれると思ったのです。なので、そのようなシステムを作ることに着手してみました。
大まかなシステム構成
Webアプリでの実装にすることは決定です。言語はPHPでRDBMSはMySQLを選択しました。この場合、全文検索システムを作るのにはNgramを用いるか、それ以外(例えばMeCab)を用いるかの方法がありますが、今回はNgramを採用しました。
なお、どこのMySQLのNgram解説ページにも書いてありますが、FULLTEXTを用いるためにエンジンはMyISAMにすることを忘れないようにします。
Ngramを用いたPHP+MySQLでの全文検索システムの作り方は幸いにも情報が蓄積されており、以下のページが大変に参考になりました。掲載されているスクリプトも利用させて頂いてます。
http://www.tatamilab.jp/rnd/archives/000390.html
実際の作業としては、Excelにセリフデータを入力します。カラムの構成はまだ設計中ではあるのですが、「セリフID(主キー)」、「シリーズタイトル名」、「前後のセリフID」、「セリフ」、「セリフ(漢字変換)」、「セリフの分かち書き」、「セリフ(漢字変換)の分かち書き」、「キャラ名」、「場所(地名)」、「同席キャラ名」、「シーンID」などを設定します。将来的に多彩な検索方法に対応するためです。
今のところはとりあえずセリフの全文検索のみに絞ります。
スクリプトを用いてセリフ原文をNgramテキスト化
Excelの入力データをCSVでphpMyAdminにインポートしたあと、セリフ原文が収められているカラムをNgramテキスト化します。前掲のページのスクリプト、ngram_converter.phpを使用します。以下のようなコードになりました。
<?php require_once('./ngram_converter.php'); $p = new NgramConverter(); $dsn = "mysql:host=localhost;dbname=suikoden;charset=utf8"; $db = new PDO($dsn, 'USER', 'PASSWORD'); $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $sql = 'SELECT * FROM word_search_test'; $prepare = $db->prepare($sql); $prepare->execute(); $result = $prepare->fetchAll(PDO::FETCH_NUM); for ($i = 0; $i < 34; $i++){ $word_by_ngram = $p->to_fulltext($result[$i][4], 2); $sql = 'UPDATE word_search_test SET word_ngram = "' . $word_by_ngram . '" WHERE word_id = ' . "$i"; $prepare = $db->prepare($sql); $prepare->execute(); } ?>
このスクリプトを実行することで、すでに格納されているセリフ原文から瞬時にNgramテキストが生成され、DBに格納されます。
WebアプリのためにWebページを作る
WebアプリなのでUIは当然Webページなわけで、Webページをゴリゴリ書きます(実はこれが一番面倒とも…)。
フォームから入力された語をキーワードにして検索するSQLを生成するのも、先ほどのスクリプトを使います。ただし、このスクリプトはmysql_real_escape_stringを使っている部分がありエラーが出ます。この変換が不要ならば変換を介さないSQLを出力するようにソースを改変すれば、問題ありません。今回は改変したソースでSQLを出力しています。
検索結果のページのコードの主要部分はこんな感じになりました。
<?php require_once('./ngram_converter.php'); $p = new NgramConverter(); $word = $_POST['q']; $sql_tmp = $p->make_match_sql($word, "word_ngram", 2); $dsn = "mysql:host=localhost;dbname=suikoden;charset=utf8"; $db = new PDO($dsn, 'USER', 'PASSWORD'); $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $sql = 'SELECT COUNT(*) FROM word_search_test'; $prepare = $db->prepare($sql); $prepare->execute(); $result = $prepare->fetchAll(PDO::FETCH_NUM); $count = $result; $sql = 'SELECT * FROM word_search_test'; $sql = $sql . " where " . $sql_tmp; $prepare = $db->prepare($sql); $prepare->execute(); $result = $prepare->fetchAll(PDO::FETCH_NUM); for ($i = 0; $i < $count; $i++){ print "<tr>"; print "<td>"; print $result[$i][3]; print "</td>" . "\n"; print "<td>"; print $result[$i][4]; print "</td>" . "\n"; print "<td>"; print $result[$i][8]; print "</td>" . "\n"; print "<td>"; print $result[$i][9]; print "</td>" . "\n"; print "</tr>"; } ?>
これで、最低限(本当に最低限)の結果の表示ができました。デザイン?知らん。
「さわり」はできた
あとはセリフ本文以外の部分(漢字セリフ含む)で検索できるようにすることです。いや、検索自体はもちろん今のままでもできますが、結果をどのように表示するか、利用する人はどういう表示を見たいのかを考えることが必要です。
また、おそらく実際に一番時間を取られる、データの入力作業が待っています。この点は省力化を図ることを可能にしてくれたlynxさんに感謝します。ただ、自分でも実際にもう一度ゲームを再プレイしなきゃとは思っています。
ああ、あとセリフの分岐の処理がどうするかというのもありますが、これはおそらくどうしようもなさそうなのでどうしようもなさそうです。いろいろと考え中です。
完成(と呼べるようなもの)がいつになるかは分かりませんが、ぼちぼち作っていきたいと思います。