搜索API(其实就是使用高级客户端调用API进行查询)(6.8)
其实这一章主要讲的是两个方面,一个是请求,一个是响应,还有一些关于请求的设置。
Search Request(搜索请求)
SearchRequest用于与搜索文档、聚合、建议相关的任何操作,还提供在结果文档上请求高亮显示的方法。下面展示一个最简单的例子
//第一行,申明一个请求对象,可以将index和type作为参数传入
SearchRequest searchRequest = new SearchRequest();
//创建以一个Builder对象,作为我们之后多个聚合查询和查询条件的容器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//将一个查询条件放入
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//把条件放入请求对象中
searchRequest.source(searchSourceBuilder);
Optional arguments(针对可选参数)
- 首先让我们看一下SearchRequest的一些可选参数
SearchRequest searchRequest = new SearchRequest("posts");
这个参数是指定了index
searchRequest.routing("routing")
这个是设置路由参数
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());
设置IndicesOptions可控制如何解析不可用索引以及如何展开通配符表达式
searchRequest.preference("_local");
使用首选参数,例如,执行搜索首选本地碎片。默认情况下是跨碎片随机化。
其实还有很多属性可以设置,比如说设置type之类的,可以进源码观察
- 然后就是SearchSourceBuilder的对象参数介绍了。
控制搜索行为的大多数选项都可以在SearchSourceBuilder上设置,它包含的选项或多或少与Rest API的搜索请求主体中的选项相当。
下面是一个简单的例子:
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); (1)
sourceBuilder.query(QueryBuilders.termQuery("user", "kimchy")); (2)
sourceBuilder.from(0); (3)
sourceBuilder.size(5); (4)
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); (5)
(1)使用默认选项创建SearchSourceBuilder。
(2)设置查询。可以是任何类型的QueryBuilder
(3)设置from选项,该选项决定开始搜索的结果索引。默认值为0。
(4)设置确定返回的搜索命中次数的大小选项。默认为10。
(5)设置一个可选的超时,以控制搜索允许花费的时间。
在此之后,只需将SearchSourceBuilder添加到SearchRequest:
searchRequest.source(sourceBuilder);
- 再之后就是说说怎么构建一个查询(Building queries)
使用QueryBuilder对象创建搜索查询。对于Elasticsearch的查询DSL支持的每种搜索查询类型,都存在QueryBuilder。
MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("user", "kimchy");
Create a full text Match Query that matches the text “kimchy” over the field “user”.其实这是看起来跟前面不同,说白就也是一个请求条件对象,只是类型不同,这个更加具体
一旦创建,QueryBuilder对象提供方法来配置它创建的搜索查询的选项:
matchQueryBuilder.fuzziness(Fuzziness.AUTO); (1)
matchQueryBuilder.prefixLength(3); (2)
matchQueryBuilder.maxExpansions(10); (3)
(1)在匹配查询上启用模糊匹配
(2)在匹配查询上设置前缀长度选项
(3)设置最大扩展选项来控制查询的模糊过程
这里其实还有一个工具类可以创建各种各样的条件对象:QueryBuilder对象也可以使用QueryBuilders实用程序类创建。这个类提供了帮助器方法,可以使用流畅的编程风格创建QueryBuilder对象
QueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("user", "kimchy")
.fuzziness(Fuzziness.AUTO)
.prefixLength(3)
.maxExpansions(10);
无论使用什么方法来创建它,QueryBuilder对象都必须添加到SearchSourceBuilder中,如下所示(这个很重要)
searchSourceBuilder.query(matchQueryBuilder);
构建查询页面提供了所有可用搜索查询的列表,以及它们对应的QueryBuilder对象和QueryBuilders帮助方法。
- 还要就是指定排序了(Specifying Sorting)
SearchSourceBuilder允许添加一个或多个SortBuilder实例。有四种特殊实现(Field-、Score-、GeoDistance-和ScriptSortBuilder)。
sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); (1)
sourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC)); (2)
(1) 按_score降序排序(默认值)
(2)也按_id字段升序排序
- 还有就是Source filtering(资源过滤)
默认情况下,搜索请求返回文档_source的内容,但是像在Rest API中一样,您可以覆盖这种行为。例如,您可以关闭_source检索完全:
sourceBuilder.fetchSource(false);
该方法还接受一个或多个通配符模式的数组,以更细粒度的方式控制包含或排除哪些字段:
String[] includeFields = new String[] {"title", "user", "innerObject.*"};
String[] excludeFields = new String[] {"_type"};
sourceBuilder.fetchSource(includeFields, excludeFields);
- 再接下来就是高亮查询(requesting Highlighting)
出显示搜索结果可以通过在SearchSourceBuilder上设置HighlightBuilder来实现。通过添加一个或多个HighlightBuilder,可以为每个字段定义不同的突出显示行为。字段实例到HighlightBuilder。
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
HighlightBuilder highlightBuilder = new HighlightBuilder(); (1)
HighlightBuilder.Field highlightTitle =
new HighlightBuilder.Field("title"); (2)
highlightTitle.highlighterType("unified"); (3)
highlightBuilder.field(highlightTitle); (4)
HighlightBuilder.Field highlightUser = new HighlightBuilder.Field("user");
highlightBuilder.field(highlightUser);
searchSourceBuilder.highlighter(highlightBuilder);
(1)创建一个新的HighlightBuilder
(2)为标题字段创建一个字段高亮显示
(3)设置字段高亮类型
(4)将字段高亮器添加到高亮构建器
有许多选项,在Rest API文档中有详细的说明。Rest API参数(例如pre_tags)通常是由具有类似名称的setter来改变的(例如#preTags(String…))。
- 接下来就是我们最重要的聚合查询请求(Requesting Aggregations)
通过首先创建适当的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);
Building Aggregations页面提供了所有可用聚合的列表,以及它们对应的AggregationBuilder对象和AggregationBuilders helper方法。
- (Requesting Suggestions)请求建议:这个不细说,看手册去
要向搜索请求添加建议,请使用一个可以从SuggestBuilders工厂类轻松访问的建议构建器实现。建议构建器需要添加到顶层的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);
- 在接下来就是请求的同步和异步了
先说同步吧(Synchronous Execution):当执行一个SearchRequest在以下方式,客户端等待SearchResponse返回之前,继续执行代码:
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
在无法解析高级REST客户机中的REST响应、请求超时或类似的情况下(服务器没有返回响应),同步调用可能会抛出IOException。
在服务器返回4xx或5xx错误代码的情况下,高级客户端尝试解析响应体错误细节,然后抛出通用的ElasticsearchException,并将原始的ResponseException作为抑制异常添加到其中。
然后就是异步了(Asynchronous Execution):执行SearchRequest也可以以异步方式完成,这样客户端就可以直接返回。用户需要通过向异步搜索方法传递请求和侦听器来指定如何处理响应或潜在的故障:
client.searchAsync(searchRequest, RequestOptions.DEFAULT, listener);
废话不多说,直接看回调示例:
ActionListener<SearchResponse> listener = new ActionListener<SearchResponse>() {
@Override
public void onResponse(SearchResponse searchResponse) {
}
@Override
public void onFailure(Exception e) {
}
};
SearchResponse(搜索响应)
执行搜索返回的SearchResponse提供了有关搜索执行本身以及对返回的文档的访问的详细信息。首先,有关于请求执行本身的有用信息,如HTTP状态码、执行时间或请求是否提前终止或超时:
RestStatus status = searchResponse.status();
TimeValue took = searchResponse.getTook();
Boolean terminatedEarly = searchResponse.isTerminatedEarly();
boolean timedOut = searchResponse.isTimedOut();
其次,响应还提供有关受搜索影响的切分总数以及成功与不成功切分的统计信息,从而提供关于切分级别上执行的信息。可能的失败也可以通过shardsearchfailure遍历数组来处理,如下例所示:
int totalShards = searchResponse.getTotalShards();
int successfulShards = searchResponse.getSuccessfulShards();
int failedShards = searchResponse.getFailedShards();
for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
// failures should be handled here
}
- 检索搜索命中(Retrieving SearchHits)
为了访问返回的文档,我们需要首先获得包含在响应中的SearchHits:
SearchHits hits = searchResponse.getHits();
SearchHits提供关于所有hits的全球信息,如总点击数或最大得分:
long totalHits = hits.getTotalHits();
float maxScore = hits.getMaxScore();
SearchHit提供访问基本信息,如索引,类型,docId和得分的每个搜索命中:
String index = hit.getIndex();
String type = hit.getType();
String id = hit.getId();
float score = hit.getScore();
此外,它允许您返回文档源,可以是简单的json字符串,也可以是键/值对的映射。在此映射中,常规字段按字段名进行键控,并包含字段值。多值字段作为对象列表返回,嵌套对象作为另一个键/值映射返回。这些案件需要作出相应的判决:
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");
- 检索聚合
可以通过首先获取聚合树的根、聚合对象,然后通过名称获取聚合,从SearchResponse中检索聚合。
Aggregations aggregations = searchResponse.getAggregations();
Terms byCompanyAggregation = aggregations.get("by_company"); (1)
Bucket elasticBucket = byCompanyAggregation.getBucketByKey("Elastic"); (2)
Avg averageAge = elasticBucket.getAggregations().get("average_age"); (3)
double avg = averageAge.getValue();
(1)获取by_company术语聚合
(2)把buckter拿来
(3)从存储桶中获取average_age子聚合
注意,如果您通过名称访问聚合,您需要根据您请求的聚合类型指定聚合接口,否则将抛出ClassCastException:
Range range = aggregations.get("by_company");
还可以将所有聚合作为映射访问,映射是由聚合名称键入的。在这种情况下,需要显式地转换到适当的聚合接口:
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();
}
}
- 检索聚合(Retrieving Aggregations)
要从SearchResponse得到建议,使用建议对象作为入口点,然后检索嵌套的建议对象:
Suggest suggest = searchResponse.getSuggest(); (1)
TermSuggestion termSuggestion = suggest.getSuggestion("suggest_user"); (2)
for (TermSuggestion.Entry entry : termSuggestion.getEntries()) { (3)
for (TermSuggestion.Entry.Option option : entry) { (4)
String suggestText = option.getText().string();
}
}
(1)使用Suggest类访问建议
(2)建议可以通过名称检索。您需要将它们分配给正确类型的建议类(这里是TermSuggestion),否则将抛出ClassCastException
(3)迭代建议条目
(4)遍历一个条目中的选项
- 检索分析结果
使用getProfileResults()方法从SearchResponse中检索分析结果。这个方法返回一个映射,其中包含一个ProfileShardResult对象,用于SearchRequest执行中涉及的每个碎片。ProfileShardResult使用唯一标识概要文件结果所对应的分片的键存储在映射中。
下面是一个示例代码,展示了如何遍历每个切分的所有分析结果:
Map<String, ProfileShardResult> profilingResults =
searchResponse.getProfileResults(); (1)
for (Map.Entry<String, ProfileShardResult> profilingResult : profilingResults.entrySet()) { (2)
String key = profilingResult.getKey(); (3)
ProfileShardResult profileShardResult = profilingResult.getValue(); (4)
}
(1)从SearchResponse中检索ProfileShardResult的映射
(2)如果shard的键是已知的,那么可以通过shard的键检索分析结果,否则遍历所有分析结果可能会更简单
(3)检索标识ProfileShardResult属于哪个切分的键
(4)检索给定切分的ProfileShardResult
ProfileShardResult对象本身包含一个或多个查询概要文件结果,每个查询针对底层Lucene索引执行:
List<QueryProfileShardResult> queryProfileShardResults =
profileShardResult.getQueryProfileResults(); (1)
for (QueryProfileShardResult queryProfileResult : queryProfileShardResults) { (2)
}
(1)检索QueryProfileShardResult列表
(2)遍历每个QueryProfileShardResult
每个QueryProfileShardResult提供对详细查询树执行的访问,返回为ProfileResult对象列表:
for (ProfileResult profileResult : queryProfileResult.getQueryResults()) { (1)
String queryName = profileResult.getQueryName(); (2)
long queryTimeInMillis = profileResult.getTime(); (3)
List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); (4)
}
(1)遍历配置文件结果
(2)遍历配置文件结果
(3)检索millis中执行Lucene查询所花费的时间
(4)检索子查询的概要结果(如果有的话)
Rest API文档包含更多关于分析查询的信息,并附有查询分析信息的描述。
QueryProfileShardResult还为Lucene收集器提供了访问分析信息的权限:
CollectorResult collectorResult = queryProfileResult.getCollectorResult(); (1)
String collectorName = collectorResult.getName(); (2)
Long collectorTimeInMillis = collectorResult.getTime(); (3)
List<CollectorResult> profiledChildren = collectorResult.getProfiledChildren(); (4)
(1)检索Lucene收集器的分析结果
(2)检索Lucene收集器的名称
(3)检索millis中执行Lucene收集器所花费的时间
(4)为子收集器检索概要结果(如果有的话)
Rest API文档包含更多关于Lucene收集器分析信息的信息。看到分析查询。
与查询树的执行方式非常相似,QueryProfileShardResult对象提供了对详细的聚合树执行的访问:
AggregationProfileShardResult aggsProfileResults =
profileShardResult.getAggregationProfileResults(); (1)
for (ProfileResult profileResult : aggsProfileResults.getProfileResults()) { (2)
String aggName = profileResult.getQueryName(); (3)
long aggTimeInMillis = profileResult.getTime(); (4)
List<ProfileResult> profiledChildren = profileResult.getProfiledChildren(); (5)
}
(1)检索AggregationProfileShardResult
(2)遍历聚合概要结果
(3)检索聚合的类型(对应于用于执行聚合的Java类)
(4)检索millis中执行Lucene收集器所花费的时间
(5)检索子聚合的概要结果(如果有的话)
Rest API文档包含更多关于分析聚合的信息。