Elasticsearch Java Rest Client API 整理總結 (二) —— SearchAPI
目錄
引言
在上一篇 中主要介紹了 Document API,本節中講解 search API
Search APIs
Java High Level REST Client 支援下面的 Search API:
Search API
Search Request
searchRequest
用來完成和搜尋文件,聚合,建議等相關的任何操作同時也提供了各種方式來完成對查詢結果的高亮操作。
最基本的查詢操作如下
SearchRequest searchRequest = new SearchRequest(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); // 新增 match_all 查詢 searchRequest.source(searchSourceBuilder); // 將 SearchSourceBuilder 新增到 SeachRequest 中
可選引數
SearchRequest searchRequest = new SearchRequest("posts"); // 設定搜尋的 index
searchRequest.types("doc"); // 設定搜尋的 type
除了配置 index
和 type
外,還有一些其他的可選引數
searchRequest.routing("routing"); // 設定 routing 引數
searchRequest.preference("_local"); // 配置搜尋時偏愛使用本地分片,預設是使用隨機分片
什麼是 routing 引數?
當索引一個文件的時候,文件會被儲存在一個主分片上。在儲存時一般都會有多個主分片。Elasticsearch 如何知道一個文件應該放置在哪個分片呢?這個過程是根據下面的這個公式來決定的:
shard = hash(routing) % number_of_primary_shards
routing
是一個可變值,預設是文件的_id
,也可以設定成一個自定義的值number_of_primary_shards
是主分片數量
所有的文件 API 都接受一個叫做 routing
的路由引數,通過這個引數我們可以自定義文件到分片的對映。一個自定義的路由引數可以用來確保所有相關的文件——例如所有屬於同一個使用者的文件——都被儲存到同一個分片中。
使用 SearchSourceBuilder
對搜尋行為的配置可以使用 SearchSourceBuilder
來完成,來看一個例項
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); // 預設配置
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); // 設定搜尋,可以是任何型別的 QueryBuilder
sourceBuilder.from(0); // 起始 index
sourceBuilder.size(5); // 大小 size
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); // 設定搜尋的超時時間
設定完成後,就可以新增到 SearchRequest
中。
SearchRequest searchRequest = new SearchRequest();
searchRequest.source(sourceBuilder);
構建查詢條件
查詢請求是通過使用 QueryBuilder
物件來完成的,並且支援 Query DSL。
DSL (domain-specific language) 領域特定語言,是指專注於某個應用程式領域的計算機語言。
— 百度百科
可以使用建構函式來建立 QueryBuilder
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("user", "kimchy");
QueryBuilder
建立後,就可以呼叫方法來配置它的查詢選項:
matchQueryBuilder.fuzziness(Fuzziness.AUTO); // 模糊查詢
matchQueryBuilder.prefixLength(3); // 字首查詢的長度
matchQueryBuilder.maxExpansions(10); // max expansion 選項,用來控制模糊查詢
也可以使用QueryBuilders
工具類來建立 QueryBuilder
物件。這個類提供了函數語言程式設計風格的各種方法用來快速建立 QueryBuilder
物件。
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("user", "kimchy")
.fuzziness(Fuzziness.AUTO)
.prefixLength(3)
.maxExpansions(10);
fuzzy-matching 拼寫錯誤時的匹配:
好的全文檢索不應該是完全相同的限定邏輯,相反,可以擴大範圍來包括可能的匹配,從而根據相關性得分將更好的匹配放在前面。
例如,搜尋
quick brown fox
時會匹配一個包含fast brown foxes
的文件
不論什麼方式建立的 QueryBuilder
,最後都需要新增到 `SearchSourceBuilder
中
searchSourceBuilder.query(matchQueryBuilder);
構建查詢 文件中提供了一個豐富的查詢列表,裡面包含各種查詢對應的QueryBuilder
物件以及QueryBuilder
helper 方法,大家可以去參考。
關於構建查詢的內容會在下篇文章中講解,敬請期待。
指定排序
SearchSourceBuilder
允許新增一個或多個SortBuilder
例項。這裡包含 4 種特殊的實現, (Field-
, Score-
, GeoDistance-
和 ScriptSortBuilder
)
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); // 根據分數 _score 降序排列 (預設行為)
sourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC)); // 根據 id 降序排列
過濾資料來源
預設情況下,查詢請求會返回文件的內容 _source
,當然我們也可以配置它。例如,禁止對 _source
的獲取
sourceBuilder.fetchSource(false);
也可以使用萬用字元模式以更細的粒度包含或排除特定的欄位:
String[] includeFields = new String[] {"title", "user", "innerObject.*"};
String[] excludeFields = new String[] {"_type"};
sourceBuilder.fetchSource(includeFields, excludeFields);
高亮請求
可以通過在 SearchSourceBuilder
上設定 HighlightBuilder
完成對結果的高亮,而且可以配置不同的欄位具有不同的高亮行為。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
HighlightBuilder highlightBuilder = new HighlightBuilder();
HighlightBuilder.Field highlightTitle =
new HighlightBuilder.Field("title"); // title 欄位高亮
highlightTitle.highlighterType("unified"); // 配置高亮型別
highlightBuilder.field(highlightTitle); // 新增到 builder
HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user");
highlightBuilder.field(highlightUser);
searchSourceBuilder.highlighter(highlightBuilder);
聚合請求
要實現聚合請求分兩步
- 建立合適的 `
AggregationBuilder
- 作為引數配置在 `
SearchSourceBuilder
上
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
TermsAggregationBuilder aggregation = AggregationBuilders.terms("by_company")
.field("company.keyword");
aggregation.subAggregation(AggregationBuilders.avg("average_age")
.field("age"));
searchSourceBuilder.aggregation(aggregation);
建議請求 Requesting Suggestions
SuggestionBuilder
實現類是由 SuggestBuilders
工廠類來建立的。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
SuggestionBuilder termSuggestionBuilder =
SuggestBuilders.termSuggestion("user").text("kmichy");
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("suggest_user", termSuggestionBuilder);
searchSourceBuilder.suggest(suggestBuilder);
對請求和聚合分析
分析 API 可用來對一個特定的查詢操作中的請求和聚合進行分析,此時要將SearchSourceBuilder
的 profile標誌位設定為 true
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.profile(true);
只要 SearchRequest
執行完成,對應的 SearchResponse
響應中就會包含 分析結果
同步執行
同步執行是阻塞式的,只有結果返回後才能繼續執行。
SearchResponse searchResponse = client.search(searchRequest);
非同步執行
非同步執行使用的是 listener
對結果進行處理。
ActionListener<SearchResponse> listener = new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
// 查詢成功
}
@Override
public void onFailure(Exception e) {
// 查詢失敗
}
};
查詢響應 SearchResponse
查詢執行完成後,會返回 SearchResponse
物件,並在物件中包含查詢執行的細節和符合條件的文件集合。
歸納一下, SerchResponse
包含的資訊如下
- 請求本身的資訊,如 HTTP 狀態碼,執行時間,或者請求是否超時
RestStatus status = searchResponse.status(); // HTTP 狀態碼
TimeValue took = searchResponse.getTook(); // 查詢佔用的時間
Boolean terminatedEarly = searchResponse.isTerminatedEarly(); // 是否由於 SearchSourceBuilder 中設定 terminateAfter 而過早終止
boolean timedOut = searchResponse.isTimedOut(); // 是否超時
- 查詢影響的分片數量的統計資訊,成功和失敗的分片
int totalShards = searchResponse.getTotalShards();
int successfulShards = searchResponse.getSuccessfulShards();
int failedShards = searchResponse.getFailedShards();
for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
// failures should be handled here
}
檢索 SearchHits
要訪問返回的文件,首先要在響應中獲取其中的 SearchHits
SearchHits hits = searchResponse.getHits();
SearchHits
中包含了所有命中的全域性資訊,如查詢命中的數量或者最大分值:
long totalHits = hits.getTotalHits();
float maxScore = hits.getMaxScore();
查詢的結果巢狀在 SearchHits
中,可以通過遍歷迴圈獲取
SearchHit[] searchHits = hits.getHits();
for (SearchHit hit : searchHits) {
// do something with the SearchHit
}
SearchHit
提供瞭如 index
, type
, docId
和每個命中查詢的分數
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
而且,還可以獲取到文件的源資料,以 JSON-String 形式或者 key-value map 對的形式。在 map 中,欄位可以是普通型別,或者是列表型別,巢狀物件。
String sourceAsString = hit.getSourceAsString();
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
String documentTitle = (String) sourceAsMap.get("title");
List<Object> users = (List<Object>) sourceAsMap.get("user");
Map<String, Object> innerObject =
(Map<String, Object>) sourceAsMap.get("innerObject");
Search API 查詢關係
上面的 QueryBuilder
, SearchSourceBuilder
和 SearchRequest
之間都是巢狀關係,為此我專門整理了一個關係圖,以便更清楚的確認它們之間的關係。感興趣的同學可用此圖與前面的 API 進行對應,以加深理解。
結語
本篇包含了 Java High level Rest Client 的 SearchAPI 部分,獲取高亮,聚合,分析的結果並沒有在本文涉及,需要的同學可參考官方文件,下篇會包含查詢構建,敬請期待~