Solr5.3+Postgresの連携方法

最近、[Apache Solr](http://lucene.apache.org/solr/)を触る機会があったので、個人的にもいじってみています。

PostgresのデータベースのコンテンツをSolrに流し込む必要があったのですが、Solr5でのPostgresとの連携については日本語文献が見当たらなかったので、やってみたメモを残しておきます。
Solr5になってから設定ファイル周りが結構変わっており、少し手間取りました。。

# 1. 環境
- CentOS 6.6 (32bit)
- Oracle JDK 8 Update 65
- Apache Solr 5.3.1
- postgresql-server 8.4.20-4.el6_7

# 2. Solrのインストール
[Apacheのページ](http://lucene.apache.org/solr/mirrors-solr-latest-redir.html)からSolrのバイナリを環境に持っていきます。
このとき、ソースコードも一緒にダウンロードしておくと、エラーが出て詰まったときにサクっと調べられます。(解決できるかどうかは別問題だけど)

バイナリを展開。

使ってみる。
[Solr Quick Start](http://lucene.apache.org/solr/quickstart.html)で案内されている方法だと、クラスタ構成(SolrCloud)で起動されます。

```
$ bin/solr start -e cloud -noprompt

Welcome to the SolrCloud example!

  --- (snip) ---
```

停止。

```
$ bin/solr stop -all ; rm -Rf example/cloud/
```

今回はクラスタ構成でなくシンプルに立ち上げます。

# 3. Postgresのインストール

[Solr + PostgreSQL連携(Solr4.9)](http://open-groove.net/solr/solr4-postgresql/)で紹介されている方法をそのまま使わせていただきました。

Postgresをインストールします。

```
# yum install postgresql-server
```

Postgresを立ち上げます。

```
$ sudo service postgresql initdb
[sudo] password for hashiwa:
データベースを初期化中:                                    [  OK  ]

$ sudo service postgresql start
postgresql サービスを開始中:                               [  OK  ]
```

Solrから接続するユーザーを作成します。(スーパーユーザーじゃないほうが良かったかも)

```
$ sudo su - postgres
-bash-4.1$ createuser solruser
新しいロールをスーパーユーザとしますか?  (y/n)y
-bash-4.1$ createdb postdb -O solruser
```

パスワードを設定します。

```
-bash-4.1$ psql
psql (8.4.20)
"help" でヘルプを表示します.

postgres=# alter user solruser encrypted password 'solrpassword';
ALTER ROLE
postgres=# alter user postgres encrypted password 'postgrespassword';
ALTER ROLE
postgres-# \q
```

認証METHODをmd5に変更します。

```
$ vim /var/lib/pgsql/data/pg_hba.conf

 67 # TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
 68
 69 # "local" is for Unix domain socket connections only
 70 local   all         all                               md5
 71 # IPv4 local connections:
 72 host    all         all         127.0.0.1/32          md5
 73 # IPv6 local connections:
 74 host    all         all         ::1/128               md5
```

Postgresサービスを再起動します。

```
# service postgresql restart
postgresql サービスを停止中:                               [  OK  ]
postgresql サービスを開始中:                               [  OK  ]
```

forumテーブルを作成します。

```
$ psql -U solruser -d postdb
ユーザ solruser のパスワード:
psql (8.4.20)
"help" でヘルプを表示します.

postdb=# create table forum (
postdb(# id serial primary key,
postdb(# name varchar(32),
postdb(# body varchar(1024),
postdb(# updated timestamp(0) not null default current_timestamp);
NOTICE:  CREATE TABLEはシリアル列"forum.id"用に暗黙的なシーケンス"forum_id_seq"を作成します。
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"forum"に暗黙的なインデックス"forum_pkey"を作成します
CREATE TABLE
```

差分インポート用の関数とトリガーを定義します。

```
postdb=# CREATE LANGUAGE plpgsql;
CREATE LANGUAGE

postdb=# create function set_update_time() returns opaque as '
postdb'#   begin
postdb'#     new.updated := ''now'';
postdb'#     return new;
postdb'#   end;
postdb'# ' language 'plpgsql';
CREATE FUNCTION

postdb=# create trigger update_tri before update on forum for each row
postdb=#   execute procedure set_update_time();
```

このとき、テーブルは以下の状態です。

```
postdb=# \d forum
                                     テーブル "public.forum"
 カラム  |               型               |                        修飾語
---------+--------------------------------+-------------------------------------------------------
 id      | integer                        | not null デフォルト nextval('forum_id_seq'::regclass)
 name    | character varying(32)          |
 body    | character varying(1024)        |
 updated | timestamp(0) without time zone | not null デフォルト now()
インデックス:
    "forum_pkey" PRIMARY KEY, btree (id)
トリガ:
    update_tri BEFORE UPDATE ON forum FOR EACH ROW EXECUTE PROCEDURE set_update_time()
```

Solrから検索されるデータを3つ挿入しておきます。

```
postdb=# insert into forum (name, body) values ('Bobbie Waterbury', 'Daddy!My Daddy!');
INSERT 0 1
postdb=# insert into forum (name, body) values ('James Bond', 'Do I look like I give a damn?');
INSERT 0 1
postdb=# insert into forum (name, body) values ('Andy Dufresne', 'Get busy living, or get busy dying.');
INSERT 0 1
```

データは以下のように挿入されました。

```
$ psql -h localhost -U solruser -d postdb -t -c 'select * from forum;';
ユーザ solruser のパスワード:
  1 | Bobbie Waterbury | Daddy!My Daddy!                     | 2015-12-26 18:02:10
  2 | James Bond       | Do I look like I give a damn?       | 2015-12-26 18:02:18
  3 | Andy Dufresne    | Get busy living, or get busy dying. | 2015-12-26 18:02:28
```

# 4. JDBCのダウンロード

Postgres用のJDBCをダウンロードしておきます。

```
$ curl https://jdbc.postgresql.org/download/postgresql-9.4.1207.jre6.jar > postgresql-9.4.1207.jre6.jar
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  593k  100  593k    0     0  93681      0  0:00:06  0:00:06 --:--:--  129k
```

# 5. Solrの設定
Solrの設定も、[Solr + PostgreSQL連携(Solr4.9)](http://open-groove.net/solr/solr4-postgresql/)をかなり参考にさせていただきました。ただ、Solr5になったことで注意が必要なところがあります。

念のため、サーバーディレクトリをコピーして、そちらを使用します。
サーバーディレクトリは、デフォルトだとserverディレクトリとなっているので、serverディレクトリを任意のディレクトリ名でコピーします。

```
$ cp -r server/ hsw_server
```

今回は、デフォルトで用意されているコレクションであるcollection1を使用します。[^1]
コピーしたサーバーディレクトリの、collection1のsolrconfig.xmlを編集します。

ライブラリの設定。

```xml:hsw_server/solr/collection1/conf/solrconfig.xml
  <lib dir="/home/hashiwa/solr/solr-5.3.1-bin/hsw_server/lib/" regex="postgresql-\d.*\.jar" />
  <lib dir="/home/hashiwa/solr/solr-5.3.1-bin/dist/" regex="solr-dataimporthandler-\d.*\.jar" />
  <lib dir="/home/hashiwa/solr/solr-5.3.1-bin/contrib/dataimporthandler-extras/lib/" regex=".*\.jar" />
```

また、Solr5ではデフォルトではスキーマレスモードになっており、schema.xmlでスキーマを指定できないようです。

[RESTでスキーマを追加することもできる](http://blog.johtani.info/blog/2013/07/04/schemaless-example/)ようですが、スキルがないのでクラシックなschema.xmlでの指定を使うことにします。

SchemaFactoryの変更。

```xml:hsw_server/solr/collection1/conf/solrconfig.xml
  <!--
  <schemaFactory class="ManagedIndexSchemaFactory">
    <bool name="mutable">true</bool>
    <str name="managedSchemaResourceName">managed-schema</str>
  </schemaFactory>
  -->
  <schemaFactory class="ClassicIndexSchemaFactory"/>
```

AddSchemaFieldsUpdateProcessorFactoryの設定の削除。

```xml:hsw_server/solr/collection1/conf/solrconfig.xml
    <!--
    <processor class="solr.AddSchemaFieldsUpdateProcessorFactory">
      <str name="defaultFieldType">strings</str>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Boolean</str>
        <str name="fieldType">booleans</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.util.Date</str>
        <str name="fieldType">tdates</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Long</str>
        <str name="valueClass">java.lang.Integer</str>
        <str name="fieldType">tlongs</str>
      </lst>
      <lst name="typeMapping">
        <str name="valueClass">java.lang.Number</str>
        <str name="fieldType">tdoubles</str>
      </lst>
    </processor>
    -->
```

リクエストハンドラの設定。
これはData Import Handlerという機能を使うための設定で、設定自体はdataconfig.xmlに記載します。

```xml:hsw_server/solr/collection1/conf/solrconfig.xml
  <requestHandler name="/dataimport-hswtest" class="org.apache.solr.handler.dataimport.DataImportHandler" >
    <lst name="defaults">
      <str name="config">dataconfig.xml</str>
    </lst>
  </requestHandler>
```

dataconfig.xmlの設定。
Postgresで設定したユーザーやらパスワードやらテーブルやらを設定します。

```xml:hsw_server/solr/collection1/conf/dataconfig.xml
<dataConfig>
  <dataSource name="ratings"
              driver="org.postgresql.Driver"
              url="jdbc:postgresql://localhost:5432/postdb"
              user="solruser"
              password="solrpassword" />

  <document>
    <entity name="forum"
            query="select * from forum"
            deltaQuery="select id from forum where updated >= '${dataimporter.last_index_time}'"
            deltaImportQuery="select * from forum where id=${dataimporter.delta.id}">
      <field column="id" name="id" />
      <field column="name" name="name" />
      <field column="body" name="body" />
      <field column="updated" name="updated" />
    </entity>
  </document>
</dataConfig>
```

また、忘れずにJDBCのjarもsolrconfig.xmlで指定した場所に置いておく必要があります。

```
$ cp postgresql-9.4.1207.jre6.jar /home/hashiwa/solr/solr-5.3.1-bin/hsw_server/lib/
```

schema.xmlを作成します。

```xml:hsw_server/solr/collection1/conf/schema.xml
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="hsw-example" version="1.5">
   <field name="id" type="string" indexed="true" stored="true" required="true" />
   <field name="name" type="text_en" indexed="true" stored="true" termVectors="true" termPositions="true" />
   <field name="body" type="text_en" indexed="true" stored="true" termVectors="true" termPositions="true" />
   <field name="updated" type="timestamp" indexed="true" stored="true" termVectors="true" termPositions="true" />
   <field name="_version_" type="long" indexed="true" stored="true"/>

   <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
   <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
   <fieldType name="timestamp" class="solr.TrieDateField" />
   <fieldType name="text_en" class="solr.TextField">
     <analyzer>
       <tokenizer class="solr.StandardTokenizerFactory"/>
       <filter class="solr.LowerCaseFilterFactory"/>
       <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_en.txt" />
     </analyzer>
   </fieldType>

   <uniqueKey>id</uniqueKey>
 <!-- depricated
   <defaultSearchField>body</defaultSearchField>
   <solrQueryParser defaultOperator="AND" />
 -->
</schema>
```

注意点：

- solr.LongFieldがなくなっているらしく、solr.TrieLongFieldにする必要がある
- idをlongにすると、elevate.xmlでid=MA147LL/Aとしているからかエラーになるため、stringにする

# 6. Solrの起動・データインポート

いよいよSolrを触っていきます。

Solrを起動。このとき、設定したサーバーディレクトリを指定する必要があります。

```
$ bin/solr start -d hsw_server/
Waiting up to 30 seconds to see Solr running on port 8983 [/]
Started Solr server on port 8983 (pid=20468). Happy searching!
```

Solrのコンソール( http://localhost:8983/solr/ )にアクセス。

![solr-01.jpg](https://qiita-image-store.s3.amazonaws.com/0/107431/accbe6d8-b616-5210-0008-7f3ad491e6a1.jpeg)

[Core Selector]からcollection1を選択し、[DataImport]をクリックします。

![solr-02.jpg](https://qiita-image-store.s3.amazonaws.com/0/107431/1b0ee970-8ce1-e9e8-5a83-f3160969e583.jpeg)

ここで、forumを選んでExecuteすると、solrconfig.xmlで設定したData Import Handlerを実行して、PostgresからデータをSolrにインポートします。
(いくら待っても画面が更新されない場合は、[Reflesh Status]を押してみる)

![solr-03.jpg](https://qiita-image-store.s3.amazonaws.com/0/107431/126e86bf-16ea-eea4-e2ed-e401b15b53fb.jpeg)

成功していたら、[Query]で検索してみます。
ちゃんとPostgresに入れた3つのデータが検索されました！

![solr-04.jpg](https://qiita-image-store.s3.amazonaws.com/0/107431/5e987b7d-fc82-fcf2-1ada-7a31f44f8a62.jpeg)


# 参考資料
- [Solr + PostgreSQL連携(Solr4.9)](http://open-groove.net/solr/solr4-postgresql/)
- [Apache Solr 5.3.0をインストールする](http://d.hatena.ne.jp/Kazuhira/20150905/1441462251)
- [Index a DB table directly into Solr](https://wiki.apache.org/solr/DIHQuickStart)

[^1]: 2015/12/30追記 collection1はデフォルトでは存在しなかったかも。なければ、Solrのコンソールなどで作成してください。