前回の記事で、Apache Solrを使って、登録・削除・検索を紹介しました。
今回は、検索結果をハイライト表示する方法を紹介します。
Apache Solrでのハイライト表示とは、検索結果として一致したキーワード前後の文字列を通知して、かつ、検索したキーワード部分を強調表示する機能になります。
本記事では、Apache Solrで最も一般的なハイライト表示である「Standard Hilighter」を使ってみます。
環境情報
環境情報は以下になります。
使用するのは、JavaとApache Solrの2つのみです。
- Java 12.0.1
- Apache Solr 6.4.2
スキーマ定義
ハイライト表示するためには、ハイライト表示するための準備が必要です。
Apache Solrのコンフィグである「managed-schema」に、フィールドタイプ定義とフィールド定義をおこないます。
フィールドタイプ定義
<fieldType name="text_general_content" class="solr.TextField" positionIncrementGap="100" multiValued="true" autoGeneratePhraseQueries="true"> <analyzer type="index"> <tokenizer class="solr.NGramTokenizerFactory" minGramSize="1" maxGramSize="1"/> <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.NGramTokenizerFactory" minGramSize="1" maxGramSize="1"/> <filter class="solr.StopFilterFactory" words="stopwords.txt" ignoreCase="true"/> <filter class="solr.SynonymFilterFactory" expand="true" ignoreCase="true" synonyms="synonyms.txt"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
フィールド定義
<field name="content" type="text_general_content" multiValued="false" indexed="true" stored="true"/>
「managed-schema」の定義を追加したら、ApacheSolrのサービスを再起動してください。
これで準備完了です。
以降に登録したドキュメントについて、ハイライト表示の文字列取得が可能になります。
Standard Highlighterのサンプルコード
「Standard Highlighter」を使った検索のサンプルコードは以下になります。
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.util.ClientUtils; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.Group; import org.apache.solr.client.solrj.response.GroupCommand; import org.apache.solr.client.solrj.response.GroupResponse; import org.apache.solr.common.util.ContentStreamBase; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrDocument; public class SolrSearch { public static void main(String[] args){ System.out.print("start: main\r\n"); // Solrのインスタンス作成 SolrClient client = new HttpSolrClient.Builder( "http://localhost:8983/solr/java_sample").build(); SolrQuery solrQuery = new SolrQuery(); // 検索結果として、文書IDを返却するよう設定 solrQuery.setFields("id"); // 検索結果の上限は100件 solrQuery.setRows(100); try { StringBuilder queryString = new StringBuilder(); String keyword = args[0]; if (keyword.equals("")) { queryString.append("*"); } else { String queryPhrase = "\"" + ClientUtils.escapeQueryChars(keyword) + "\""; queryString.append("("); queryString.append("content:"); queryString.append(queryPhrase); queryString.append(")"); } // 検索実行 System.out.println("q=" + queryString.toString()); solrQuery.set("hl", true); solrQuery.set("hl.fl", "content"); solrQuery.set("hl.simple.pre", "<b>"); solrQuery.set("hl.simple.post", "</b>"); solrQuery.set("hl.fragsize", 20); solrQuery.set("hl.maxAnalyzedChars", 100); solrQuery.set("q", queryString.toString()); QueryResponse response = client.query(solrQuery); // 検索結果を表示 SolrDocumentList list = response.getResults(); if (list == null) { System.out.println("文書は存在しませんでした。"); } else { System.out.println(list.getNumFound() + "件ヒットしました。"); // ハイライト情報を取得して加工 Map<String,Map<String,List<String>>> highlighting = response.getHighlighting(); for (SolrDocument doc : list) { String id = (String) doc.getFieldValue("id"); Map<String, List<String>> map = highlighting.get(id); List<String> contentList = map.get("content"); for(String val : contentList) { val = val.replaceAll("[\r\n\t]", ""); System.out.println("val=" + val); } } } } catch (SolrServerException e) { System.out.print("SolrServerException Occured!\r\n"); e.printStackTrace(); } catch (IOException e) { System.out.print("IOException Occured!\r\n"); e.printStackTrace(); } finally { try { // コミットして、コネクションをクローズ client.commit(); client.close(); } catch (Exception e) { System.out.print("Exception Occured!\r\n"); e.printStackTrace(); } } System.out.print("end: main\r\n"); } }
「Standard Highlighter」を使用するためのパラメータ設定は57~63行目です。
検索結果をハイライト文字列として取得しているのが76~83行目。
ハイライト文字をWEB画面上に出力することを想定して、改行とタブを削除しています。
以下のようなテキストファイルをApacheSolrに登録して、検索を試してみます。
主食と野菜をしっかり食べさせよう
このテキストファイルが登録されている状態で『野菜』というキーワードで検索してみます。
すると、以下のような文字列がハイライト文字としてApacheSolrから返却されてきます。
主食と<b>野</b><b>菜</b>をしっかり食べさせよう
これをWEBページに出力すると以下のような表示になります。
主食と野菜をしっかり食べさせよう検索してヒットした「野菜」という文字が強調表示されています。
これが、ハイライト表示の基本的な動きになります。
Standard Highlighter のパラメータ
サンプルプログラムで、Standard Highlighter を使用した検索をおこない、ハイライト文字を取得できます。
このハイライト表示はパラメータでチューニング可能です。
具体的には以下の部分。
solrQuery.set("hl", true); solrQuery.set("hl.fl", "content"); solrQuery.set("hl.simple.pre", "<b>"); solrQuery.set("hl.simple.post", "</b>"); solrQuery.set("hl.fragsize", 20); solrQuery.set("hl.maxAnalyzedChars", 100);
「Standard Highlighter」を使用するには、上記のパラメータを設定するだけでOKです。
各パラメータの意味合いは以下になります。
パラメータ |
デフォルト |
説明 |
hl |
blank |
このパラメータを「true」にする、ハイライトがオンになります。 デフォルトはブランク(オフ)です。 |
hl.fl |
blank |
ハイライトの対象となるフィールド。 複数定義する場合は、カンマ区切りで指定します。 |
hl.simple.pre |
<em> |
ハイライト表示する文字の前に挿入する文字。 |
hl.simple.post |
</em> |
ハイライト表示する文字の後に挿入する文字。 |
hl.fragsize |
100 |
スニペットひとつあたりの最大文字数。 |
hl.maxAnalyzedChars |
51200 |
スニペットの処理対象最大文字数。 |
スニペットとは、”検索キーワードを含む文書”です。
今回の例では「hl.fragsize」は20としているので、”検索キーワードを含む文書”は最大20文字です。
例えば、下のような150文字の文字列があるとします。
ああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ主食と野菜をしっかり食べさせよういいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいいい
この文字に対して「野菜」を検索キーワードとして指定します。
その際の返却文字列は以下となります。20文字です。
ああああ主食と野菜をしっかり食べさせよう「hl.fragsize」は20なので、返却される文字列は最大20文字、ということですね。
まとめ
いかがでしたでしょうか?
「Standard Highlighter」の使い方について、ある程度わかって頂けたかと思います。
ハイライト表示は、「Standard Highlighter」以外に以下の2つがあります。
- FastVector Highlighter
- Postings Highlighter
次回は、「FastVector Highlighter」を使ったハイライト表示を紹介したいと思います。
それではまた!