Kouhei Sutou
kou****@clear*****
2013年 7月 9日 (火) 16:57:20 JST
須藤です。 In <46551****@web10*****> "[groonga-dev,01522] Re: 特殊記号混じりの前方一致検索について" on Sat, 6 Jul 2013 00:37:00 +0900 (JST), <mail_babir****@yahoo*****> wrote: >> 「\」で特殊文字をエスケープできるようにしようと思います。 >> 例えば、「\(仮\)*」のように書けるようにするということです。 > > アプリケーション側でのエスケープで対応となるのですね。 はい。 > 個人的には、結局のところ「(」等の特殊文字が入力されたことをアプリケーション側で逐一検知して、その先頭に「\」を付けるという動作になるため、現状のマルチバイトに変換して回避している方法と大差ないのではという印象です。 たしかにそうですね。 >> * エラーを握りつぶしてしまうと、どうしてヒットしないんだろ >> う?あるいはどうしてヒットするんだろう?ということが起き >> てしまいそう > > おそらくmroongaを初めて使う場合、myisamで通っていた特殊文字付きのクエリが通らない点に対して、不都合を感じることが多いかと思います。 > > 自動エスケープの有無というよりは、myisamで通っていたクエリは、最低限mroongaでも通るようになっていて、同一の検索結果を取得できて欲しいというところが本音です。 エラーケースの扱いもMyISAMに合わせて欲しいということですよね。 どこまで合わせるかは難しいところだと思っています。というのは、 MyISAMとInnoDBでも挙動が違うからです。(後述) MyISAMからの乗り換えの人のことだけを考えればよいのであれば MyISAMに合わせるというのは現実的なのかもしれませんが、そのよ うな人たちがmroongaユーザーのうちどのくらいいるのかを知らな いままそっちに振ってしまうことに不安を感じています。 (mroongaユーザーの傾向を知るにはどうしたらよいのかしら。。。) たとえば、'(仮)*'は ( ← グループ開始 仮 ← グループの内容 ) ← グループ終了 * ← 前の語を前方一致(ただし、前の語がないのでエラー) というように解釈されます。 mroongaではこのときエラーと報告しますが、MyISAMでは単にマッ チしないという挙動です。 ---- mysql> CREATE TABLE `test` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `main` varchar(255) NOT NULL COLLATE 'utf8_unicode_ci', -> PRIMARY KEY (`id`), -> FULLTEXT INDEX (main) -> ) ENGINE=myisam DEFAULT CHARSET=utf8; Query OK, 0 rows affected (0.04 sec) mysql> INSERT INTO `test` (`main`) VALUES ('(仮)テスト テスト1'); Query OK, 1 row affected (0.02 sec) mysql> INSERT INTO `test` (`main`) VALUES ('仮テスト テスト1'); Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('(仮)*' IN BOOLEAN MODE); Empty set (0.00 sec) ---- また、'(仮*'は ( ← グループ開始 仮 ← グループの内容 * ← 前の語を前方一致 (グループ終了マークがないのでエラー) と解釈されます。 mroongaはやはりこれもエラーとして報告しますが、MyISAMでは最初 の'('を無視して、グループ化自体なかったものとして処理している ようにみえます。 ---- mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('(仮*' IN BOOLEAN MODE); +----+---------------------------+ | id | main | +----+---------------------------+ | 2 | 仮テスト テスト1 | +----+---------------------------+ 1 row in set (0.00 sec) ---- MySQL 5.6.12のInnoDBではどちらもエラーになりませんが、どちら もヒットしませんでした。 ---- mysql> CREATE TABLE `test` ( -> `id` int(11) NOT NULL AUTO_INCREMENT, -> `main` varchar(255) NOT NULL COLLATE 'utf8_unicode_ci', -> PRIMARY KEY (`id`), -> FULLTEXT INDEX (main) -> ) ENGINE=innodb DEFAULT CHARSET=utf8; Query OK, 0 rows affected (1.18 sec) mysql> INSERT INTO `test` (`main`) VALUES ('(仮)テスト テスト1'); Query OK, 1 row affected (0.03 sec) mysql> INSERT INTO `test` (`main`) VALUES ('仮テスト テスト1'); Query OK, 1 row affected (0.03 sec) mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('(仮)*' IN BOOLEAN MODE); Empty set (0.00 sec) mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('(仮*' IN BOOLEAN MODE); Empty set (0.01 sec) ---- InnoDBでは'仮*'のようにシンタックスエラーでないものはヒット しました。 ---- mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('仮*' IN BOOLEAN MODE); +----+---------------------------+ | id | main | +----+---------------------------+ | 2 | 仮テスト テスト1 | +----+---------------------------+ 1 row in set (0.00 sec) ---- このように、MyISAMとInnoDBでも挙動が違いました。 なお、MyISAMの挙動では「(仮」から始まる語を検索することはで きません。mroongaのようにマルチバイトバージョンの文字に変換 して検索するというような回避策もありません。 InnoDBも試してみて気づいたのですが、InnoDBはクライアントにシ ンタックスエラーを返すのではなく、ログにエラーを出力していま した。エラーを検出する方向で実装するのあれば、クライアントに エラーを返すよりもこの動作の方がMySQLらしいのかもしれません。 (MySQLで、他の類似のケースでも同じようになっているのならロ グに出力するだけにする、という挙動にした方がよいかなぁと思え てきました。) > オプションとして実装頂ければ、使い手側で利用判断ができるため、万一希望しない動作となった場合にも以前の設定を適用できるようにすると、問題が少ないかと考えました。 たしかに、それはあると思います。 > * MATCH AGAINSTにオプションを渡すにはセッション変数などで > 渡さないといけないので、使い勝手がよいのか判断がつかなかっ > た > > > プラグマのような記述で指定できると良さそうですが、mroonga_match_escalation_thresholdのようなシステム変数で最初から設定できる形でも十分だと思います。 プラグマはTritonn/mroonga固有の機能なので、MyISAMとの親和性 という意味では逆に障害になってしまうのではないかと心配してし まいます。変数を使うのもプラグマほどではないですが、同じ点を 心配してしまいます。 > ただ、制限を加える場合にはそれ相応の対応が必要というのは当然なのですが、制限を加えないデフォルトの動作において、なおエスケープ処理が別途必要という状況は使い勝手に問題があるように感じてしまいます。 > > 現状ではmyisamで動いていてmroongaで動かない部分はこのエスケープ周りが大きいと感じているので、この点の差異がなくなれば、myisamからの乗り換えに障害がなくなるのではと考えています。 > > 技術的に難しい面があるとは思いますが、アプリケーション側での操作が極力減るような形で対応策をご検討頂けると幸いです。 うーん、デフォルトで「(」、「)」、「*」、「+」などを使った高 度な使い方ができることが使い勝手の問題につながっているのでは ないかと感じました。前方一致検索やグループ化などといった高度 な使い方をしない人は「(」、「)」、「*」、「+」はそのまま検索 して欲しく、エスケープが邪魔に感じるのかと思いました。 MyISAMの挙動とはずれていきますが、「(」、「)」、「*」、「+」 など特殊な文字を特殊な文字と解釈せずにそのものとして検索する モードを用意するのはどうでしょうか? 例えば、「(仮)* テスト」と指定したら「(仮)*」という単語または 「テスト」という単語が含まれているレコードを検索するといった 具合です。 と書いてみましたが、そんなにうれしくなさそうな気がします ね。。。 あまりうまく伝えられた気がしませんが、シンタックスエラーの挙 動をMyISAM時に合わせることにはまだ抵抗を感じています。。。 本当に必要なのか、というところがピンときていないからかもしれ ません。 -- 須藤 功平 <kou****@clear*****> 株式会社クリアコード <http://www.clear-code.com/> (03-6231-7270) groongaサポート: http://groonga.org/ja/support/ パッチ採用はじめました: http://www.clear-code.com/recruitment/ コミットへのコメントサービスはじめました: http://www.clear-code.com/services/commit-comment.html