Fix bug in single shard optimization when sorting documents in search request

This commit adds a function to shard-level query result to determine whether
there are any hits that needs fetching. Currently, a shard-level query result
can have hits when there are search hits and/or completion suggestion hits.
The newly added function encapsulates the checks to determine if a shard-level
query result has any fetchable hits, which is used in optimizing for sorting
documents and releasing search request contexts.
This commit is contained in:
Areek Zillur 2016-08-12 17:05:35 -04:00
parent 7542ef3173
commit 40d7ebc515
4 changed files with 11 additions and 13 deletions

View File

@ -322,11 +322,8 @@ abstract class AbstractSearchAsyncAction<FirstResult extends SearchPhaseResult>
// we only release search context that we did not fetch from if we are not scrolling // we only release search context that we did not fetch from if we are not scrolling
if (request.scroll() == null) { if (request.scroll() == null) {
for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults.asList()) { for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults.asList()) {
QuerySearchResult queryResult = entry.value.queryResult().queryResult(); QuerySearchResult queryResult = entry.value.queryResult();
final TopDocs topDocs = queryResult.topDocs(); if (queryResult.hasHits()
final Suggest suggest = queryResult.suggest();
if (((topDocs != null && topDocs.scoreDocs.length > 0) // the shard had matches
||suggest != null && suggest.hasScoreDocs()) // or had suggest docs
&& docIdsToLoad.get(entry.index) == null) { // but none of them made it to the global top docs && docIdsToLoad.get(entry.index) == null) { // but none of them made it to the global top docs
try { try {
DiscoveryNode node = nodes.get(entry.value.queryResult().shardTarget().nodeId()); DiscoveryNode node = nodes.get(entry.value.queryResult().shardTarget().nodeId());

View File

@ -269,7 +269,7 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
loadOrExecuteQueryPhase(request, context); loadOrExecuteQueryPhase(request, context);
if (hasHits(context.queryResult()) == false && context.scrollContext() == null) { if (context.queryResult().hasHits() == false && context.scrollContext() == null) {
freeContext(context.id()); freeContext(context.id());
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
@ -324,7 +324,7 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
operationListener.onPreQueryPhase(context); operationListener.onPreQueryPhase(context);
long time = System.nanoTime(); long time = System.nanoTime();
queryPhase.execute(context); queryPhase.execute(context);
if (hasHits(context.queryResult()) == false && context.scrollContext() == null) { if (context.queryResult().hasHits() == false && context.scrollContext() == null) {
// no hits, we can release the context since there will be no fetch phase // no hits, we can release the context since there will be no fetch phase
freeContext(context.id()); freeContext(context.id());
} else { } else {
@ -861,11 +861,6 @@ public class SearchService extends AbstractLifecycleComponent implements IndexEv
context.docIdsToLoad(docIdsToLoad, 0, docIdsToLoad.length); context.docIdsToLoad(docIdsToLoad, 0, docIdsToLoad.length);
} }
private static boolean hasHits(final QuerySearchResult searchResult) {
return searchResult.topDocs().scoreDocs.length > 0 ||
(searchResult.suggest() != null && searchResult.suggest().hasScoreDocs());
}
private void processScroll(InternalScrollSearchRequest request, SearchContext context) { private void processScroll(InternalScrollSearchRequest request, SearchContext context) {
// process scroll // process scroll
context.from(context.from() + context.size()); context.from(context.from() + context.size());

View File

@ -181,7 +181,7 @@ public class SearchPhaseController extends AbstractComponent {
} else { } else {
// lets see if we only got hits from a single shard, if so, we can optimize... // lets see if we only got hits from a single shard, if so, we can optimize...
for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : results) { for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : results) {
if (entry.value.queryResult().topDocs().scoreDocs.length > 0) { if (entry.value.queryResult().hasHits()) {
if (result != null) { // we already have one, can't really optimize if (result != null) { // we already have one, can't really optimize
canOptimize = false; canOptimize = false;
break; break;

View File

@ -188,6 +188,12 @@ public class QuerySearchResult extends QuerySearchResultProvider {
return this; return this;
} }
/** Returns true iff the result has hits */
public boolean hasHits() {
return (topDocs != null && topDocs.scoreDocs.length > 0) ||
(suggest != null && suggest.hasScoreDocs());
}
public static QuerySearchResult readQuerySearchResult(StreamInput in) throws IOException { public static QuerySearchResult readQuerySearchResult(StreamInput in) throws IOException {
QuerySearchResult result = new QuerySearchResult(); QuerySearchResult result = new QuerySearchResult();
result.readFrom(in); result.readFrom(in);