diff --git a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java index ed76181c646..d0c827c4dc1 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java @@ -61,6 +61,7 @@ import org.elasticsearch.search.lookup.SourceLookup; import org.elasticsearch.tasks.TaskCancelledException; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -144,13 +145,20 @@ public class FetchPhase implements SearchPhase { } try { + DocIdToIndex[] docs = new DocIdToIndex[context.docIdsToLoadSize()]; + for (int index = 0; index < context.docIdsToLoadSize(); index++) { + docs[index] = new DocIdToIndex(context.docIdsToLoad()[context.docIdsToLoadFrom() + index], index); + } + Arrays.sort(docs); + SearchHit[] hits = new SearchHit[context.docIdsToLoadSize()]; + SearchHit[] sortedHits = new SearchHit[context.docIdsToLoadSize()]; FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext(); for (int index = 0; index < context.docIdsToLoadSize(); index++) { if (context.isCancelled()) { throw new TaskCancelledException("cancelled"); } - int docId = context.docIdsToLoad()[context.docIdsToLoadFrom() + index]; + int docId = docs[index].docId; int readerIndex = ReaderUtil.subIndex(docId, context.searcher().getIndexReader().leaves()); LeafReaderContext subReaderContext = context.searcher().getIndexReader().leaves().get(readerIndex); int subDocId = docId - subReaderContext.docBase; @@ -165,7 +173,8 @@ public class FetchPhase implements SearchPhase { storedToRequestedFields, subReaderContext); } - hits[index] = searchHit; + sortedHits[index] = searchHit; + hits[docs[index].index] = searchHit; hitContext.reset(searchHit, subReaderContext, subDocId, context.searcher()); for (FetchSubPhase fetchSubPhase : fetchSubPhases) { fetchSubPhase.hitExecute(context, hitContext); @@ -176,7 +185,7 @@ public class FetchPhase implements SearchPhase { } for (FetchSubPhase fetchSubPhase : fetchSubPhases) { - fetchSubPhase.hitsExecute(context, hits); + fetchSubPhase.hitsExecute(context, sortedHits); if (context.isCancelled()) { throw new TaskCancelledException("cancelled"); } @@ -189,6 +198,21 @@ public class FetchPhase implements SearchPhase { } } + static class DocIdToIndex implements Comparable { + final int docId; + final int index; + + DocIdToIndex(int docId, int index) { + this.docId = docId; + this.index = index; + } + + @Override + public int compareTo(DocIdToIndex o) { + return Integer.compare(docId, o.docId); + } + } + private int findRootDocumentIfNested(SearchContext context, LeafReaderContext subReaderContext, int subDocId) throws IOException { if (context.mapperService().hasNested()) { BitSet bits = context.bitsetFilterCache() diff --git a/server/src/main/java/org/elasticsearch/search/fetch/FetchSubPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/FetchSubPhase.java index 8a8e4e8d77f..c04eb3c77e8 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/FetchSubPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/FetchSubPhase.java @@ -81,6 +81,8 @@ public interface FetchSubPhase { */ default void hitExecute(SearchContext context, HitContext hitContext) throws IOException {} - + /** + * Executes the hits level phase (note, hits are sorted by doc ids). + */ default void hitsExecute(SearchContext context, SearchHit[] hits) throws IOException {} } diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchDocValuesPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchDocValuesPhase.java index fc3f89fcc95..495ff260dce 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchDocValuesPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchDocValuesPhase.java @@ -40,9 +40,7 @@ import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -78,9 +76,6 @@ public final class FetchDocValuesPhase implements FetchSubPhase { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId)); - if (context.docValuesContext().fields().stream() .map(f -> f.format) .filter(USE_DEFAULT_FORMAT::equals) diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchScorePhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchScorePhase.java index fe2a79513bc..396dc837cf2 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchScorePhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchScorePhase.java @@ -30,8 +30,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; import java.util.Iterator; public class FetchScorePhase implements FetchSubPhase { @@ -44,9 +42,6 @@ public class FetchScorePhase implements FetchSubPhase { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId)); - final IndexSearcher searcher = context.searcher(); final Weight weight = searcher.createWeight(searcher.rewrite(context.query()), ScoreMode.COMPLETE, 1); Iterator leafContextIterator = searcher.getIndexReader().leaves().iterator(); diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchVersionPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchVersionPhase.java index 3c81af09be3..c588dd3fc9d 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchVersionPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/FetchVersionPhase.java @@ -28,8 +28,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; public final class FetchVersionPhase implements FetchSubPhase { @Override @@ -39,9 +37,6 @@ public final class FetchVersionPhase implements FetchSubPhase { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId)); - int lastReaderId = -1; NumericDocValues versions = null; for (SearchHit hit : hits) { diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesPhase.java index 9302f6d9d63..275e6ba011d 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/MatchedQueriesPhase.java @@ -35,7 +35,6 @@ import org.elasticsearch.search.internal.SearchContext.Lifetime; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -49,8 +48,6 @@ public final class MatchedQueriesPhase implements FetchSubPhase { context.parsedQuery() == null) { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, (a, b) -> Integer.compare(a.docId(), b.docId())); @SuppressWarnings("unchecked") List[] matchedQueries = new List[hits.length]; for (int i = 0; i < matchedQueries.length; ++i) { diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsPhase.java index 281517a2fb3..bd0c43b8aca 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/ScriptFieldsPhase.java @@ -30,10 +30,8 @@ import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; public final class ScriptFieldsPhase implements FetchSubPhase { @@ -44,9 +42,6 @@ public final class ScriptFieldsPhase implements FetchSubPhase { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId)); - int lastReaderId = -1; FieldScript[] leafScripts = null; List scriptFields = context.scriptFields().fields(); diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/SeqNoPrimaryTermPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/SeqNoPrimaryTermPhase.java index d8e1f060070..831adfbc799 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/SeqNoPrimaryTermPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/SeqNoPrimaryTermPhase.java @@ -28,8 +28,6 @@ import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Arrays; -import java.util.Comparator; public final class SeqNoPrimaryTermPhase implements FetchSubPhase { @Override @@ -38,9 +36,6 @@ public final class SeqNoPrimaryTermPhase implements FetchSubPhase { return; } - hits = hits.clone(); // don't modify the incoming hits - Arrays.sort(hits, Comparator.comparingInt(SearchHit::docId)); - int lastReaderId = -1; NumericDocValues seqNoField = null; NumericDocValues primaryTermField = null;