引言
在上一篇 中主要介紹了 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 部分,獲取高亮,聚合,分析的結果並沒有在本文涉及,需要的同學可參考官方文檔,下篇會包含查詢構建,敬請期待~