検索エンジン? として、Solr か Elasticsearch かで 迷ったあげく、
Elasticsearch を MacOS el capitan にインストールして、
java クライアントで検索できるように実装するまでの実施したことを記載します。
インストールする目的
Solr とどちらを使うか迷いましたが、
Postgress に登録してあるデータ(Fes情報)を logstash で Elasticsearch に登録し、
Kuromoji で わかち書きのindex 作成。
その結果を画面から検索できることを目的にインストールしました。
参考サイト
-
Elasticsearch インストール
-
Kuromoij plugin インストール
-
Logstash の インストール
-
Java クライアント の 実装とインストール
前提
以下、OS、Java、Postgress の Version について記載します。
-
OS
sw_vers ---------------------------- ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G1217 ----------------------------
-
Java
java -version ---------------------------- java version "1.8.0_45" Java(TM) SE Runtime Environment (build 1.8.0_45-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode) ----------------------------
-
Postgress
psql --version ---------------------------- psql (PostgreSQL) 9.5.2 ----------------------------
実施したこと
実施したことは以下になります。
version は全て 5.2.2 がインストールされました。
Elasticsearchのインストール
brew install elasticsearch
brew install elasticsearch
-------------------------------
🍺 /usr/local/Cellar/elasticsearch/5.2.2: 98 files, 35.8MB, built in 6 minutes 58 seconds
-------------------------------
PATH 設定
何度のcommand を実行するので、elasticsearch の command をPATHに設定します。
vi ~/.bash_profile
export ELASTICPATH=/usr/local/opt/elasticsearch/libexec/bin/
export PATH=$PATH:$ELASTICPATH
plugin コマンドについて
command の名称は elasticsearch-plugin
等
接頭部にelasticsearch
が付与されています。
command の名称が 5.0.0
から変わったようです。
elasticsearch
elasticsearch-plugin
elasticsearch-systemd-pre-exec
elasticsearch-translog
elasticsearch.in.sh
[補足]自動起動設定
elasticsearch の 起動、停止を繰り返していたので、
実施していませんが、以下コマンドで自動起動設定、自動起動設定後の、起動、停止が可能です。
# 自動起動設定
ln -sfv /usr/local/Cellar/elasticsearch/5.2.2/*.plist ~/Library/LaunchAgents
# 起動
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
# 停止
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
Kuromojiプラグインのインストール
install
elasticsearch-plugin install analysis-kuromoji
---------------------------
-> Downloading analysis-kuromoji from elastic
[=================================================] 100%
-> Installed analysis-kuromoji
---------------------------
elasticsearch 再起動
Ctrl + C で elasticsearch を停止 再度コマンド実行で起動して、pluginを認識させます。1
[1] 再起動しなくても認識されるかもしれません。
elasticsearch
plugin が認識されていることを確認
-
curl
curl 'localhost:9200/_cat/plugins?v' ------------------------------------- name component version JlcSImk analysis-kuromoji 5.2.2 -------------------------------------
-
elasticsearch-plugin
elasticsearch-plugin list ------------------------------------- analysis-kuromoji -------------------------------------
kuromoji を使用するindex を作成。
5.0.0
からelasticsearch.yml
で index の 設定ができなくなりました。
kuromoji_tokenizer をdefault の analyzer として使用するindex を作成します。
index 名 festival
はこの後、logstash を使ってデータを流し込む index 名と一致させています。
curl -XPUT 'http://localhost:9200/festival' -d '
{
"index":{
"analysis":{
"tokenizer" : {
"kuromoji" : {
"type":"kuromoji_tokenizer",
"mode":"search"
}
},
"analyzer" : {
"default" : {
"type" : "custom",
"tokenizer" : "kuromoji_tokenizer"
}
}
}
}
}'
default の Index 設定が効いているかを確認する
curl -XGET "http://localhost:9200/festival/_analyze?pretty" -d "ジャズストリート"
---------------------------------------------------
{
"tokens" : [
{
"token" : "ジャズ",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 0
},
{
"token" : "ストリート",
"start_offset" : 3,
"end_offset" : 8,
"type" : "word",
"position" : 1
}
]
}
---------------------------------------------------
補足.設定ファイル編集
5.2.2 は、elasticsearch.yml
でdefault tokenizer
の設定はできなくなっています。
elasticsearch.yml
に default の analyzner を設定すると、
以下のエラーメッセージが出力されます。
-
yml ファイル編集
cd /usr/local/etc/elasticsearch vi elasticsearch.yml --------------------------------------- ########################################## # set kuromoji as default Tokenizer ########################################## index.analysis.analyzer.default.type: custom index.analysis.analyzer.default.tokenizer: kuromoji_tokenizer ---------------------------------------
-
エラーメッセージ
************************************************************************************* Found index level settings on node level configuration. Since elasticsearch 5.x index level settings can NOT be set on the nodes configuration like the elasticsearch.yaml, in system properties or command line arguments.In order to upgrade all indices the settings must be updated via the /${index}/_settings API. Unless all settings are dynamic all indices must be closed in order to apply the upgradeIndices created in the future should use index templates to set default values. Please ensure all required values are updated on all indices by executing: curl -XPUT 'http://localhost:9200/_all/_settings?preserve_existing=true' -d '{ "index.analysis.analyzer.default.tokenizer" : "kuromoji_tokenizer", "index.analysis.analyzer.default.type" : "custom" }' *************************************************************************************
Logstashのインストール
brew install logstash
brew install logstash
----------------------------
==> Caveats
Please read the getting started guide located at:
https://www.elastic.co/guide/en/logstash/current/getting-started-with-logstash.html
==> Summary
🍺 /usr/local/Cellar/logstash/5.2.2: 10,588 files, 167.1MB, built in 21 minutes 51 seconds
----------------------------
PATH の設定
vi ~/.bash_profile
export LOGSTASHPATH=/usr/local/Cellar/logstash/5.2.2/bin/
export PATH=$PATH:$LOGSTASHPATH
logstash-input-jdbc のインストール
logstash-plugin install logstash-input-jdbc
---------------------
Validating logstash-input-jdbc
Installing logstash-input-jdbc
Installation successful
---------------------
logstash.conf の作成
以下のような、logstash.conf を作成しました。
input {
jdbc {
jdbc_driver_library => "{YOUR_POSTGRESS_JDBC_PATH}"
jdbc_driver_class => "org.postgresql.Driver"
jdbc_connection_string => "jdbc:postgresql://localhost:5432/{YOUR_DATABASE_NAME}"
jdbc_user => "{YOUR_JDBC_USER}"
jdbc_password => "{YOUR_JDBC_PASSWORD}"
schedule => "* * * * *"
statement => "SELECT id,name,site_url,description,create_date,update_date,held_year_id,end_date,start_date,encode(thumbalizr,'base64') as thumbalizr from festival"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "festival"
doc_as_upsert => true
document_id => "%{id}"
action => "update"
}
stdout { codec => rubydebug }
}
logstash.conf の記載内容についての説明
-
jdbc_driver_library jdbc_driver_library は、jdbc_driver の配置先のパスを記載します。相対パスでも記載できそうですが、
どこからの相対パスかが、わからなかったので、絶対パス記載で動作させました。 -
statement
encode(thumbalizr,'base64')
で blog を base64エンコードしてElasticsearch に 流し込んでいます。
Binary datatype | Elasticsearch Reference [5.2] | Elastic
を見る限り、Blob 値直接は設定できないと判断しました。 -
doc_as_upsert, document_id, action
最初、指定なしでデータを登録したところ、重複データが大量にできてしまったので、doc_as_upsert
document_id
action
の指定で、
DB側のキー値を明示して、upsert で登録するようにしました。2
[2]action
の指定は不要かもしれません。
設定ファイルを読み込み、logstash 実行
logstash -f scripts/logstash.conf
Javaクライアントのインストールと、実装
上記の設定で、データの流し込みまで実施できたので、
アプリケーションの検索で使用するクライアントを実装します。
調べた限りだと、REST client や、 Transport client があるようです。
今回は、Transport client で実装しました。
pom.xml に dependency を追加
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.2.2</version>
</dependency>
設定情報を確認する
Transport client では、cluster.name
や node.name
を設定する必要があります。3
[3] addTransportAddress
で ipアドレス設定すれば動くかと思いきや、自環境では動きませんでした。
以下 curl コマンドで、cluster名 と、 node名 が取得できます。
-
cluster名を取得
curl 'localhost:9200/_cat/health?v' --------------------------------------------------------- epoch timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent 1490023819 00:30:19 elasticsearch_clustername yellow 1 1 5 5 0 0 5 0 - 50.0% ---------------------------------------------------------
-
node名を取得
curl 'localhost:9200/_cat/nodes?v' ---------------------------------------------------------- ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 10.0.1.4 50 100 15 2.47 mdi * JlcSImk ----------------------------------------------------------
クライアントの実装
以下のようにメソッドは実装しました。keyword を引数に、match検索を行います。
/**
* get
*
* @param keyword
* @return
*/
public List<Festival> get(String keyword) {
TransportClient client = null;
try {
client = new PreBuiltTransportClient(Settings.builder()
.put("cluster.name", "elasticsearch_clustername").put("client.transport.sniff", false)
.put("node.name", "JlcSImk")
.put("client.transport.ping_timeout", 20, TimeUnit.SECONDS).build())
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("10.0.1.4"), 9300));
} catch (UnknownHostException e) {
e.printStackTrace();
}
Map<String, Object> template_params = new HashMap<>();
template_params.put("keyword", keyword);
SearchResponse sr = new SearchTemplateRequestBuilder(client)
.setScript("{\n" +
" \"query\" : {\n" +
" \"match\" : {\n" +
" \"name\" : \"{{keyword}}\"\n" +
" }\n" +
" }\n" +
"}")
.setScriptType(ScriptType.INLINE)
.setScriptParams(template_params)
.setRequest(new SearchRequest())
.get()
.getResponse();
SearchHit[] results = sr.getHits().getHits();
List<Festival> festivals = new ArrayList<>();
for (SearchHit hit : results) {
String sourceAsString = hit.getSourceAsString();
if (sourceAsString != null) {
ObjectMapper mapper = new ObjectMapper();
Festival fes = null;
try {
fes = mapper.readValue(sourceAsString, Festival.class);
} catch (IOException e) {
e.printStackTrace();
}
festivals.add(fes);
}
}
client.close();
return festivals;
}
クライアントの実装の説明
-
PreBuiltTransportClient
PreBuiltTransportClient
の引数にBuilderを設定します。ポート番号はデフォルトだと9300
で、HTTPとは別でポートが空いています。 -
SearchTemplateRequestBuilder
String 文字列直接でクエリの生成もできますが、こちら を参考に
SearchTemplateRequestBuilder を使用する形で実装しました。 -
戻り値の変換
Jackson のObjectMapper
を使って戻りのJSON文字列をJAVAクラスに変換しました。
curl コマンドで Elasticsearch からは、以下の戻りが返ってきてましたので、
curl -XGET localhost:9200/festival/_search?pretty -d '{"query":{"match":{"name":"高槻"}}}}' ------------------------------------------------- { "took" : 124, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 4.3933816, "hits" : [ { "_index" : "festival", "_type" : "logs", "_id" : "270", "_score" : 4.3933816, "_source" : { "end_date" : null, "@timestamp" : "2017-03-20T15:45:00.276Z", "site_url" : "http://www.0726.info/", "held_year_id" : 8, "name" : "高槻ジャズストリート", "@version" : "1", "description" : "", "thumbalizr" : null, "id" : 270, "create_date" : "2016-12-11T14:38:59.740Z", "update_date" : "2016-12-11T14:38:59.740Z", "start_date" : null } } ] } } -------------------------------------------------
_source
の部分をINPUT に jsonschema2pojo でJAVAのクラスを生成し、JSON文字列から
マッピングしています。
TODO
上記の実装で、JAVAから Elasticsearch の 検索結果の取得ができるようになりました。
ローカルで実行している感じだと、接続オブジェクトの時間がかかっているようで、
結構もっさりしています。ALL IN ONE で DBサーバーも、APサーバーも一緒くただと、
RDB のほうが早かったりしそうです。
接続オブジェクトCache とか、 NodeClient を使う等このあたりは改善できるのかもしれないので、
後日実施してみようかと思います。
以上です。
コメント