- 简介
- query_then_fetch
- query_and_fetch
- dfs_query_and_fetch
- dfs_query_then_fetch
- count
- scan
简介
Elasticsearch允许通过指定搜索类型来选择查询在内部如何处理。不同的搜索类型适合不同的情况;
可以只在乎性能,但有时查询的关联性可能是最重要的因素。使用search_type请求参数指定搜索类型,
其各种取值介绍参考下文,其中size参数指定一次查询中返回的最大文档数。默认值为0。在Elasticsearch代码中,会将上述几种搜索类型分隔成几个阶段phrases顺序执行,
前提是可以分成两个阶段。
比如query_and_fetch
不能分解
成两
个阶段,只需一步就能完成查询。
而dfs_query_then_fetch需要三个阶段。这几种搜索类型最终会调用具体的搜索类SearchService,
该类的介绍参考另一篇文章。
query_then_fetch
此搜索类型分两步,第一步:执行查询得到对文档进行排序和分级所需信息。这一步在所有的分片上执行。然后,只在相关分片上
查询文档的实际
内容。不同于query_and_fetch,此查询类型返回结果的最大数量等于size参数的值。默认使用这个类型。
public class TransportSearchQueryThenFetchAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''第一阶段是queryphrase''' @Override protected String firstPhaseName() { return "query"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<QuerySearchResult> listener) { '''调用SearchServiceTransportAction发送query请求''' searchService.sendExecuteQuery(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段''' @Override protected void moveToSecondPhase() { ... } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
query_and_fetch
这通常是最快也最简单的搜索类型实现。查询在所有分片上并行执行(当然,任意一个主分片,只查询一个副本),
所有分片
返回等于size值的结果数。返回文档的最大数量等于size的值乘以分片的数量。
public class TransportSearchQueryAndFetchAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''只有query_fetch''' @Override protected String firstPhaseName() { return "query_fetch"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<QueryFetchSearchResult> listener) { '''调用SearchServiceTransportAction发送fetch请求''' searchService.sendExecuteFetch(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段,对于该搜索类型, 第二阶段无须再查询''' @Override protected void moveToSecondPhase() throws Exception { try { innerFinishHim(); } catch (Throwable e) { ReduceSearchPhaseException failure = new ReduceSearchPhaseException("merge", "", e, buildShardFailures()); if (logger.isDebugEnabled()) { logger.debug("failed to reduce search", failure); } listener.onFailure(failure); } } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段''' @Override protected void moveToSecondPhase() { ... } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
dfs_query_and_fetch
跟query_and_fetch类似,但相比query_and_fetch,它包含一个额外阶段,在初始查询中执行分布式词频的计算,
以得到返回文件的更精确的得分
,从而让查询结果更相关。
public class TransportSearchDfsQueryAndFetchAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''第一阶段是dfs''' @Override protected String firstPhaseName() { return "dfs"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<DfsSearchResult> listener) { '''调用SearchServiceTransportAction发送dfs请求''' searchService.sendExecuteDfs(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段''' protected void moveToSecondPhase() { for (final AtomicArray.Entry<DfsSearchResult> entry : firstResults.asList()) { DfsSearchResult dfsResult = entry.value; DiscoveryNode node = nodes.get(dfsResult.shardTarget().nodeId()); if (node.id().equals(nodes.localNodeId())) { localOperations++; } else { QuerySearchRequest querySearchRequest = new QuerySearchRequest(request, dfsResult.id(), dfs); '''执行第二阶段fetch''' executeSecondPhase(entry.index, dfsResult, counter, node, querySearchRequest); } } ... } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
dfs_query_then_fetch
与前一个dfs_query_and_ fetch一样,dfs_query_then_fetch类似于相应的query_then_fetch,但比query_ then_fetch多了一个额外的阶段,就像dfs_query_and_fetch一样。
public class TransportSearchDfsQueryThenFetchAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''第一阶段是dfs''' @Override protected String firstPhaseName() { return "dfs"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<DfsSearchResult> listener) { '''调用SearchServiceTransportAction发送dfs请求''' searchService.sendExecuteDfs(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段''' protected void moveToSecondPhase() { final AggregatedDfs dfs = searchPhaseController.aggregateDfs(firstResults); final AtomicInteger counter = new AtomicInteger(firstResults.asList().size()); int localOperations = 0; for (final AtomicArray.Entry<DfsSearchResult> entry : firstResults.asList()) { DfsSearchResult dfsResult = entry.value; DiscoveryNode node = nodes.get(dfsResult.shardTarget().nodeId()); if (node.id().equals(nodes.localNodeId())) { localOperations++; } else { QuerySearchRequest querySearchRequest = new QuerySearchRequest(request, dfsResult.id(), dfs); '''执行第二阶段query''' executeQuery(entry.index, dfsResult, counter, querySearchRequest, node); } } ... } void executeQuery(final int shardIndex, final DfsSearchResult dfsResult, final AtomicInteger counter, final QuerySearchRequest querySearchRequest, DiscoveryNode node) { searchService.sendExecuteQuery(node, querySearchRequest, new SearchServiceListener<QuerySearchResult>() { @Override public void onResult(QuerySearchResult result) { result.shardTarget(dfsResult.shardTarget()); queryResults.set(shardIndex, result); if (counter.decrementAndGet() == 0) { '''执行第三个阶段fetch''' executeFetchPhase(); } } } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
count
一个特殊的搜索,只返回匹配查询的文档数。如果你只需要结果数量,而不关心文档,
应该使用这个搜索类型。
public class TransportSearchCountAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''第一阶段是query''' @Override protected String firstPhaseName() { return "query"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<QuerySearchResult> listener) { '''调用SearchServiceTransportAction发送query请求''' searchService.sendExecuteQuery(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段,此搜索类型第二个阶段无需查询''' protected void moveToSecondPhase() { protected void moveToSecondPhase() throws Exception { final InternalSearchResponse internalResponse = searchPhaseController.merge(SearchPhaseController.EMPTY_DOCS, firstResults, (AtomicArray<? extends FetchSearchResultProvider>) AtomicArray.empty()); String scrollId = null; if (request.scroll() != null) { scrollId = buildScrollId(request.searchType(), firstResults, null); } listener.onResponse(new SearchResponse(internalResponse, scrollId, expectedSuccessfulOps, successulOps.get(), buildTookInMillis(), buildShardFailures())); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
scan
另一个特殊的搜索类型,只有在要让查询返回大量结果时才用。它跟一般的查询有点不同,因为在发送第一个请
求之后,
Elasticsearch响应一个滚动标识符,类似于关系型数据库中的游标。
所有查询需要在_search/scroll REST端点运行,并需要在请求主体中发送返回的滚动标识符。
public class TransportSearchScanAction extends TransportSearchTypeAction { '''执行请求''' @Override protected void doExecute(SearchRequest searchRequest, ActionListener<SearchResponse> listener) { new AsyncAction(searchRequest, listener).start(); } private class AsyncAction extends BaseAsyncAction<QuerySearchResult> { '''第一阶段是init_scan''' @Override protected String firstPhaseName() { return "init_scan"; } @Override protected void sendExecuteFirstPhase(DiscoveryNode node, ShardSearchRequest request, SearchServiceListener<QuerySearchResult> listener) { '''调用SearchServiceTransportAction发送scan请求''' searchService.sendExecuteScan(node, request, listener); } '''在其父类TransportSearchTypeAction的onFirstPhaseResult函数中调用 在收到第一阶段的处理结果后转移到第二阶段,此搜索类型第二个阶段无需查询''' @Override protected void moveToSecondPhase() throws Exception { final InternalSearchResponse internalResponse = searchPhaseController.merge(SearchPhaseController.EMPTY_DOCS, firstResults, (AtomicArray<? extends FetchSearchResultProvider>) AtomicArray.empty()); String scrollId = null; if (request.scroll() != null) { scrollId = buildScrollId(request.searchType(), firstResults, ImmutableMap.of("total_hits", Long.toString(internalResponse.hits().totalHits()))); } listener.onResponse(new SearchResponse(internalResponse, scrollId, expectedSuccessfulOps, successulOps.get(), buildTookInMillis(), buildShardFailures())); } }}