Search API
搜索請求
SearchRequest用於與搜索文檔、聚合、suggestions相關的任何操作,還提供了在結果文檔上請求高亮的方法。
在最基本的表單中,我們可以向請求添加查詢:
SearchRequest searchRequest = new SearchRequest(); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); searchRequest.source(searchSourceBuilder);
- 創建
SeachRequest,沒有參數,這將針對所有索引運行。 - 大多數搜索參數都添加到
SearchSourceBuilder中,它為搜索請求body中的所有內容提供了setter。 - 將
match_all查詢添加到SearchSourceBuilder。 - 將
SearchSourceBuilder添加到SeachRequest。
可選參數
我們先來看一下SearchRequest的一些可選參數:
SearchRequest searchRequest = new SearchRequest("posts"); searchRequest.types("doc");
- 將請求限制為一個索引。
- 將請求限制為一個類型。
還有一些其他有趣的可選參數:
searchRequest.routing("routing");
- 設置路由參數。
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
- 設置
IndicesOptions控制如何解析不可用的索引以及如何擴展通配符表達式。
searchRequest.preference("_local");
- 使用首選項參數,例如執行搜索以優先選擇本地碎片,默認是隨機的跨碎片。
使用SearchSourceBuilder
控制搜索行為的大多數選項都可以在SearchSourceBuilder上設置,它包含或多或少與Rest API的搜索請求body中等效的選項。
以下是一些常見選項的幾個示例:
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); sourceBuilder.from(0); sourceBuilder.size(5); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
- 使用默認選項創建
SearchSourceBuilder。 - 設置查詢,可以是任何類型的
QueryBuilder。 - 設置確定結果要從哪個索引開始搜索的
from選項,默認為0。 - 設置確定搜索命中返回的數的
size選項,默認為10。 - 設置一個可選的超時,控制允許搜索的時間。
在此之后,只需將SearchSourceBuilder添加到SearchRequest:
SearchRequest searchRequest = new SearchRequest(); searchRequest.indices("posts"); searchRequest.source(sourceBuilder);
構建查詢
使用QueryBuilder對象創建搜索查詢,QueryBuilder存在對於Elasticsearch的查詢DSL支持的每種搜索查詢類型。
可以使用其構造函數創建QueryBuilder:
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("user", "kimchy");
- 創建一個在字段“user”上匹配文本“kimchy”的全文匹配查詢。
創建后,QueryBuilder對象提供了配置其創建的搜索查詢選項的方法:
matchQueryBuilder.fuzziness(Fuzziness.AUTO); matchQueryBuilder.prefixLength(3); matchQueryBuilder.maxExpansions(10);
- 在匹配查詢上啟用模糊匹配。
- 在匹配查詢上設置前綴長度選項。
- 設置最大擴展選項以控制查詢的模糊過程。
也可以使用QueryBuilders實用程序類創建QueryBuilder對象,此類提供了可用於使用流暢的編程樣式創建QueryBuilder對象的輔助方法:
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("user", "kimchy") .fuzziness(Fuzziness.AUTO) .prefixLength(3) .maxExpansions(10);
無論用於創建它的方法是什么,都必須將QueryBuilder對象添加到SearchSourceBuilder,如下所示:
searchSourceBuilder.query(matchQueryBuilder);
構建查詢頁面提供了所有可用的搜索查詢的列表及其相應的QueryBuilder對象和QueryBuilders輔助方法。
指定排序
SearchSourceBuilder允許添加一個或多個SortBuilder實例,有四種特殊的實現(Field-,Score-,GeoDistance-和ScriptSortBuilder)。
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); sourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC));
- 按
_score降序排序(默認值)。 - 也可以按
_id字段進行升序排序。
源過濾
默認情況下,搜索請求會返回文檔_source的內容,但與Rest API中的內容一樣,你可以覆蓋此行為,例如,你可以完全關閉_source檢索:
sourceBuilder.fetchSource(false);
該方法還接受一個或多個通配符模式的數組,以控制以更精細的方式包含或排除哪些字段:
String[] includeFields = new String[] {"title", "user", "innerObject.*"}; String[] excludeFields = new String[] {"_type"}; sourceBuilder.fetchSource(includeFields, excludeFields);
請求高亮
通過在SearchSourceBuilder上設置HighlightBuilder,可以實現高亮搜索結果,通過將一個或多個HighlightBuilder.Field實例添加到HighlightBuilder,可以為每個字段定義不同的高亮行為。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); HighlightBuilder highlightBuilder = new HighlightBuilder(); HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("title"); highlightTitle.highlighterType("unified"); highlightBuilder.field(highlightTitle); HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user"); highlightBuilder.field(highlightUser); searchSourceBuilder.highlighter(highlightBuilder);
- 創建一個新的
HighlightBuilder。 - 為
title字段創建字段高光色。 - 設置字段高光色類型。
- 將字段高光色添加到高亮構建器。
Rest API文檔中有許多選項需要詳細說明,Rest API參數(例如pre_tags)通常由具有相似名稱的setter更改而來(例如#preTags(String ...))。
稍后可以從SearchResponse中檢索高亮的文本片段。
請求聚合
可以通過先創建適當的AggregationBuilder然后在SearchSourceBuilder上設置聚合來將聚合添加到搜索中,在以下示例中,我們在公司名稱上創建terms聚合 ,使用子聚合在公司員工平均年齡上:
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); TermsAggregationBuilder aggregation = AggregationBuilders.terms("by_company") .field("company.keyword"); aggregation.subAggregation(AggregationBuilders.avg("average_age") .field("age")); searchSourceBuilder.aggregation(aggregation);
構建聚合頁面提供了所有可用的聚合的列表及其對應的AggregationBuilder對象和AggregationBuilders輔助方法。
稍后我們將看到如何訪問SearchResponse中的聚合。
請求Suggestion
要向搜索請求添加建議,請使用從SuggestBuilders工廠類中可輕松訪問的SuggestionBuilder的實現之一,Suggestion構建器需要添加到頂級SuggestBuilder,它本身可以在SearchSourceBuilder上設置。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SuggestionBuilder termSuggestionBuilder = SuggestBuilders.termSuggestion("user").text("kmichy"); SuggestBuilder suggestBuilder = new SuggestBuilder(); suggestBuilder.addSuggestion("suggest_user", termSuggestionBuilder); searchSourceBuilder.suggest(suggestBuilder);
- 為
user字段和文本kmichy創建一個新的TermSuggestionBuilder。 - 添加suggestion構建器並將其命名為
suggest_user。
我們稍后將看到如何從SearchResponse中檢索suggestion。
分析查詢和聚合
分析API可用於分析特定搜索請求的查詢和聚合的執行情況,為了使用它,必須在SearchSourceBuilder上將profile標志設置為true:
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.profile(true);
執行SearchRequest后,相應的SearchResponse將包含分析結果。
同步執行
以下列方式執行SearchRequest時,客戶端在繼續執行代碼之前等待返回SearchResponse:
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
異步執行
執行SearchRequest也可以以異步方式完成,以便客戶端可以直接返回,用戶需要通過將請求和監聽器傳遞給異步搜索方法來指定響應或潛在的故障如何處理:
client.searchAsync(searchRequest, RequestOptions.DEFAULT, listener);
- 要執行的
SearchRequest和執行完成時要使用的ActionListener。
異步方法不會阻塞並且立即返回,完成后,如果執行成功完成則使用onResponse方法回調ActionListener,如果失敗則使用onFailure方法。
SearchResponse的典型監聽器如下所示:
ActionListener<SearchResponse> listener = new ActionListener<SearchResponse>() { @Override public void onResponse(SearchResponse searchResponse) { } @Override public void onFailure(Exception e) { } };
onResponse:執行成功完成時調用。onFailure:在整個SearchRequest失敗時調用。
SearchResponse
通過執行搜索返回的SearchResponse提供有關搜索執行本身以及對返回文檔的訪問的詳細信息,首先,有關於請求執行本身的有用信息,例如HTTP狀態碼,執行時間或請求是提前終止還是超時:
RestStatus status = searchResponse.status(); TimeValue took = searchResponse.getTook(); Boolean terminatedEarly = searchResponse.isTerminatedEarly(); boolean timedOut = searchResponse.isTimedOut();
其次,響應還通過提供有關搜索影響的碎片總數以及成功與不成功碎片的統計信息的碎片級別執行的信息,可以通過在ShardSearchFailures上迭代數組來處理可能的失敗,如下例所示:
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提供對每個搜索命中的索引、類型、docId和分數等基本信息的訪問:
String index = hit.getIndex(); String type = hit.getType(); String id = hit.getId(); float score = hit.getScore();
此外,它還允許你以簡單的JSON-String或鍵/值對映射的形式返回文檔源,在此映射中,常規字段由字段名稱鍵控並包含字段值,多值字段作為對象列表返回,嵌套對象作為另一個鍵/值映射返回,這些案例需要相應地進行投射:
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");
檢索高亮
如果需要,可以從結果中的每個SearchHit檢索高亮的文本片段,命中對象提供對HighlightField實例的字段名稱映射的訪問,每個實例包含一個或多個高亮的文本片段:
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits.getHits()) { Map<String, HighlightField> highlightFields = hit.getHighlightFields(); HighlightField highlight = highlightFields.get("title"); Text[] fragments = highlight.fragments(); String fragmentString = fragments[0].string(); }
- 獲取
title字段的高亮。 - 獲取包含高亮的字段內容的一個或多個片段。
檢索聚合
可以從SearchResponse檢索聚合,先獲取聚合樹的根,Aggregations對象,然后按名稱獲取聚合。
Aggregations aggregations = searchResponse.getAggregations(); Terms byCompanyAggregation = aggregations.get("by_company"); Bucket elasticBucket = byCompanyAggregation.getBucketByKey("Elastic"); Avg averageAge = elasticBucket.getAggregations().get("average_age"); double avg = averageAge.getValue();
- 獲取
by_company的terms聚合。 - 獲取使用
Elastic鍵入的桶。 - 從該桶中獲取
average_age子聚合。
請注意,如果按名稱訪問聚合,則需要根據所請求的聚合類型指定聚合接口,否則將引發ClassCastException:
Range range = aggregations.get("by_company");
- 這將引發異常,因為“by_company”是一個
terms聚合,但我們嘗試將其作為range聚合進行檢索。
還可以將所有聚合作為由聚合名稱鍵入的映射進行訪問,在這種情況下,需要顯式地進行到正確聚合接口的轉換:
Map<String, Aggregation> aggregationMap = aggregations.getAsMap(); Terms companyAggregation = (Terms) aggregationMap.get("by_company");
還有一些getter將所有頂級聚合作為列表返回:
List<Aggregation> aggregationList = aggregations.asList();
最后但並非最不重要的是,你可以迭代所有聚合,然后例如決定如何根據類型進一步處理它們:
for (Aggregation agg : aggregations) { String type = agg.getType(); if (type.equals(TermsAggregationBuilder.NAME)) { Bucket elasticBucket = ((Terms) agg).getBucketByKey("Elastic"); long numberOfDocs = elasticBucket.getDocCount(); } }
檢索suggestions
要從SearchResponse獲取suggestions,請使用Suggest對象作為入口點,然后檢索嵌套的suggestion對象:
Suggest suggest = searchResponse.getSuggest(); TermSuggestion termSuggestion = suggest.getSuggestion("suggest_user"); for (TermSuggestion.Entry entry : termSuggestion.getEntries()) { for (TermSuggestion.Entry.Option option : entry) { String suggestText = option.getText().string(); } }
- 使用
Suggest類訪問suggestions。 - 可以通過名稱檢索suggestions,你需要將它們分配給正確類型的Suggestion類(此處為
TermSuggestion),否則拋出ClassCastException。 - 迭代suggestion條目。
- 在一個條目中迭代選項。
檢索性能分析結果
使用getProfileResults()方法從SearchResponse檢索分析結果,此方法返回包含SearchSquest執行中涉及的每個碎片的ProfileShardResult對象的Map,ProfileShardResult使用唯一標識分析結果對應的碎片的鍵存儲在Map中。
下面是一個示例代碼,顯示如何迭代每個碎片的所有分析結果:
Map<String, ProfileShardResult> profilingResults = searchResponse.getProfileResults(); for (Map.Entry<String, ProfileShardResult> profilingResult : profilingResults.entrySet()) { String key = profilingResult.getKey(); ProfileShardResult profileShardResult = profilingResult.getValue(); }
- 從
SearchResponse中檢索ProfileShardResult的Map。 - 如果鍵已知,則可以通過碎片的鍵檢索分析結果,否則迭代所有分析結果可能更簡單。
- 檢索標識
ProfileShardResult屬於哪個碎片的鍵。 - 檢索給定碎片的
ProfileShardResult。
ProfileShardResult對象本身包含一個或多個查詢分析結果,每個查詢針對基礎Lucene索引執行:
List<QueryProfileShardResult> queryProfileShardResults =
profileShardResult.getQueryProfileResults();
for (QueryProfileShardResult queryProfileResult : queryProfileShardResults) { }
- 檢索
QueryProfileShardResult的列表。 - 迭代每個
QueryProfileShardResult。
每個QueryProfileShardResult都提供對詳細查詢樹執行的訪問,作為ProfileResult對象列表返回:
for (ProfileResult profileResult : queryProfileResult.getQueryResults()) { String queryName = profileResult.getQueryName(); long queryTimeInMillis = profileResult.getTime(); List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); }
- 迭代分析結果。
- 檢索Lucene查詢的名稱。
- 檢索執行Lucene查詢所花費的時間。
- 檢索子查詢的分析結果(如果有)。
Rest API文檔包含有關分析查詢的更多信息以及查詢分析信息的說明。
QueryProfileShardResult還可以訪問Lucene收集器的分析信息:
CollectorResult collectorResult = queryProfileResult.getCollectorResult(); String collectorName = collectorResult.getName(); Long collectorTimeInMillis = collectorResult.getTime(); List<CollectorResult> profiledChildren = collectorResult.getProfiledChildren();
- 檢索Lucene收集器的分析結果。
- 檢索Lucene收集器的名稱。
- 檢索執行Lucene收集器所花費的時間。
- 檢索子收集器的分析結果(如果有)。
Rest API文檔包含有關Lucene收集器的分析信息的更多信息。
以與查詢樹執行非常類似的方式,QueryProfileShardResult對象提供對詳細聚合樹執行的訪問:
AggregationProfileShardResult aggsProfileResults = profileShardResult.getAggregationProfileResults(); for (ProfileResult profileResult : aggsProfileResults.getProfileResults()) { String aggName = profileResult.getQueryName(); long aggTimeInMillis = profileResult.getTime(); List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); }
- 檢索
AggregationProfileShardResult。 - 迭代聚合分析結果。
- 檢索聚合的類型(對應於用於執行聚合的Java類)。
- 檢索執行Lucene收集器所花費的時間。
- 檢索子聚合的分析結果(如果有)。
Rest API文檔包含有關分析聚合的更多信息。
