同じ検索キーワードなのに全文検索結果が異なる#同じ検索キーワードでも一緒に指定するクエリによっては全文検索の結果が異なることがあります。ここでは、その原因と対策方法を説明します。例#まず、実際に検索結果が異なる例を説明します。DDLは以下の通りです。BlogsテーブルのbodyカラムをTokenMecabトークナイザーを使ってトークナイズしてからインデックスを作成しています。bodyテスト用のデータは1件だけ投入します。["東京都民に深刻なダメージを与えました。10:18:34"],]まず、全文検索のみで検索します。この場合ヒットします。"東京都"'[[0,4102.268052438,0.000743783],[[[1],[["_id","UInt32"],["updated_at","Time"],["body","ShortText"]],[1,1285031914.0,"東京都民に深刻なダメージを与えました。0:0:0の秒表記)。この場合もヒットします。1285858800'[[0,4387.524084839,0.001525487],[[[1],[["_id","UInt32"],["updated_at","Time"],["body","ShortText"]],[1,1285031914.0,"東京都民に深刻なダメージを与えました。"]]]]最後に、範囲指定と全文検索の順番を入れ替えて検索します。個々の条件は同じですが、この場合はヒットしません。"東京都"'[[0,4400.292570838,0.000647716],[[[0],[["_id","UInt32"],["updated_at","Time"],["body","ShortText"]]]]]どうしてこのような挙動になるかを説明します。原因#このような挙動になるのは全文検索時に複数の検索の挙動を使い分けているからです。Groongaは基本的に完全一致検索のみを行います。上記の例では「東京都民に深刻なダメージを与えました。」を「東京都」というクエリで検索していますが、TokenMecabトークナイザーを使っている場合はこのクエリはマッチしません。検索対象の「東京都民に深刻なダメージを与えました。都とトークナイズされるため、完全一致しません。Groongaは完全一致検索した結果のヒット件数が所定の閾値を超えない場合に限り、非わかち書き検索を行い、それでもヒット件数が閾値を超えない場合は部分一致検索を行います(閾値は1がデフォルト値となっています)。このケースのデータは部分一致検索ではヒットするので、「東京都」クエリのみを指定するとヒットします。1285858800」で1件ヒットし、閾値を越える)は、たとえ完全一致検索で1件もヒットしない場合でも部分一致検索などを行いません。"東京都"'そのため、条件の順序を変えると検索結果が変わるという状況が発生します。以下で、この情報を回避する方法を2種類紹介しますが、それぞれトレードオフとなる条件があるので採用するかどうかを十分検討してください。トークナイザーを変更する#TokenMecabトークナイザーは事前に準備した辞書を用いてトークナイズするため、再現率よりも適合率を重視したトークナイザーと言えます。TokenBigramなど、N-gram系のトークナイザーは適合率を重視したトークナイザーと言えます。TokenMecabの場合「東京都」で「京都」に完全一致することはありませんが、TokenBigramでは完全一致します。TokenMecabでは「東京都民」に完全一致しませんが、TokenBigramでは完全一致します。このようにN-gram系のトークナイザーを指定することにより再現率をあげることができますが、適合率が下がり検索ノイズが含まれる可能性が高くなります。match_columnsで使用する索引毎に重み付けを指定します。ここでも、前述の例を使って具体例を示します。TokenBigramを用いた索引を追加します。bodyこの状態でも以前はマッチしなかったレコードがヒットするようになります。"東京都"'[[0,7163.448064902,0.000418127],[[[1],[["_id","UInt32"],["updated_at","Time"],["body","ShortText"]],[1,1285031914.0,"東京都民に深刻なダメージを与えました。"]]]]しかし、N-gram系のトークナイザーの方がTokenMecabトークナイザーよりも語のヒット数が多いため、N-gram系のヒットスコアの方が重く扱われてしまいます。N-gram系のトークナイザーの方がTokenMecabトークナイザーよりも適合率の低い場合が多いので、このままでは検索ノイズが上位に表示される可能性が高くなります。そこで、TokenMecabトークナイザーを使って作った索引の方をTokenBigramトークナイザーを使って作った索引よりも重視するように重み付けを指定します。これは、match_columnsオプションで指定できます。body'[[0,8167.364602632,0.000647003],[[[1],[["_score","Int32"],["body","ShortText"]],[13,"東京都民に深刻なダメージを与えました。"]]]]この場合はスコアが11になっています。内訳は、Terms.blog_body索引(TokenMecabトークナイザーを使用)でマッチしたので10、Bigram.blog_body索引(TokenBigramトークナイザーを使用)でマッチしたので3、これらを合計して13になっています。このようにTokenMecabトークナイザーの重みを高くすることにより、検索ノイズが上位にくることを抑えつつ再現率を上げることができます。この例は日本語だったのでTokenBigramトークナイザーでよかったのですが、アルファベットの場合はTokenBigramSplitSymbolAlphaトークナイザーなども利用する必要があります。billiardとなり、「bill」では完全一致しません。dとなり、「bill」でも完全一致します。TokenBigramSplitSymbolAlphaトークナイザーを使う場合も重み付けを考慮する必要があることはかわりありません。利用できるバイグラム系のトークナイザーの一覧は以下の通りです。バイグラムでトークナイズする。連続する記号・アルファベット・数字は一語として扱う。記号もバイグラムでトークナイズする。連続するアルファベット・数字は一語として扱う。記号とアルファベットもバイグラムでトークナイズする。連続する数字は一語として扱う。記号・アルファベット・数字もバイグラムでトークナイズする。バイグラムでトークナイズする。連続する記号・アルファベット・数字は一語として扱う。記号もバイグラムでトークナイズする。連続するアルファベット・数字は一語として扱う。記号とアルファベットもバイグラムでトークナイズする。連続する数字は一語として扱う。記号・アルファベット・数字もバイグラムでトークナイズする。オプションで変更することができます。100この場合も対策方法1同様、検索ノイズが上位に現れる可能性が高くなることに注意してください。検索ノイズが多くなった場合は指定する値を低くする必要があります。