はじめに
- Solrを使ってPDFなどのドキュメントをインデクシングする要件があるとのことで、手順についてまとめておきます。
- ドキュメントのインデクシングではApacheTikaを取り込んで実装されたSolrCellを利用しています。
- ApacheTikaは節操ないくらい色々なものがパースできます。具体的にはPDFだけではなく、Word、Excel、HTML、画像、動画、圧縮されたファイル、クラスファイルなど色々なものがサポートされています。詳細についてはこちらを読んでみてください。
ドキュメントを読み込む前の設定
SolrConfig.xml
- SolrConfig.xmlでsolr.extraction.ExtractingRequestHandlerが有効になっていることを確認します。
- uprefixとは、取得できたフィールド名の中でschema.xmlのフィールド定義にないものの先頭に付与する文字列を表しています。(後述するschema.xmlの定義と関連があります)
- fmap.xxxとはxxxというフィールド名をschema.xmlで定義されたフィールドの何にマッピングするかを定義しています。
- この場合、contentはdoctextというフィールド名にマッピングしています。
SolrConfig.xml
<requestHandler name="/update/extract"
startup="lazy"
class="solr.extraction.ExtractingRequestHandler" >
<lst name="defaults">
<str name="lowernames">true</str>
<str name="uprefix">ignored_</str>
<!-- capture link hrefs but ignore div attributes -->
<str name="captureAttr">true</str>
<str name="fmap.a">links</str>
<str name="fmap.content">doctext</str>
<str name="fmap.div">ignored_</str>
</lst>
</requestHandler>
schema.xml
- solrconfig.xmlで必要となったignored_をダイナミックフィールドとして定義しています。
- ignored_フィールドは、インデクシングしたくないので、fieldtypeで保存もインデクシングもしないように定義しておきます。
- 全文検索されたフィールドはdoctextに入れます。
schema.xml
<dynamicField name="ignored_*" type="ignored" multiValued="true"/>
<field name="doctext" type="text_ja" indexed="false" stored="true" multiValued="true"/>
・・・
<fieldtype name="ignored" stored="false" indexed="false" multiValued="true" class="solr.StrField" /
- 余談ですがドキュメントではない他のフィールドと合わせて全文検索させたい場合は、copyFieldの設定なども合わせてしておきます。
<copyField source="doctext" dest="text"/>
SolrJで取り込んでみます。
- Solrのリファレンスガイドを取り込んでみます。
- なんてことはないコードなので説明は省きます。
pom.xml
<dependencies>
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>4.10.3</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
Pdf.java
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
import org.apache.solr.common.util.NamedList;
public class Pdf {
public static void main(String... args) throws Exception {
final SolrServer solr = new HttpSolrServer(
"http://10.0.0.172:8983/solr/dora");
final ContentStreamUpdateRequest update = new ContentStreamUpdateRequest(
"/update/extract");
update.addFile(new File(
"/Users/yuzuru/Documents/work/apache-solr-ref-guide-4.10.pdf"),
"application/pdf");
update.setParam("literal.id", "apache-solr-ref-guide-4.10.pdf");
update.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
NamedList<Object> named = solr.request(update);
System.out.println("finish" + named);
}
}
その他抑えておきたいこと
- curlで実行できるオプションはSolrJでももちろん指定できます。
解析だけさせることができます。
- parameterにextractOnly=trueを付与します。
update.setParam("extractOnly", "true");
- するとこんな感じで解析した結果が取れます。
<p/>
</div>
<div class="page">
<p/>
<p>483Apache Solr Reference Guide 4.10
</p>
<p><requestHandler name="/replication" class="solr.ReplicationHandler" >
<lst name="slave">
</p>
以下略
解析した結果とれるフィールド名を知りたい。
contentというフィールドは確実に入ってきますが、それ以外に取れるフィールドを知りたいですよね。下記のようにインデクシングする必要はありませんが、勉強がてらインデクシングしてみます。
1.ダイナミックフィールドs_*を指定しておきます。
<dynamicField name="s_*" type="string" indexed="true" stored="true" multiValued="true"/>
2.uprefixにs_を指定して実行します。
update.setParam("uprefix", "s_");
- solrのqueryを実行してみると色々取得できていることがわかります。
"id": "apache-solr-ref-guide-4.10.pdf",
"s_meta_creation_date": [
"2014-09-02T21:24:47Z"
],
"s_dcterms_modified": [
"2014-09-02T21:24:47Z"
],
"s_meta_save_date": [
"2014-09-02T21:24:47Z"
],
"s_dcterms_created": [
"2014-09-02T21:24:47Z"
],
"s_last_modified": [
"2014-09-02T21:24:47Z"
],
"s_date": [
"2014-09-02T21:24:47Z"
],
"s_modified": [
"2014-09-02T21:24:47Z"
],
"s_xmptpg_npages": [
"511"
],
"s_creation_date": [
"2014-09-02T21:24:47Z"
],