EXTENSIONS

第4回 全文検索のさまざまなテクニック|Apache Solr ハンズオンセミナー
無料

ご利用にはKandaSearchへの
ユーザー登録(無料)が必要です

Misc
第4回 全文検索のさまざまなテクニック|Apache Solr ハンズオンセミナー

最新バージョン: 1.0.0

開発者: RONDHUIT

ダウンロード数: 39

最終更新日: 2022-05-30

Copyright: RONDHUIT Co.,LTD

最新バージョン: 1.0.0

開発者: RONDHUIT

ダウンロード数: 39

最終更新日: 2022-05-30

Copyright: RONDHUIT Co.,LTD

Apache Solr ハンズオンセミナー「第4回 全文検索のさまざまなテクニック」のスライドです。

第4回 全文検索のさまざまなテクニック|Apache Solr ハンズオンセミナー

P01 表紙

第4回 全文検索のさまざまなテクニック Apache Solr ハンズオンセミナー
株式会社ロンウイット
関口宏司 @kojisays

P02 改訂履歴

2022年5月30日 初版

P03 ご注意

  • 本書演習やセミナー内で使用される辞書や設定ファイルなどの「エクステンション」は、 KandaSearch の利用規約 https://kandasearch.com/policy/terms の範囲内でご利用いただけます。

P04 本日の内容

  • フィールド横断検索
    • デバッグ
  • 前方一致/後方一致/部分一致検索の正しいやり方
  • ランダムソート
  • カテゴリー階層を考慮したファセット
  • クエリサジェスチョン(オートコンプリート)
  • 地理検索
  • [演習]
  • Q&A, アンケート

P05 [演習] 演習環境の準備

P06 [演習1] 検索エンジンプロジェクトの作成

これから Apache Solr のいろいろな機能を使っていきます。本セミナーでは Apache Solr 環境を簡単に準備できる KandaSearch を通じて Apache Solr を使っていきます。
そのためには、 https://kandasearch.com/ にログイン後、プロジェクトを作成します。

  1. https://kandasearch.com/ にログインします。
  2. 「プロジェクトを作成する」というボタンからプロジェクトを作成してみましょう。
    • プロジェクト名はわかりやすいものをつけましょう。日本語の名前も可能です。
    • 会社名(架空の名前でも可)などにしてもいいでしょう。
    • プロジェクト名は後から設定メニューでいくらでも変更が可能です。

P07 [演習1a] プロジェクト管理機能(オプション)

(お試しになりたい方のみお試しください。このステップを飛ばしても以降の[演習]には差し支えございません)

  1. プロジェクトメンバーに同僚の開発者を招待することができます。
    • 画面左の「メンバー」メニューから招待したい人のメールアドレスを入力して「管理者」または「ユーザー」の役割で招待できます。
  2. プロジェクトにアイコンを設定できます。
    • 画面左の「設定」メニューからプロジェクトのアイコンに使用する画像をアップロードできます。

P08 [演習2] 検索エンジンインスタンスを作成

作成したプロジェクト内に検索エンジンインスタンス、つまり Apache Solr を作成してみましょう。

  1. 画面左の「オーバービュー」メニューから「インスタンスを作成する」ボタンをクリックし、インスタンス作成ステップを開始します。
  2. プラン選択にて「一週間トライアル」(右下)を選んで次に進みます。
    • 一週間後に自動的に削除されます。
    • 「スタンドアロン」と「SolrCloud」が選択できますが、「スタンドアロン」を選びます。
  3. インスタンス情報入力にて「インスタンス名」と「インスタンスサブドメイン」、「IPアドレス範囲」を設定します。
    • 「インスタンスサブドメイン」はユニークである必要があります。プロジェクト名とインスタンス名に日本語等特殊文字を使用していない場合は、「自動生成」をクリックして自動で作成することができます。
    • 「IPアドレス範囲」に設定したIPアドレスからのみこの Apache Solr へのアクセスを許可するファイアウォールが設定されます。(最大4つ)
    • 最終確認で作成される検索エンジンインスタンスをご確認後、ボタンをクリックして作成し、1〜2分お待ちください。

P09 全文検索のさまざまなテクニック

P10 フィールド横断検索の実現方法

具体例として、titleとbodyフィールドを与えられたkeywordで検索する場面を考えます。以下の3つのやり方があります。

  1. copyFieldを使う方法 ×
    • OR演算子を使う方法より実現が楽。
    • Normが効かなくなるのでランキングが不適切になる。
    • copy先のフィールド型しか使えない。
<field name="text" type="text_ja" indexed="true" stored="false" multiValued="true"/>
<copyField source="title" dest="text"/>
<copyField source="body"  dest="text"/>
df=text&q=keyword
  1. OR演算子を使う方法 △
    • BooleanQuery(BQ)をSolrクライアントで展開。
    • OR演算子によるクエリ式展開が大変。
    • 形態素解析や文字N-gramなどを組み合わせて使える。
    • ランキングが不適切になる場面が生じる。
q.op=OR
q=title:keyword body:keyword
  1. edismaxを使う方法
    • 使い方が簡単。DisjunctionMaxQuery(DMQ)とBQを組み合わせたクエリ式がSolrにより自動生成される。
    • 形態素解析や文字N-gramなどを組み合わせて使える。
    • ランキングが適切。
defType=edismax
qf=title^3 body
q=keyword

P11 BooleanQueryとDisjunctionMaxQueryの比較

下のように2件の文書を持つインデックスに対して、
apple computer
をtitleとbodyフィールドを横断検索する場合を考えます。

インデックスの内容(カッコ内数値はフィールドの重みを示す) | id | title (x1.5) | body (x1.0) | |-------|--------------|---------------| | item1 | apple | apple fruit | | item2 | computer | apple desktop |

BQのOR演算子で展開する方法
q.op=OR&
q=(title:apple^1.5 body:apple) (title:computer^1.5 body:computer)

BQ { BQ( title:apple^1.5 OR body:apple ) OR
BQ( title:computer^1.5 OR body:computer ) }

⇒ BQ( A OR B ) ⇒ スコアはA+Bの合計となる。

id title (x1.5) body (x1.0)
item1 apple apple fruit (1.5 + 1.0) + (0.0 + 0.0) = 2.5
item2 computer apple desktop (0.0 + 1.0) + (1.5 + 0.0) = 2.5

BQとDMQの組み合わせで展開する方法
defType=edismax&qf=title^1.5 body&
q=apple computer

BQ { DMQ( title:apple^1.5 | body:apple ) OR
DMQ( title:computer^1.5 | body:computer ) }

⇒ DMQ( A | B ) ⇒ スコアは max(A,B) となる。

id title (x1.5) body (x1.0)
item1 apple apple fruit max(1.5, 1.0) + max(0.0, 0.0) = 1.5
item2 computer apple desktop max(0.0, 1.0) + max(1.5, 0.0) = 2.5

P12 [演習3] BQのOR演算子展開とedismaxの比較

  1. KandaSearchのライブラリーから演習用データセットを探してローカルPCにダウンロードし、UNZIPします。
  2. 新しいコレクションcollection1を新規作成します。
  3. UNZIPしたデータの中で、seminar4-dismax1.jsonをKandaSearch UIからインデクシングします。
  4. KandaSearch の検索UIを使って以下の通り設定します。
    • コレクション:collection1
    • ユニークキー:id
    • タイトル:title
    • ボディ:keywords
  5. 以下の2つの方法で検索し、検索結果表示順を比較しましょう。
    • BQのOR演算子展開(defType=lucene):q=(title:apple^1.5 keywords:apple) (title:computer^1.5 keywords:computer)
    • edismax(defType=edismax):q=apple computer, qf=title^1.5 keywords

P13 DisjunctionMaxQueryのtie breakerパラメーター

下のように3件の文書を持つインデックスに対して、
apple computer
をtitleとbodyフィールドを横断検索する場合を考えます。

id title (x1.5) body (x1.0)
item1 apple apple fruit
item2 computer apple desktop
item3 computer apple computer

edismaxをtieパラメーターなしで使った場合
defType=edismax&qf=title^1.5 body&
q=apple computer

BQ { DMQ( title:apple^1.5 | body:apple ) OR
DMQ( title:computer^1.5 | body:computer ) }

id title (x1.5) body (x1.0)
item1 apple apple fruit max(1.5, 1.0) + max(0.0, 0.0) = 1.5
item2 computer apple desktop max(0.0, 1.0) + max(1.5, 0.0) = 2.5
item3 computer apple computer max(0.0, 1.0) + max(1.5, 1.0) = 2.5

BQとDMQの組み合わせで展開する方法
defType=edismax&qf=title^1.5 body&tie=0.1&
q=apple computer

BQ { DMQ( title:apple^1.5 | body:apple ) w/ tie=0.1 OR
DMQ( title:computer^1.5 | body:computer ) w/ tie=0.1 }

⇒ DMQ( A | B ) w/ tie=0.1 ⇒ スコアは max(A,B) + second(A,B)*0.1 となる。

id title (x1.5) body (x1.0)
item1 apple apple fruit max(1.5, 1.0) + 1.00.1 + max(0.0, 0.0) + 0.00.1 = 1.6
item2 computer apple desktop max(0.0, 1.0) + 0.00.1 + max(1.5, 0.0) + 0.00.1 = 2.5
item3 computer apple computer max(0.0, 1.0) + 0.00.1 + max(1.5, 1.0) + 1.00.1 = 2.6

P14 [演習4] edismaxのtie breakerの動作確認(1)

  1. 前の演習からの続きです。UNZIPしたデータの中で、seminar4-dismax2.jsonをKandaSearch UIからcollection1にインデクシングします。
  2. KandaSearch の検索UIを使って以下の通り設定します。
    • コレクション:collection1
    • ユニークキー:id
    • タイトル:title
    • ボディ:keywords
  3. 前の演習同様、次のようにedismaxで検索します。しかし、item1が先頭に来てしまい、予想と異なります。これは次の演習で修正します。
    • edismax:q=apple computer, qf=title^1.5 keywords, defType=edismax

P15 [演習5] edismaxのtie breakerの動作確認(2)

前の演習で予想と異なり、item1が先頭に来てしまうのは、titleフィールドに出現する単語数がcomputer > appleとなることで、IDFが効き過ぎてしまっているためです。以下の手順でこの「効き過ぎ」を解消し、再度同じ検索、次いでtieパラメーターを使って検索し、結果を比較します。

  1. managed-schemaに以下の行を追加し、collection1をリロードします。
    <similarity class="solr.ClassicSimilarityFactory"/>
    
  2. KandaSearch の検索UIを使って前問のedismax検索を実行します。今度は予想通り、item2, item3, item1の順に表示されます。
  3. 次に、tie=0.1を追加設定し、再度検索して検索結果表示順を確認します。
  4. Solr Admin UIにて、wt=xml、debugQuery=onとすると、スコア計算の算出根拠を詳しく見ることができます。

P16 検索エンジンにおける前方/後方/部分一致検索とは?

  • 一般的な前方一致/後方一致/部分一致検索のイメージは以下のようなものでしょう。
    • 正規表現による前方一致検索:grep -E "^inter" foo.txt
    • ワイルドカードによる後方一致検索:ls *.txt
    • ワイルドカードによる部分一致検索:ls *-1.0-SNAPSHOT*
  • 検索エンジンの検索の単位は単語なので、(特に文字N-gramであれば)基本的にユーザーからは「部分一致検索」がされているように見えます。
    • たとえば、「ここではきものを脱ぐ」を対象に、「きもの」や「はきもの」を文字N-gram(たとえば2-gram)で検索すれば、両方ともヒットするので、ユーザーからは部分一致検索ができているように見えます。
  • 一方、そのこととは別に、Apache Solr(Lucene)は単語のワイルドカード検索が可能です。
    • ""と"?"をサポートしています。しかしながらワイルドカード検索は遅く、特に```""```で開始するワイルドカード検索は推奨されていません。⇒前方一致/後方一致/部分一致をワイルドカード検索で行うのはアンチパターンです。

P17 これ以降の演習で使用するデータについて

{
  "id": 1,
  "category": "コミュニティ関連施設",
  "name": "市民活動サポートセンター",
  "hierarchy": "市民利用施設/市民文化・社会教育系施設/コミュニティ関連施設/市民活動サポートセンター",
  "address": "浦和区東高砂町11番1",
  "location": "35.858694,139.659134"
}

P18 [演習6] さいたま市オープンデータのインデクシング

  • KandaSearch のライブラリーからさいたま市のオープンデータ「公共施設の位置情報(緯度経度)一覧」のデータとそのconfigをダウンロードします。コレクションgeospatialを作成してデータをインデクシングします。

P19 [演習7] 部分一致検索の動作確認

以降の演習では、nameフィールドで「部分一致検索」「前方一致検索」「後方一致検索」を行ってみます。「プラザ」を部分一致で検索するために、name_2gフィールドに対して「プラザ」を検索してみましょう。以下のようにパラメーターを設定し、「プラザ」を部分一致検索してください。

  • 共通に設定するパラメーター
    • コレクション:geospatial
    • ユニークキー:id
    • ボディ:location
    • defType:lucene
  • 部分一致検索のためのパラメーター
    • タイトル:name_2g
    • デフォルトフィールド(df):name_2g
    • ハイライトフィールド:name_2g

P20 [演習8] 前方一致検索の動作確認

次に、前方一致検索するために、name_forwardフィールドに対して「プラザ」を検索してみましょう。以下のようにパラメーターを設定して検索します。

  • 前方一致検索のためのパラメーター
    • タイトル:name_forward
    • デフォルトフィールド(df):name_forward
    • ハイライトフィールド:name_forward

Solr Admin UIのAnalysisを使って、name_forward(text_forward)フィールドの動作を確認してみましょう。

P21 前方一致検索のためのフィールド定義

<fieldType name="text_forward" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <tokenizer class="solr.EdgeNGramTokenizerFactory" minGramSize="1" maxGramSize="100"/>
    # プラザイースト → プ、プラ、プラザ、プラザイ、プラザイー、プラザイース、プラザイースト
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
  <analyzer type="query">
    <tokenizer class="solr.KeywordTokenizerFactory"/>
    # プラザ → プラザ
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>

P22 後方一致のためのフィールド定義

<fieldType name="text_backward" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <tokenizer class="solr.KeywordTokenizerFactory"/>
    # ワークプラザ →
    <filter class="solr.ReverseStringFilterFactory"/>
    # → ザラプクーワ
    <filter class="solr.LowerCaseFilterFactory"/>
    <filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="100"/>
    # → ザ、ザラ、ザラプ、ザラプク、ザラプクー、ザラプクーワ
  </analyzer>
  <analyzer type="query">
    # プラザ →
    <tokenizer class="solr.KeywordTokenizerFactory"/>
    <filter class="solr.ReverseStringFilterFactory"/>
    <filter class="solr.LowerCaseFilterFactory"/>
    # → ザラプ
  </analyzer>
</fieldType>

P23 [演習9] 後方一致検索の動作確認

では実際に、後方一致検索をするために、name_backwardフィールドに対して「プラザ」や「センター」を検索してみましょう。以下のようにパラメーターを設定して検索します。

  • 後方一致検索のためのパラメーター
    • タイトル:name_backward
    • デフォルトフィールド(df):name_backward
    • ハイライトフィールド:name_backward ⇒ 後方一致検索はうまくハイライトできません。

Solr Admin UIのAnalysisを使って、name_backward(text_backward)フィールドの動作を確認してみましょう。

さらに、Schemaでname_backwardを選択し、Load Term Infoにてトップ100個ほどの単語を表示させて、文字順が逆転してインデクシングされていることを確認します。

P24 ランダムソート

  • モール型のECサイト等において、出展者の公平性を担保するために、適当な期間、検索結果をランダムに表示させたい、ということがあります。
    • 「適当な期間」の例:同一検索セッション内、1時間毎、1日、1週間、etc.
  • 以下のような方法だとうまくいきません。
    • 単純にランダムに検索結果を表示する方法⇒ページ送り等をしたときに、前のページと同じ商品が表示されてしまう。
    • 文書フィールドにあらかじめ乱数を埋め込んでおき、そのフィールドでソートする方法⇒ソート順を変えたいとき、再インデクシングが必要になってしまう。

Apache Solr にはランダムソート用にrandomというフィールド型があり、下記のような設定と検索時のソートパラメーター指定だけで動作します。

設定

<fieldType name="random" class="solr.RandomSortField" indexed="true"/>
<dynamicField name="random_*"   type="random" />

- 検索時のソートパラメーター指定:sort=random_<乱数> desc
- しくみ:randomフィールドでソートが指定されると、文書毎に「フィールド名+文書ID+インデックスバージョン」をシードにした乱数が計算されて当該フィールドに動的に割り当てられます。これにより、<乱数>の部分を適当なタイミングで切り替えるだけで、ランダムソートによる検索結果順の切り替えタイミングを自由に制御できます。

P25 [演習10] ランダムソートの動作確認

KandaSearch のライブラリーからECサイトのデータとconfigを入手し、コレクションecを作成して、ECサイトデータをインデクシングします。その上で以下のように指定して通常の検索とランダムソート付き検索を試してみましょう。

  • 以下のように設定して「のり」を通常検索してみます。検索結果表示順を控えておきます。
    • コレクション:ec、タイトル:productName、ボディ:productInfo、defType=edismax
  • 次に、ソートに以下のように設定して検索します。"1111"の部分を変えると検索結果表示順が変わることを確認します。
    • ソート(descはascでも可):random_1111 desc

P26 カテゴリー階層を考慮したファセット

  • Apache Solrで提供されるPathHierarchyTokenizerを使うと、次のようなカテゴリー階層を考慮したファセット(と絞り込み検索)が実現できます。
    • Apache Solrのクライアントプログラム側でも若干の協力が必要です。
  • PathHierarchyTokenizerはデリミタ(デフォルトで"/")毎に階層的にトークンを出力します。(例)/aaa/bbb/ccc ⇒ /aaa, /aaa/bbb, /aaa/bbb/ccc
  • 階層的トークンの出力例
ID 文書 トークン
1 りんご(青森県産) /果物/りんご ⇒ /果物, /果物/りんご
2 りんご(山形県産) /果物/りんご ⇒ /果物, /果物/りんご
3 みかん(和歌山県産) /果物/みかん ⇒ /果物, /果物/みかん
4 みかん(愛媛県産) /果物/みかん ⇒ /果物, /果物/みかん
5 ぶどう(山梨県産) /果物/ぶどう ⇒ /果物, /果物/ぶどう
"/果物", 5,             果物 (5)
"/果物/りんご", 2,         └りんご (2)
"/果物/みかん", 2,       └みかん (2)
"/果物/ぶどう", 1          └ぶどう (1)

P27 [演習11] PathHierarchyTokenizerの動作確認

Solr Admin UIのAnalysisを使って、PathHierarchyTokenizerの動作を確認しましょう。コレクションはecまたはgeospatialのどちらかを使います。

  1. Solr Admin UIのAnalysisでpathhieフィールド型を選択します。
  2. Field Value (Index)に /aaa/bbb/ccc と入力して Analyse Values ボタンをクリックし、出力されるトークン列を確認しましょう。
    <fieldType name="pathhie" class="solr.TextField">
    <analyzer type="index">
     <tokenizer class="solr.PathHierarchyTokenizerFactory" delimiter="/"/>
    </analyzer>
    <analyzer type="query">
     <tokenizer class="solr.KeywordTokenizerFactory"/>
    </analyzer>
    </fieldType>
    

P28 [演習12] カテゴリー階層ファセットの動作確認

ここではecコレクションを使ってカテゴリー階層を考慮したファセットの動作を確認します(geospatialコレクションの場合、hierarchyフィールドが階層ファセットになります)。

  1. KandaSearch UIでecコレクションを全件検索します。
  2. 検索結果の「本(20)」のチェックボックスをクリックして、「本」で絞り込みます。
    • 「本(20)」直下の内訳が「本/和書(15)」「本/洋書(5)」となっていることを確認します(さらにそれより下の階層の内訳や件数を確認します)。
  3. さらに「本/和書(15)」のチェックボックスをクリックして、「本/和書」で絞り込みます。
  4. 検索結果の「JSONを表示する」をクリックして、検索結果JSONレスポンスを確認します。

P29 クエリサジェスチョン

  • 「(クエリ)サジェスチョン」は、別名「オートコンプリート」などとも呼ばれ、検索キーワード入力を補完する機能として、検索サイトでよく見られるようになりました。
  • 検索窓に数文字タイプし始めたときに、検索キーワードを提示(サジェスト)してくれる機能です。
  • Apache Solr では SuggestComponent を使って実現できます。
    • 詳細についてはApache Solrのマニュアルや演習で用いられる設定を参照してください。
  • Apache Solr のサジェスチョン機能は任意のキーワードを提示できますが、以下の点に注意しながら実施する必要があります。
    • サジェストしたキーワードで検索した場合、「ヒット0件」が発生してはいけません。そのため、Solrのログレコードの中からヒット件数がN件以上(N>0)の検索キーワードを抽出したり、インデクシング対象文書から抽出するなど、工夫します。
    • 次ページの演習では、インデクシング対象文書の特定フィールドのデータをサジェストデータとして用います。

P30 [演習13] クエリサジェスチョン

geospatialまたはecコレクションで試すことができます。KandaSearch UIの検索のSUGGESTタブに設定するだけで試すことができます。defTypeはどちらもedismaxとします。

  • geospatialコレクションの場合
    • コレクション:geospatial
    • リクエストハンドラ:/suggest 辞書:suggester(他にはinfixSuggester、fuzzySuggesterが使えます。これらをカンマ区切りで複数指定もできます)
  • ecコレクションの場合
    • コレクション:ec
    • リクエストハンドラ:/suggest 辞書:suggester(他にはinfixSuggester、fuzzySuggesterが使えます。これらをカンマ区切りで複数指定もできます)

P31 [演習14] クエリサジェスチョン(Advanced)

Apache Solr 提供のサジェスチョン機能は、日本語IMEの動作を考慮に入れていないので、日本語入力下でのサジェストがうまく動作しないことが多々あります。

RCSSを使うとこの不具合が解消され、日本語サジェスチョンがうまく動作します。

  1. geospatial/ecコレクションのmanaged-schemaとsolrconfig.xmlのコメントアウトされている「RCSS依存部分」のコメントを外して有効化します。
  2. KandaSearch のライブラリーからRCSSを入手し、インスタンスのdata/lib/または各コレクション下のlib/にダウンロードします。
    • インスタンスのdata/lib/にダウンロードした場合は、インスタンスの再起動が必要です。
    • 各コレクション下のlib/にダウンロードしたときは、コレクションのリロードが必要です。
  3. SUGGEST設定の「辞書」にて、rcssSuggester, rcssInfixSuggester, rcssFuzzySuggesterを1つ、またはカンマ区切りで複数指定して、検索を試します。日本語IMEが働いている環境でも、サジェストが正しく動作します。

P32 [演習15] 地理検索(空間検索)

前の演習で作成したgeospatialコレクションには、さいたま市の公共施設データがその緯度・経度情報と共にインデクシングされています。このコレクションを利用して、下記の手順で地理検索を試します。

  1. 以下の通り指定して「市役所」を検索します。
    • コレクション:geospatial、ユニークキー:id、タイトル:name、ボディ:location、defType=edismax
  2. 検索結果に「さいたま市役所」が表示されるので、緯度経度情報をコピーします。
  3. Google Map https://www.google.co.jp/maps/?hl=ja を表示し、コピーした緯度経度情報を検索して地図上に「さいたま市役所」を表示させます。
  4. KandaSearch UIに戻り、以下の通り指定して、全件検索(*:*)します。すると、さいたま市役所から1km以内の公共施設が近い順に表示されます。 リクエストハンドラ(qt):/spatial 任意のパラメーター:pt=35.861648,139.645496
  5. solrconfig.xmlの/spatialに地理検索に下記の必要なパラメーターが設定されているのでそれを確認します。
spatial=true        # 地理検索を実行
fq={!geofilt}
sort=geodist() asc    # ptから近い順にソート
sfield=location        # 緯度・経度フィールド名
d=1            # 半径1km以内

P33 Q&A, アンケート

お見積もり・詳細は KandaSearch チームに
お気軽にお問い合わせください。

お問い合わせ