INFORMATION
サブスクリプション

日本語「もしかして」検索について

ロンウイットのSolrサブスクリプション・パッケージはバージョン0.9から、お客様からご要望の多かった日本語の「もしかして検索」に対応しました。本記事ではその新機能を詳しく紹介します。

「もしかして検索」とは?

もしかして検索は、GoogleやYahoo!の検索窓に、間違った(と思われる)検索語を入力したとき、以下の画面例のように「もしかして○○」(Googleの場合)あるいは「○○ではありませんか?」(Yahoo!の場合)という文言を検索結果ページに表示する機能です。

Googleによる「もしかして○○」


Yahoo!による「○○ではありませんか?」


「○○」の部分にはその間違った検索語を訂正した正しい(と思われる)検索語がアンカーリンクで表示されます。そのため、もし訂正された検索語が正しかった場合、ユーザーは検索語を再入力することなく、リンクをクリックするだけで再検索できるようになります。「もしかして検索」は、ユーザーの省力化に大きく貢献する、大変優れた効果的な検索機能といえるでしょう。

GoogleやYahoo!で検索に親しんだお客様からは、Solrを使って運営するECサイトや企業内検索システムでも同様の機能を提供したいというご要望を多数いただいておりましたが、今回のリリースで本機能をご利用いただけるようになりました。

まずは使ってみる

ロンウイットのSolrサブスクリプション・パッケージ(以下、サブスクリプションと記す)は、インストール後すぐにいろいろな機能を試せるように、日本語のサンプルデータといくつかの目的別に設定された環境(solrconfig.xmlやschema.xmlなどの設定ファイル)が付属しています。日本語のサンプルデータとしては、歴代の内閣総理大臣の所信表明演説が付属していますので、ここではそのデータをそのまま使ってみます。また環境は、シングルコア構成のbasic環境を使います(マルチコア構成でももちろん使えますが、説明の中ではシングルコア構成のURLを示します)。

ではさっそく「もしかして検索」を試してみましょう。サンプルデータを登録してあるbasic環境を起動したら、Webブラウザから次のURLにアクセスしてください:
http://localhost:8080/solr/spell?q=笑止高齢化&spellcheck=true&spellcheck.build=true

通常の検索と同様、qパラメータに検索語を指定します。ここでは「笑止高齢化」という検索を行いました。もちろん、「少子高齢化」が正しいキーワードであり、「笑止高齢化」というキーワードはないので、ヒット件数は0件となります。ただし、Solrは次のように「少子高齢化」という正しいキーワード候補をXMLで返してくれます:
<?xml version="1.0" encoding="UTF-8"?>
<response>
  <lst name="responseHeader">
    <int name="status">0</int>
    <int name="QTime">105</int>
  </lst>
  <str name="command">build</str>
  <result name="response" numFound="0" start="0"/>
  <lst name="spellcheck">
    <lst name="suggestions">
      <lst name="笑止高齢化">
        <int name="numFound">1</int>
        <int name="startOffset">0</int>
        <int name="endOffset">5</int>
        <arr name="suggestion">
          <str>少子高齢化</str>
        </arr>
      </lst>
    </lst>
  </lst>
</response>

なお、日本語のもしかして検索は、Solrのスペルチェック機能を応用していますので、もしかして検索を発動させるときは、spellcheck=trueというパラメータを指定します。また「もしかして」の結果は上記のように”spellcheck”のパートに表示されます。これ以降の出力例では、”spellcheck”部分のみを表示し、ヘッダ部分など他の部分は省略します。

他の検索語でも試してみましょう。たとえば、「インターネット」の誤りである「いんたーねっt」(IMEを使ったローマ字日本語入力で、最後の”o”を入力せずに検索ボタンをクリックしてしまった例)を検索してみると:
http://localhost:8080/solr/spell?q=いんたーねっt&spellcheck=true

次のように期待通り「インターネット」が訂正候補として返されます:
<lst name="spellcheck">
  <lst name="suggestions">
    <lst name="いんたーねっt">
      <int name="numFound">1</int>
      <int name="startOffset">0</int>
      <int name="endOffset">7</int>
      <arr name="suggestion">
        <str>インターネット</str>
      </arr>
    </lst>
  </lst>
</lst>

さらに他のキーワードでも試してみましょう。たとえば、2つのキーワードを使った検索「滅入るマガジン 位置べー同名」ではいかがでしょうか:
http://localhost:8080/solr/spell?q=滅入るマガジン 位置べー同名&spellcheck=true

2つのキーワードとも正しく入力できていない例ですが、果たしてSolrは次のように2つのキーワード(順に「メールマガジン」「日米同盟」が正解)とも訂正してくれました:
<lst name="spellcheck">
  <lst name="suggestions">
    <lst name="滅入るマガジン">
      <int name="numFound">1</int>
      <int name="startOffset">0</int>
      <int name="endOffset">7</int>
      <arr name="suggestion">
        <str>メールマガジン</str>
      </arr>
    </lst>
    <lst name="位置べー同名">
      <int name="numFound">1</int>
      <int name="startOffset">8</int>
      <int name="endOffset">14</int>
      <arr name="suggestion">
        <str>日米同盟</str>
      </arr>
    </lst>
  </lst>
</lst>

2つ(以上)のキーワードを一度に訂正した全体の結果が欲しい場合、次のようにspellcheck.collate=trueというパラメータを使うと便利です:
http://localhost:8080/solr/spell?q=滅入るマガジン 位置べー同名&spellcheck=true&spellcheck.collate=true

すると、次のようにcollationというパートに「メールマガジン 日米同盟」という訂正後のキーワード全体が返され、クライアントは非常に楽に処理ができます:
<lst name="spellcheck">
  <lst name="suggestions">
    <lst name="滅入るマガジン">
      <int name="numFound">1</int>
      <int name="startOffset">0</int>
      <int name="endOffset">7</int>
      <arr name="suggestion">
        <str>メールマガジン</str>
      </arr>
    </lst>
    <lst name="位置べー同名">
      <int name="numFound">1</int>
      <int name="startOffset">8</int>
      <int name="endOffset">14</int>
      <arr name="suggestion">
        <str>日米同盟</str>
      </arr>
    </lst>
    <str name="collation">メールマガジン 日米同盟</str>
  </lst>
</lst>

もしかして検索のしくみ〜スペルチェック辞書の作成と利用

もしかして検索を実行するには、あらかじめ「スペルチェック辞書」を作成しておく必要があります。スペルチェック辞書は、「正解のキーワード」リストから生成したLuceneのインデックスです。正解のキーワードリストというのは、上記の例でいうと「少子高齢化」「インターネット」「メールマガジン」「日米同盟」などの訂正候補として表示されるキーワードのリストです。

正解のキーワードリストは下図のように、既存のSolrのインデックスから作成する(下図①)ことも、別途用意したテキストファイルから作成する(下図②)こともできます。上記の例では、サブスクリプションに付属している歴代の内閣総理大臣の所信表明演説のテキストデータを登録したSolrのインデックスから作成しました(下図①の方法):

もしかして検索のしくみとスペルチェック辞書


スペルチェック辞書を作成するには、最初の検索リクエスト時に、spellcheck.build=trueというパラメータを指定します(上記の例でも最初の検索リクエストのみにspellcheck.build=trueパラメータを指定しています)。

spellcheck.build=trueパラメータは、スペルチェック辞書を新たに作成するときのみ指定するようにしてください(検索リクエストのたびに毎回指定しないように注意してください)。正解のキーワードリストをSolrのインデックスから作成する場合は、newSearcherイベント実行時にspellcheck.build=trueを指定するとよいでしょう。一度スペルチェック辞書を作成してしまえば、それ以降の検索リクエストは既存のスペルチェック辞書を利用してもしかして検索機能が実行できるようになります。

solrconfig.xmlの設定

ここでは、日本語もしかして検索に必要なsolrconfig.xmlの設定について詳しく見ていきましょう。主要な設定は、サーチコンポーネントとリクエストハンドラの2つです。

サーチコンポーネント

まずは、サーチコンポーネントを見てみます:
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
  <str name="queryAnalyzerFieldType">text_ws</str>
  <lst name="spellchecker">
    <str name="classname">com.rondhuit.solr.spell.IndexBasedSpellChecker</str>
    <str name="name">default</str>
    <str name="field">statement_buzz</str>
    <str name="fieldType">text_ws</str>
    <str name="spellcheckIndexDir">spellchecker</str>
    <str name="wordAnalyzerFieldType">text_ja_spell</str>
  </lst>
</searchComponent>

SpellCheckComponentにspellcheckという名前をつけてサーチコンポーネントを定義しています。その内部ではまず、queryAnalyzerFieldTypeという指定があります。queryAnalyzerFieldTypeには、クエリーコンバーターで使用されるAnalyzerのフィールド型を指定します。クエリーコンバーターとは、qパラメータに指定された検索キーワードをトークンのリストに変換するためのクラスで、その内部ではクエリパーサーではなくAnalyzerを用いています。queryAnalyzerFieldTypeにはそこで使われるAnalyzerの元になるフィールド型を指定しますが、省略した場合はLuceneのWhitespaceAnalyzerが使われます。クエリーコンバーターはsolrconfig.xmlで指定できますが、指定しなかった場合はデフォルトのSpellingQueryConverterが使われます。

サーチコンポーネントのspellcheckerの中で設定する項目は、classnameで指定された各クラスにより異なります。ロンウイットのサブスクリプションでは、前述の正解のキーワードリストを既存のSolrのインデックスから作成する場合に使用するIndexBasedSpellCheckerと、別途用意したテキストファイルから作成する場合に使用するFileBasedSpellCheckerを提供しています。下表にそれぞれのクラスについて設定項目を示します:
パラメータIndexBased
SpellChecker
FileBased
SpellChecker
name複数のスペルチェック設定を行った場合、nameを指定してその設定に名前をつけます。nameを指定しなかったときはdefaultという名前になります。
fieldspellcheck.qパラメータで指定されたクエリをパースしてトークンリストを取得するときに使われるAnalyzerのもととなるフィールド名を指定します。また、IndexBasedSpellCheckerにおいてはキーワード候補を選択するフィールドの指定にもなります。
fieldType上記のfieldを上書きします。また、FileBasedSpellCheckerにおいてはテキストファイルの正解のキーワードをスペルチェック辞書に登録する際のAnalyzerとして機能します。
spellcheckIndexDirスペルチェック辞書のインデックスのディレクトリ名を指定します。
wordAnalyzerFieldTypeサブスクリプションでもしかして検索を行う場合は必ずtext_ja_spellと指定し、schema.xmlにそのフィールド型が設定されている必要があります。
sourceLocationLuceneなど他のアプリケーションで作成したインデックスの場合はそのインデックスの絶対パスを指定します。Solrのインデックスを使う場合は指定しません。confディレクトリに配置した正解のキーワードリストを記したテキストファイルのファイル名を指定します。
comparatorClassComparator<SuggestWord>を実装したコンパレータクラス名、または”score”か”freq”と指定します。デフォルトはscoreで、もしかしてで表示される候補の選択をスコアの高いもので行います。freqと指定すると、インデックス内での登場回数の多いキーワードが候補として選択されます。それ以外の基準で選びたいときは、独自クラスを実装できるようになっています。
thresholdTokenFrequencyもしかしてで表示されるキーワードを候補として選択するかどうか、インデックス内での最低頻度の割合で指定します。デフォルトは0で、すべてのキーワードを訂正候補として選択します。0.3と指定すると、30%以上の文書に使われるキーワードのみを訂正候補として選択します。(無視)
characterEncoding(無視)正解のキーワードリストを記載したファイルの文字コードを指定します。

リクエストハンドラ

次にリクエストハンドラの定義を見てみます:
<requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">
  <lst name="defaults">
    <str name="df">statement</str>
    <str name="spellcheck.onlyMorePopular">false</str>
    <str name="spellcheck.extendedResults">false</str>
    <str name="spellcheck.count">1</str>
  </lst>
  <arr name="last-components">
    <str>spellcheck</str>
  </arr>
</requestHandler>

ここで重要なのは、last-componentsで前述のサーチコンポーネントであるspellcheckを指定していることです。これにより、通常の検索に追加してもしかして検索を実行するようになっています。

リクエストハンドラの設定のその他の部分は、通常の検索で指定しているものと同じです。もしかして検索固有の”spellcheck.”で始まるパラメータについては、以下で説明します。

リクエストパラメータ

もしかして検索固有の”spellcheck.”で始まるパラメータについて、下表で説明します:
パラメータ説明
spellcheckもしかして検索を行う場合はtrue、行わない場合はfalseを指定します。
spellcheck.qもしかして検索を行う検索キーワードを指定します。指定しなかった場合、qパラメータが参照されます。qパラメータの場合は、クエリ文字列から訂正候補のキーワードを取り出すためにクエリコンバーターが使われるのに対し、spellcheck.qパラメータの場合は、field/fieldTypeのAnalyzerが使われるという違いがあります。
spellcheck.buildスペルチェック辞書を作成する場合にこのパラメータにtrueを指定してもしかして検索を実行します。
spellcheck.reloadスペルチェック辞書を再ロードします。
spellcheck.dictionarysolrconfig.xmlの中でnameを使って複数のスペルチェックを設定し分けているとき、その名前をこのパラメータで指定します。
spellcheck.count正解のキーワード候補を最大いくつ返してもらいたいかを指定します。デフォルトは1です。
spellcheck.onlyMorePopulartrueに設定すると、検索キーワードよりも多く検索本体のインデックスに登録されているキーワードを訂正候補として提示します(検索キーワードが正解の場合であっても)。デフォルトはfalseです。
spellcheck.extendedResultstrueに設定するとより詳細な情報を返すようになります。
spellcheck.collatetrueに設定すると複数の検索キーワードからなるクエリをまとめて訂正後のクエリ文字列にして提示してくれます。
spellcheck.maxCollationscollationで返す最大数を指定します。spellcheck.collate=trueのときのみ有効で、デフォルトは1です。
spellcheck.maxCollationTriescollationで返す訂正候補のキーワードを探す際、このパラメータの数を最大のトライ回数とします。spellcheck.collate=trueのときのみ有効で、デフォルトは0です。
spellcheck.maxCollationEvaluations同じくcollationで返す訂正候補のキーワードを探す際の組み合わせを試す最大回数です。たくさんのスペルミスを犯すユーザーのためのセーフティネットであり、デフォルトの10000は多くの場合で適当な数値です。
spellcheck.collateExtendedResultsspellcheck.collate=trueのときのみ有効で、collationの出力にさらに詳細な情報を付加させることができます。

もしかして検索結果のクライアント側の取り扱いについて

Solrに対してもしかして検索を実行したクライアントは、以下のようなもしかして検索の結果を含んだXMLを受信します:
<lst name="spellcheck">
  <lst name="suggestions">
    <lst name="今無いn精度かいかく">
      <int name="numFound">1</int>
      <int name="startOffset">0</int>
      <int name="endOffset">10</int>
      <arr name="suggestion">
        <str>公務員制度改革</str>
      </arr>
    </lst>
    <str name="collation">公務員制度改革</str>
  </lst>
</lst>

このSolrクライアントプログラムはこのXMLをHTMLに変換してユーザーのブラウザに表示するわけですが、「もしかして○○」と表示するかどうかは適当な基準を設けてアプリケーションごとに決めておく必要があります。たとえば、numFoundが0の場合や10件以下の場合、などとします。もしかしてで提示されるキーワードはあくまでも候補で、ユーザーのクエリが絶対間違っているという訳ではありません。そのためある程度ヒット件数が少ない場合のみ「もしかして○○」と表示する、というように決めておくのがよいでしょう。

まとめ

本稿ではサブスクリプション・パッケージ0.9から可能になった日本語対応のもしかして検索について、詳しくご説明しました。「もしかして検索」は、GoogleやYahoo!の検索でユーザーに広く親しまれている機能であり、ユーザーの省力化に大きく貢献する、大変優れた効果的な検索です。ぜひECサイトをはじめとしたインターネット向けの検索システムや企業内検索システムに導入してみてください。

KandaSearch

KandaSearch はクラウド型企業向け検索エンジンサービスです。
オープンAPIでカスタマイズが自由にできます。

  • セマンティックサーチ

    人間が理解するように検索エンジンがテキストや画像を理解して検索できます。

  • クローラー

    検索対象文書を収集するWebクローラーが使えます。

  • 簡単操作のUIと豊富なライブラリー

    検索や辞書UIに加え、定義済み専門用語辞書/類義語辞書やプラグインがあります。

  • ローコードで低コスト導入

    検索UIで使い勝手を調整した後、Webアプリケーションを自動生成できます。

セミナー

企業が検索エンジンを選定する際のポイントから、
実際の導入デモをお客様ご自身でご体験!