2018年3月6日 星期二

使用 Jest 搜尋 Elasticsearch

Elasticsearch 官方本來是有提供官方的 SDK 可以用來操作 Elasticsearch
不過…官方版的 SDK 在存取 Elasticsearch 時,是把自己當作 Elasticsearch Cluster 的一員來看待
因此會有包括 heartbeat 等行為在 SDK 裡自動發生。
理論上這麼做的好處是,SDK 可以看見整個 Cluster 的狀態,因此可以比較妥善地選擇存取的節點
不過代價就是維持 Elasticsearch 連線所需要的資源更多、連線速度感覺好像變慢
此外,對於跑在 AWS Lambda 的程式來說,有不少潛在的缺點。
在 Elasticsearch 官方的描述 [1] 中,有建議了可以考慮使用 Jest [2],因此就來試試它了。

Maven 設定

Jest 目前的文件,其實有點看不太出來該怎麼依據使用的 Elasticsearch 版本做選擇
我自己環境的 Elasticsearch 是 5.4 版,現在是使用 2.4.0 版的 Jest。

Dependency 設定如下

<dependency>
	<groupId>io.searchbox</groupId>
	<artifactId>jest</artifactId>
	<version>2.4.0</version>
</dependency>
<dependency>
	<groupId>org.elasticsearch</groupId>
	<artifactId>elasticsearch</artifactId>
	<version>5.4.3</version>
	<exclusions>
		<exclusion>
			<groupId>org.elasticsearch.plugin</groupId>
			<artifactId>*</artifactId>
		</exclusion>
	</exclusions>
</dependency>

其中,這裡有設定了兩個 dependency,一個是 Jest,另一個是 Elasticsearch 原生的 SDK
至於為什麼要這麼做,主要是在於想要存取 Elasticsearch 時,想要使用 SDK 裡提供的 QueryBuilders
這樣程式碼寫起來會比直接寫純文字來得易讀。

建立連線

建立連線的部份,如果想要做些細部的設定,可以從 HttpClientConfig 這個地方著手。

List addressList = new LinkedList<>();
addressList.add("http://xxx.xxx.xxx.xxx:9200");
Gson gson = new Gson();

HttpClientConfig config = new HttpClientConfig.Builder(addressList)
    .multiThreaded(true)
    .maxTotalConnection(10)
    .gson(gson)
    .build();

JestClientFactory factory = new JestClientFactory();
factory.setHttpClientConfig(config);
JestClient client = factory.getObject();

第一行的 addressList 就是存取 Elasticsearch 的路徑,這裡需要是完整的 URL。
另外 Jest 查回來的東西,可以使用 GSON 自動做序列化,因此 Jest 內部需要一個 GSON 的實體
可以自己指定一個自己已經在使用的實體,或者不指定,Jest 也會自己產生一個。

查詢 Elasticsearch

要查詢 Elasticsearch 時,前面有提到了,我想要用 SDK 提供的 QueryBuilders
因此查詢式的組成,這裡會用 SDK 提供的 QueryBuilders 和 SearchSourceBuilder 來建立。

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
        .query(QueryBuilders.termQuery("field", "value"));

Search searchQuery = new Search.Builder(sourceBuilder.toString())
        .addIndex("myindex")
        .build();

SearchResult result = client.execute(sourceBuilder.toString());

這裡使用的是 Term Query,查詢的 index 是 myindex。然後搜尋標的是欄位 field 的值是 value 的資料。

取得結果並且序列化

前面也有提到,Jest 預設會使用 GSON 做序列化,因此假設我已經定義了一個 class MyObject 用來表達 Elasticsearch 上的資料
那麼在 Jest 的搜尋結果中,就可以直接指定類別,讓它將查詢結果序列化。

List<MyObject> objects = result.getHits(MyObject.class).stream()
      .map(obj -> obj.source)
      .collect(Collectors.toList());

這裡同時使用了 Lambda Expression,只收集查詢結果而略過其他 Elasticsearch 的回覆(例如 score 等等)。

參考資料
  1. Java Clients for Elasticsearch
  2. Jest

沒有留言: