1. 程式人生 > >Elasticsearch Java Rest Client API 整理總結 (二) —— SearchAPI

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

除了配置 indextype 外,還有一些其他的可選引數

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);

聚合請求

要實現聚合請求分兩步

  1. 建立合適的 `AggregationBuilder
  2. 作為引數配置在 `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 提供瞭如 indextypedocId 和每個命中查詢的分數

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 查詢關係

上面的 QueryBuilderSearchSourceBuilderSearchRequest 之間都是巢狀關係,為此我專門整理了一個關係圖,以便更清楚的確認它們之間的關係。感興趣的同學可用此圖與前面的 API 進行對應,以加深理解。

search API 關係圖

結語

本篇包含了 Java High level Rest Client 的 SearchAPI 部分,獲取高亮,聚合,分析的結果並沒有在本文涉及,需要的同學可參考官方文件,下篇會包含查詢構建,敬請期待~

系列文章列表