ご利用にはKandaSearchへの
ユーザー登録(無料)が必要です
最新バージョン: 1.2.0
開発者: RONDHUIT
ダウンロード数: 46
最終更新日: 2022-07-11
Copyright: RONDHUIT Co.,LTD
最新バージョン: 1.2.0
開発者: RONDHUIT
ダウンロード数: 46
最終更新日: 2022-07-11
Copyright: RONDHUIT Co.,LTD
Apache Solr ハンズオンセミナー「第1回 Apache Solr 入門」のスライドです。
第1回 Apache Solr 入門
Apache Solr ハンズオンセミナー
株式会社ロンウイット
関口宏司 @kojisays
2022年5月9日 | 初版 |
---|---|
2022年5月20日 | エクステンション利用に関する注意事項を追加。 |
2022年6月22日 | Webアプリダウンロードの演習を追加。 |
2022年7月11日 | 1)本編の一部をAppendixに移動。2)KandaSearchの拡張機能メニュー変更に合わせ内容更新。 |
これから Apache Solr のいろいろな機能を使っていきます。本セミナーでは Apache Solr 環境を簡単に準備できる KandaSearch を通じて Apache Solr を使っていきます。
そのためには、 https://kandasearch.com/ にログイン後、プロジェクトを作成します。
(お試しになりたい方のみお試しください。このステップを飛ばしても以降の[演習]には差し支えございません)
作成したプロジェクト内に検索エンジンインスタンス、つまり Apache Solr を作成してみましょう。
順次検索方式
転置インデックス方式
検索対象文書
人手によるラベル抽出
コンピューターによるラベル抽出
文書ID | 検索対象文書 |
---|---|
1 | カツオはサザエの弟 |
2 | サザエはワカメの姉 |
3 | ワカメはカツオの妹 |
カツオ:1, は:1, サザエ:1, の:1, 弟:1,
サザエ:2, は:2, ワカメ:2, の:2, 姉:2,
ワカメ:3, は:3, カツオ:3, の:3, 妹:3
単語 | ID |
---|---|
の | 1,2,3 |
は | 1,2,3 |
カツオ | 1,3 |
サザエ | 1,2 |
ワカメ | 2,3 |
姉 | 2 |
弟 | 1 |
妹 | 3 |
検索質問 q=サザエ
↓
単語 | ID |
---|---|
サザエ | 1,2 |
↓
文書1と2を返す
検索質問 q=ワカメ AND 姉
↓
単語 | ID |
---|---|
ワカメ | 2,3 |
姉 | 2 |
↓
文書2を返す
リレーショナルデータベース | 検索エンジン | |
---|---|---|
特徴 | 関係モデル トランザクション管理 CRUD | 高速な全文検索 |
テーブル | 複数 正規化 | 単一 非正規化 |
検索 | SELECT 関係論理演算 | キーワード検索 (AND/OR/NOT、表記揺れ、類義語、・・・) |
検索結果 | 集合 ソート | 関連度(クエリと文書の類似度)順 |
検索質問 q=ワカメ OR 妹
↓
単語 | ID |
---|---|
ワカメ | 2,3 |
妹 | 3 |
↓
文書3,2の順で返す
スコア=クエリと文書の関連度
e.g. score( q, d ) = cos( q, d )
値は単語の重要度(ここでは個数)
tf*idf などが使われることが多い。
形態素解析
文字N-gram
(例1)ここではきものを脱ぐ
ここ / では / きもの / を / 脱ぐ
ここ / で / はきもの / を / 脱ぐ
(例2)人間違い
人 / 間違い
人間 / 違い
検索質問 q=きもの
↓
単語 | ID |
---|---|
ここ | 1, … |
で | 1, … |
はきもの | 1, … |
を | 1, … |
脱ぐ | 1, … |
↓
ヒットなし
検索質問① q=きもの → PhraseQuery("きも もの")
検索質問② q=はきもの → PhraseQuery("はき きも もの")
↓
単語 | ID |
---|---|
: | : |
きも | 1, … |
はき | 1, … |
もの | 1, … |
: | : |
↓
どちらもヒットあり
検索質問 q=妹
↓
単語 | ID |
---|---|
: | : |
の姉 | 2 |
の弟 | 1 |
の妹 | 3 |
: | : |
カツ | 1 |
ツオ | 1 |
: | : |
↓
ヒットなし
クエリ
↓
検索機能
単語分割機能
転置インデックス
インデクシング機能
↓
検索結果
・インターネット
・データベース
・書誌データ
・企業内データ
Elasticsearch との比較
その他の商用検索エンジン
インスタンスが起動したら Apache Solr の管理画面にアクセスしてみましょう。
インデクシングリクエスト / 検索リクエスト
http://localhost:8983/solr/collection1
・/collection1
managed-schema
solrconfig.xml
・インデックス
http://localhost:8983/solr/collection2
・/collection1
managed-schema
solrconfig.xml
・インデックス
インスタンス内に好きなだけ(ただしその分、メモリやディスクを消費します)コレクションを作成できます。コレクション1つにつき、独立したインデックス1つが作成されます。1つ目のコレクションを作ってみましょう。
<uniqueKey>id</uniqueKey>
<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="name" type="text_general" indexed="true" stored="true"/>
<field name="cat" type="string" indexed="true" stored="true" multiValued="true"/>
<field name="features" type="text_general" indexed="true" stored="true" multiValued="true"/>
<field name="price" type="pfloat" indexed="true" stored="true"/>
<query>
<filterCache size="512" initialSize="512" autowarmCount="0"/>
</query>
<requestHandler name="/select" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<int name="rows">10</int>
<bool name="preferLocalShards">false</bool>
</lst>
</requestHandler>
作成したコレクションに Solr 付属のサンプルデータを登録(インデクシング)します。サンプルデータは KandaSearch のライブラリーから入手します。
インデクシングする文書はXMLやJSONで以下のように表現します。1文書はフィールド名とその値のペアのセットです。
<add>
<doc>
<field name="id">IW-02</field>
<field name="name">iPod & iPod Mini USB 2.0 Cable</field>
<field name="manu">Belkin</field>
<!-- Join -->
<field name="manu_id_s">belkin</field>
<field name="cat">electronics</field>
<field name="cat">connector</field>
<field name="features">car power adapter for iPod, white</field>
<field name="weight">2.0</field>
<field name="price">11.50</field>
<field name="popularity">1</field>
<field name="inStock">false</field>
<!-- San Francisco store -->
<field name="store">37.7752,-122.4232</field>
<field name="manufacturedate_dt">2006-02-14T23:55:59Z</field>
</doc>
</add>
[
{
"id" : "978-0641723445",
"cat" : ["book","hardcover"],
"name" : "The Lightning Thief",
"author" : "Rick Riordan",
"series_t" : "Percy Jackson and the Olympians",
"sequence_i" : 1,
"genre_s" : "fantasy",
"inStock" : true,
"price" : 12.50,
"pages_i" : 384
},
{
"id" : "978-1423103349",
"cat" : ["book","paperback"],
"name" : "The Sea of Monsters",
"author" : "Rick Riordan",
"series_t" : "Percy Jackson and the Olympians",
"sequence_i" : 2,
"genre_s" : "fantasy",
"inStock" : true,
"price" : 6.49,
"pages_i" : 304
}
]
Solr のサンプルデータが正しく登録されているか、検索して確認します。検索で使えるパラメーターや確認事項は多岐にわたりますので、講師の操作を参考にしながらいろいろ試してみてください。
Apache Solr は検索結果をXMLやJSON形式で返却します。そのため、一般的に下図のようなWebアプリケーション・サーバーを配置して、HTMLに変換します。
検索ユーザー
↓
<検索リクエスト>
↓
Webアプリケーションサーバー
↓
<検索リクエスト>
↓
Apache Solr・インデックス ↓
<検索結果レスポンス(XML/JSON)>
↓
Webアプリケーションサーバー
↓
<検索結果HTMLページ>
↓
検索ユーザー
Apache Solr への検索リクエスト②(HTTP GETの場合)
http://localhost:8983/solr/collection1/select?q=検索キーワード&start=0&rows=10
・プロトコル
・Solrサーバーホスト名&ポート番号
・コンテキスト名
・コレクション名
・リクエストハンドラー名
リクエストパラメーター
検索は /select リクエストハンドラーに、HTTP GET(POSTも可)リクエストを投げることで行います。基本的なパラメーターを以下に示します。
パラメーター | 説明 | 例 |
---|---|---|
q | 検索キーワードや検索式(次ページ)を指定。 | q=人工知能 |
start | 取得する検索結果の開始位置(デフォルトは0)を指定。 | # 最初の10件を取得 start=0&rows=10 # 次の10件を取得 start=10&rows=10 |
rows | 取得する検索結果件数(デフォルトは10)を指定。 | |
df | デフォルト検索フィールド名を指定。 | df=features |
fl | 検索結果として返却するフィールドをカンマ区切りで指定。 | fl=id,name,score |
fq | フィルタークエリを指定。 | fq=cat:memory |
q.op | デフォルトオペレーターとしてOR(デフォルト)またはANDを指定。 | q.op=AND |
sort | 昇順/降順でソートするフィールド(複数可)を指定。 | sort=price asc, score desc |
クエリパーサーがluceneまたはedismaxのとき(defType=lucene または edismax)、以下のような検索式がqパラメーターに指定できます。
例 | 説明 |
---|---|
q=人工知能 | 「人工知能」を検索します。フィールドが指定されていないのでdfで指定されたフィールドを検索します。dfが指定されていないとエラーになります。 |
q=name:人工知能 | nameフィールドを「人工知能」で検索します。 |
q=name:人工知能 AND cat:game | nameフィールドに「人工知能」かつcatフィールドに「game」を含む文書を検索します。 |
q=name:(AI OR 人工知能) NOT cat:game | nameフィールドに「AI」または「人工知能」を含むがcatフィールドに「game」を含まない文書を検索します。 |
q=price:[0 TO 100] | priceフィールドの値が0から100までの文書を検索します。 |
q=title:人工知能^10 body:人工知能 | titleフィールドまたはbodyフィールドに「人工知能」を含む文書を検索します。titleフィールドに重み10(デフォルトは1)をかけ、bodyフィールドより重要度を上げています。 |
q="artificial intelligence" | デフォルトフィールド(df)を"artifical intelligence"というフレーズで検索します。 |
{
"responseHeader":{
"status":0,
"QTime":0,
"params":{
"q":"id:[0000000001 TO 0000000002]",
"fl":"id,score,childcareCenterName,address1",
"q.op":"OR",
"rows":"10"}},
"response":{"numFound":2,"start":0,"maxScore":1.0,"numFoundExact":true,"docs":[
{
"id":"0000000001",
"childcareCenterName":"大森小学校子どもルーム",
"address1":"千葉県千葉市中央区大森町268",
"score":1.0},
{
"id":"0000000002",
"childcareCenterName":"寒川小学校子どもルーム",
"address1":"千葉県千葉市中央区寒川町1-205",
"score":1.0}]
}
}
Solr 付属のサンプルデータは日本語を含まないので日本語の動作がよくわかりません。これ以降は日本語データを登録して日本語を検索できるか試してみます。
{ "responseHeader":{
"status":0,
"QTime":4,
"params":{
"q":"放課後",
"hl":"true",
"fl":"id,score,childcareCenterName,address1",
"hl.fl":"childcareCenterName",
"rows": 2,
"facet":"off"}},
"response":{"numFound":235,"start":0,"maxScore":61.1878,"numFoundExact":true,"docs":[
{ "id":"0000000179",
"childcareCenterName":"船橋放課後ルーム",
"address1":"千葉県船橋市本町4-17-20",
"score":61.1878},
{ "id":"0000000180",
"childcareCenterName":"湊町放課後ルーム",
"address1":"千葉県船橋市湊町1-16-5",
"score":61.1878}]
},
"highlighting":{
"0000000179":{"childcareCenterName":["船橋<b>放課後</b>ルーム"]},
"0000000180":{"childcareCenterName":["湊町<b>放課後</b>ルーム"]}}}
productNameとproductInfoの両フィールドを一度に検索するにはどうしたらいいでしょうか。次の2通りの場合で考えてみてください。
※ edismaxは、デファクトスタンダードなクエリパーサーです。luceneクエリパーサーのほぼ全ての機能を包含しつつ、フィールドの横断検索等が簡単にできるようになっています。edismax について詳しくは「第4回 全文検索のさまざまなテクニック」で説明します。
千葉県のオープンデータ「子育て施設一覧」(KandaSearch のライブラリーから取得できます)を使ってファセットと絞り込み検索をやってみます。これまでの操作と同様に、コレクション(childcareとします)を作成し、データをインデクシングします(ダウンロードしたZIPファイルを解凍する必要があります)。インスタンスの「検索」から以下を試します。
以下の2つの社内検索システム、どちらが使いやすいでしょうか。
A. あらかじめファイル種別や作成日を選ばなければ検索できないシステム
B. とりあえず検索でき、検索結果を見ながらファイル種別や作成日を選んで検索結果を絞り込めるシステム
「議事録」で検索!
q=議事録&facet=on
&facet.query=date:[* TO 2020-12-31T23:59:59Z]
&facet.query=date:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]
&facet.query=date:[2022-01-01T00:00:00Z TO *]
&facet.field=filetype
最初のレスポンス
"facet_queries":{
"date:[* TO 2020-12-31T23:59:59Z]":85,
"date:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]":60,
"date:[2022-01-01T00:00:00Z TO *]":48},
"facet_fields":{
"filetype":[
"PDF",100,
"Word",50,
"Excel",43]}
絞り込んで検索!
q=議事録&fq=filetype:Word&facet=on
&facet.query=date:[* TO 2020-12-31T23:59:59Z]
&facet.query=date:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]
&facet.query=date:[2022-01-01T00:00:00Z TO *]
絞り込み後のレスポンス
"facet_queries":{
"date:[* TO 2020-12-31T23:59:59Z]":0,
"date:[2021-01-01T00:00:00Z TO 2021-12-31T23:59:59Z]":20,
"date:[2022-01-01T00:00:00Z TO *]":30},
"facet_fields":{
"filetype":[]}
前の演習で作成した「EC」または「子育て支援施設」のコレクションを使って検索メニューで検索できることを確認した状態で、同じ検索メニューの中で「WEBアプリダウンロード(JAVA)」をクリックします(タイミングによってダウンロードに時間がかかることがあります)。ダウンロードしたZIPファイルを展開し、README.mdにしたがって以下のように起動すると、Webアプリケーションが起動しますので、http://localhost:9090/ にアクセスしてWeb画面から検索をすぐさま実行できます。ZIPファイルにはソースコードとGradleのビルド環境も全て含まれていますので、Webアプリケーションの開発の参考にすることができます。なお、実行とビルドにはJava 1.8以上が必要です。
java -jar ks-search-client-1.0.2.war
スタンドアロンの Solr を起動/停止するには、Apache Solr に付属の solr コマンドを使って次のように行います。
solr start
solr stop
スタンドアロンの Solr にコレクション(コア)を作成するには、Apache Solr に付属の solr コマンドを使って次のように行います。
solr create_core -c collection1 -d sample_techproducts_configs
インデクシングは Apache Solr に付属の post コマンドを使って次のように行います。
post -c collection1 -commit yes example/exampledocs/*.xml
<delete>〜</delete>
というXMLをPOSTして実行します。削除する文書のユニークキー値をに指定して削除
<delete>
<id>978-0641723445</id>
<id>978-1423103349</id>
</delete>
削除する文書クエリで指定して削除
<delete>
<query>name:ipod</query>
<query>cat:book price:[0 TO 100]</query>
</delete>
<autoCommit>
<maxDocs>10000</maxDocs>
<maxTime>30000</maxTime>
</autoCommit>
パラメーター名 | 説明 | デフォルト |
---|---|---|
hl | hl=onとしてハイライトを使うことを宣言します。 | hl=off |
hl.method | original, unified, fastVectorの中からハイライト方式を指定します。 | hl.method=original |
hl.fl | ハイライト対象フィールドをカンマ区切りで指定します。ハイライトするには、stored=trueでなければいけません。 | なし |
hl.snippets | 返却して欲しいハイライトスニペット最大数を指定します。 | hl.snippets=1 |
hl.fragsize | 各スニペットのおよその文字数を指定します。 | hl.fragsize=100 |
hl.requireFieldMatch | trueのとき、検索時に指定されたフィールドのみハイライトします。 | hl.requireFieldMatch=false |
hl.usePhraseHighlighter | trueのとき、フレーズ単位でハイライトを行います。 | hl.usePhraseHighlighter=true |
hl.highlightMultiTerm | ワイルドカード検索やあいまい検索でもハイライトを行うかどうかを指定します。 | hl.highlightMultiTerm=true |