Kouhei Sutou
kou****@clear*****
2013年 8月 1日 (木) 16:38:07 JST
須藤です。 遅くなってすみません。。。 In <63331****@web10*****> "[groonga-dev,01550] Re: 特殊記号混じりの前方一致検索について" on Sat, 27 Jul 2013 12:33:51 +0900 (JST), <mail_babir****@yahoo*****> wrote: >> mroonga_escape()みたいな関数を提供して、 >> >> SELECT ... WHERE >> MATCH (...) AGAINST (mroonga_escape("(仮)") IN BOOLEAN MODE); >> >> というようにするのはどうでしょうか?これで「(仮)」そのものを >> 検索するイメージです。(「(...)」をグループ化する書き方とし >> て認識しない。) ... > これは良さそうですね! > > 単純な前方一致検索の用途で、()が含まれているせいでパースエラーが出ていることが結構あるので、便利だと思います。 よかったです! > 場合によっては複数回エスケープ関数を使うことがありそうなので、関数名はe()くらい短くできるといいですね。 おぉ。。。それはやり過ぎな気持ちになりました。。。 MySQL全体でのエスケープ関数ならe()でもよさそうな気がしますが、 mroonga固有のエスケープ関数なのでe()はやり過ぎな気がする、と 感じました。 > ただ、また話を戻してしまって恐縮なのですが、 > 「+(仮)* -(笑)」のようなクエリで「(仮)が前方一致しつつ、(笑)は含まない」という検索条件をユーザが直接指定できるようにさせる場合、アプリケーション側では「半角スペースでワード分割して、前方一文字に+-を含む場合はそれを外し、後方一文字に*がある場合はそれも外したうえで、mroonga_escape()で括って、先ほど外した文字を付け直す」ということになるかと思います。 > > これはさすがに手順が煩雑なので、もう少し上手い方法を正規表現で書くなり、「¥¥」でのエスケープをすることになるとは思いますが。 > > ただ、「使いたい特殊文字が所定の位置にあることをアプリケーション側で特定して、それ以外の部分にある特殊文字だけをエスケープする」という処理は必要になってしまうかと思います。 はい、そうなると思います。 > これはアプリケーション側の責任、と言われてしまうと仕方がないのですが、myisamの場合、以前検証頂いた結果のように上記のクエリを直接mysqlに流しても正確に検索ができるんですね。 む、ちょっと待ってください。 データとして以下の2件が入った状態で、 * (仮)テスト テスト1 * 仮テスト テスト1 以下の検索結果になるのが前に確認した結果ですよね。 * '(仮*' -> 2件ヒット * '(仮)*' -> 1件ヒット(「(仮)テスト テスト1」がヒット) 私、「正確に検索」できている気がしていないので、もう少し調べ てみました。以下のように3つのデータが入っている場合を考えま す。↑にidが3のデータを追加しただけです。 mysql> SELECT * FROM `test`; +----+-----------------------------+ | id | main | +----+-----------------------------+ | 1 | (仮)テスト テスト1 | | 2 | 仮テスト テスト1 | | 3 | テスト1 仮 | +----+-----------------------------+ 3 rows in set (0.00 sec) この状態で「(仮)*」で検索します。正確に検索できていればid=1だ けがヒットするはずですよね。しかし、以下のようにid=3もヒット します。 mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('(仮)*' IN BOOLEAN MODE); +----+-----------------------------+ | id | main | +----+-----------------------------+ | 1 | (仮)テスト テスト1 | | 3 | テスト1 仮 | +----+-----------------------------+ 2 rows in set (0.00 sec) このことから、MyISAMは「()」を無視していると考えられます。試 しに「仮」だけで検索しても同じ結果になります。 mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('仮' IN BOOLEAN MODE);+----+-----------------------------+ | id | main | +----+-----------------------------+ | 1 | (仮)テスト テスト1 | | 3 | テスト1 仮 | +----+-----------------------------+ 2 rows in set (0.00 sec) この結果から想像するに、「*」も無視しています。 さらに想像すると、「(仮)テスト テスト1」は 「(仮)テスト」「テスト1」 ではなく、 「仮」「テスト」「テスト1」 とトークナイズされているような気がします。試しに「テスト」で 検索するとid=1がヒットします。 mysql> SELECT * FROM `test` WHERE MATCH(`main`) AGAINST('テスト' IN BOOLEAN MODE); +----+-----------------------------+ | id | main | +----+-----------------------------+ | 1 | (仮)テスト テスト1 | +----+-----------------------------+ 1 row in set (0.00 sec) このことから、MyISAMで「(仮)*」が正確に検索できているように みえるのは実はそうではなく、単に「仮」で検索しているだけで、 「(仮)*」だと誤ヒットがありうるのではないかと考えられます。 > そして、mysql自体のマニュアルを確認しても()+-*~><などの特殊文字をエスケープするように指示している記述が見当たらないです。 はい、その通りです。 > この点から考えると、mysqlとしてはin boolean modeで有効になる特殊文字については、特段エスケープ処理をしなくても動作する、という想定になっているような気がするのですが、どうでしょうか。 前述の通り、そうではなく、特殊な文字を無視して検索しているよ うに見えます。 > innodbでの挙動が、ft_min_word_len=1の場合でも前回テスト頂いた結果と同じならば、ストレージエンジンによって挙動が違うということで間違いないですが、仮にmyisamと同等の結果が得られるようになっている場合は、mroongaもその挙動に合わせられる方が望ましいと思います。 InnoDBの挙動を確認したかは忘れてしまったのですが、InnoDBはロ グにエラーを出していたのでシンタックスエラーの場合はヒットし ないと思います。なので、ストレージエンジンによって挙動が違う というのが現状じゃないかという気がしています。。。 > あと、こじつけ的な気もしますが、mysqlで全文検索といえばmyisamという認識が一般的と思うので、その挙動に合わせる点では問題はないと思っています。 > > ・innodbが全文検索に対応したのがmysql5.6以降なので、そもそもmyisamの利用者が圧倒的多数と思われる > ・もともと全文検索自体はmyisam固有の機能だったので、innodbよりmyisamに合わせる方が後方互換性があると言えそう > ・myisamに合わせている限り、少なくとも想定外の検索結果になるということは考えにくい > > なので、メリットはあってもデメリットは少ないと思うのです。 > (sennaからの移行やgroongaの実装との兼ね合いまで考えると話は変わってきそうですが) なるほど。ただ、MyISAMで全文検索をしてきた人がどのくらいいる のかわからないので、なんとも言えないなぁという気持ちがまだあ ります。だれか、そのあたりの事情を教えてくれるとうれしいので すが。。。 > 自動エスケープというよりは、特殊文字の解析方法をmyisamに合わせることができれば、より自然でいいかと思います。 > > myisamの実装を見て、似た実装ができそうなら引き続き検討頂けると有り難いです。 とすると、エラーの扱いまで含めたMyISAM完全互換のクエリー構文 パーサーを作ることになりますねぇ。構文エラーのときにエラー扱 いにできないと、パーサーを作るためのツールと相性が悪いので作 りづらいんですよね。。。うーん。。。 > mroonga_escape()については、あれば確実に便利な関数だと思うので、myisamの挙動の件とは別に実装して頂けると嬉しいです。 > (ユーザに特殊文字を使わせたくない場合に、予めその関数でラッピングしておくという使い方もできるので) はい、ちょっと検討してみます。 > しつこく蒸し返して恐縮ですが、特殊文字の解析挙動が合えば、mroongaの導入にあたっての障壁がより低くなることは間違いないと思う(どうやってエスケープしようという点で悩まなくて済むし、後になってエスケープが必要なことを知って慌てることもない)ので、実現可能であれば、ぜひお願いしたいと思います。 は、はい。。。 うーん、私は、ユーザーとしては「特殊文字が勝手に無視されてエ ラーにならずに動いているけど期待した挙動じゃないこともある (今のMyISAMの挙動)」よりは、「エラーになってすぐに問題がわ かる(InnoDBのログをみる場合や今のmroongaの挙動)」というタ イプなので、なかなか踏ん切りがつかないんですよねぇ。。。 -- 須藤 功平 <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