[groonga-dev,00700] Re: TokenBigramSplitSymbolAlphaDigitを利用した場合の検索結果について

アーカイブの一覧に戻る

Kouhei Sutou kou****@clear*****
2012年 2月 21日 (火) 16:59:03 JST


須藤です。

In <040836c4-0850-395d-a4f9-018570f00a33 @ api104>
  "[groonga-dev,00699] TokenBigramSplitSymbolAlphaDigitを利用した場合の検索結果について" on Tue, 21 Feb 2012 15:24:30 +0900,
  "Hirano" <hirano_verf****@mail*****> wrote:

> TokenBigramSplitSymbolAlphaDigitを利用した全文検索で、データによって検索が意図した結果になりません。

これは、実は、以下のようないろいろな要素が組み合わされていて
少し難しい話なのです!

  * 文字の正規化
  * 空白文字の扱い
  * 検索方法のエスカレーション
    http://groonga.org/ja/docs/spec/search.html

> 検索語「to S」で検索を行うとデータによって
> ヒットの有無が下記のようになります。
> 
> DBデータパターン1
> to S(letter ver.)→ ヒット
> to S《ラーメン》→ ヒット
> 
> DBデータパターン2
> to S(letter ver.)→ ヒットしない
> to S《ラーメン》→ ヒットしない
> to S → ヒット
> 
> 
> DBデータパターン2では3件ヒットすると思うのですが、実際は1件ヒットと
> なってしまいます。
> このような動作になる理由を教えていただけないでしょうか。

理由の説明の前にまずは動作を確認します。

まず、:key_normalize => trueをfalseにして文字の正規化を行わ
ないようにします。すると、以下のようにすべてのレコードがヒッ
トします。

  --- search No.1 ---
  to S(letter ver.)
  to S《ラーメン》
  --- search No.2 ---
  to S(letter ver.)
  to S《ラーメン》
  to S

次は、:key_normalize => trueのままでデータを少し変えてみます。
最初の2つのレコードの「to S」の後に空白を入れて以下のようにし
ます。

  to S(letter ver.)
  to S《ラーメン》
↓
  to S (letter ver.)
  to S 《ラーメン》

すると、以下のようにすべてのレコードがヒットします。

  --- search No.1 ---
  to S (letter ver.)
  to S 《ラーメン》
  --- search No.2 ---
  to S (letter ver.)
  to S 《ラーメン》
  to S

最後に、:key_normalize => trueもデータも変えずにトークナイザー
をTokenBigramIgnoreBlankSplitSymbolAlphaDigitに変えてみます。

すると、以下のようにすべてのレコードがヒットします。

  --- search No.1 ---
  to S(letter ver.)
  to S《ラーメン》
  --- search No.2 ---
  to S(letter ver.)
  to S《ラーメン》
  to S


それでは、元のスクリプトの挙動と、それぞれの方法でどうしてす
べてのレコードがヒットするようになるのかを説明します。

○ 元の挙動

文字を正規化すると

  to S(letter ver.)

は以下のようにトークナイズされます。
(正規化すると小文字や半角の文字に統一されます。)

  ["(l", ")", ".)", "er", "et", "le", "o", "r", "r.", "s(", "te", "to", "tt", "ve"]

「to S」の部分は「to」「o」「s(」にトークナイズされています。

一方、検索語である「to S」をトークナイズすると以下のようにトー
クナイズされます。

  ["to", "o", "s"]

このように、「to S(letter ver.)」は「S」と「(」がくっつい
たままトークナイズされるのですが、「to S」は「S」だけのトー
クンにトークナイズされます。そのため、「to S」は
「to S(letter ver.)」にマッチしません。

しかし、検索結果ではマッチしています。これは、「検索方法のエ
スカレーション」が行われているためです。
  http://groonga.org/ja/docs/spec/search.html#id2

↑にあるようにgroongaは1つも完全一致しないレコードがあると、
少しゆるめに検索します。そのときに「to S(letter ver.)」も
ヒットするようになります。(今回はN-gram系のトークナイザーを
利用しているので前方一致検索でマッチしています。)

これは「to S(letter ver.)」だけではなく、「to S《ラーメン》」
のときも同様の挙動になります。そのため、レコードが
「to S(letter ver.)」と「to S《ラーメン》」だけのときは完
全一致検索でヒットするレコードがないため、両方ヒットします。

しかし、「to S」も加わると「to S」が完全一致検索でヒットして
しまうため、ゆるめの検索を実行しなくなります。そのため、
「to S(letter ver.)」と「to S《ラーメン》」はヒットしなく
なります。

これが、元の挙動です。

○ :key_normalize => trueをfalseにする

文字の正規化をやめると

  to S(letter ver.)

は以下のようにトークナイズされます。

  [" S", " v", ".)", "S(", "er", "et", "le", "o ", "r ", "r.", "te", "to", "tt", "ve", "(l", ")"]

トークンに空白も含まれています。一方、

  to S

は

  [" S", "S", "o ", "to"]

となります。このトークンは「to S(letter ver.)」のトークン
にも含まれているため完全一致でヒットします。そのため、「to S」
というレコードが加わっても「to S(letter ver.)」も
「to S《ラーメン》」も依然としてヒットします。

ただし、正規化しなくなっているため検索ワードが「to s」(Sが
小文字になっている)ではヒットしないことに注意してください。

○ 「to S」の後に空白をいれる

「to S」の後に空白を入れて

  to S (letter ver.)

とすると、以下のようにトークナイズされます。

  ["(l", ")", ".)", "er", "et", "le", "o", "r", "r.", "s", "te", "to", "tt", "ve"]

「to S」の部分は「to」「o」「s」にトークナイズされています。
「S」と「(」が離れたため、「to S」にマッチするようになりま
す。そのため、完全一致するようになり、「to S」というレコード
が加わっても「to S(letter ver.)」も「to S《ラーメン》」も
依然としてヒットします。

○ TokenBigramIgnoreBlankSplitSymbolAlphaDigitトークナイザーを使う

TokenBigramIgnoreBlankSplitSymbolAlphaDigitトークナイザーを
使うと

  to S(letter ver.)

は以下のようにトークナイズされます。

  ["(l", ")", ".)", "er", "et", "le", "os", "r.", "rv", "s(", "te", "to", "tt", "ve"]

「to S」の部分は「to」「os」「s(」にトークナイズされています。

「to S」をトークナイズすると以下のようになります。

  ["os", "s", "to"]

ここでは空白を無視するトークなイザーを使っているため、「to」
と「os」がこの順番で現れていれば「to S」が出現していると判断
することができます。(最後の"s"だけのトークンは気にしなくて
もよい。)

よって、この場合もすべてのレコードが完全一致でヒットします。

○ まとめ

今回の例のようなすべてのレコードにヒットさせたい場合は
TokenBigramIgnoreBlankSplitSymbolAlphaDigitトークナイザーを
使うのがニーズにマッチする気がします。ただ、ヒット数が多くな
りがちなのでパフォーマンスに影響がでるかもしれません。そのた
め、本当にすべてのレコードにヒットさせる必要があるのか(ヒッ
トするようになるレコードは本当にヒットして欲しいレコードなの
か)を検討してみた方がよいかもしれません。
、検索した人が必要のない情報をたくさん出してしまうと使いやす
さにも影響してしまいますので。。。


○ おまけ

どのようにトークナイズされたかはトークナイザーを指定したテー
ブルのキーを見るとわかります。たとえば、以下のようにすると
「to S」がどのようにトークナイズされたかを確認できます。

  Groonga[@table_name].add("C", :word => "to S")
  p Groonga["ift_bigram"].collect(&:key)




こちらで挙動を確認できるスクリプトがあってとても助かりました。
ありがとうございます。

-- 
須藤 功平 <kou****@clear*****>
株式会社クリアコード <http://www.clear-code.com/> (03-6231-7270)

プログラミングが好きなソフトウェア開発者を募集中:
  http://www.clear-code.com/recruitment/




groonga-dev メーリングリストの案内
アーカイブの一覧に戻る