From fabfca569b34b28d0a7a7c452e38139c8d67ef00 Mon Sep 17 00:00:00 2001 From: Mayya Sharipova Date: Thu, 20 Aug 2020 14:23:09 -0400 Subject: [PATCH] Implement filtering functionality in DocComparator --- .../apache/lucene/search/FieldComparator.java | 74 +------ .../lucene/search/FieldValueHitQueue.java | 23 ++- .../search/FilteringAfterDocComparator.java | 38 ---- .../FilteringAfterDocLeafComparator.java | 157 --------------- .../search/FilteringFieldComparator.java | 7 +- .../org/apache/lucene/search/SortField.java | 3 +- .../lucene/search/TopFieldCollector.java | 5 +- .../search/comparators/DocComparator.java | 185 ++++++++++++++++++ .../search/comparators/MinDocIterator.java | 66 +++++++ .../TestFieldSortOptimizationSkipping.java | 13 +- 10 files changed, 284 insertions(+), 287 deletions(-) delete mode 100644 lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocComparator.java delete mode 100644 lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocLeafComparator.java create mode 100644 lucene/core/src/java/org/apache/lucene/search/comparators/DocComparator.java create mode 100644 lucene/core/src/java/org/apache/lucene/search/comparators/MinDocIterator.java diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java index 687f20ddb4f4..b8e3a4c2ef26 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java +++ b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java @@ -136,6 +136,13 @@ public int compareValues(T first, T second) { } } + /** + * Informs the comparator that sorting is done in reverse. + * This is necessary only for skipping functionality. + */ + public void setReverse() { + } + /** * Base FieldComparator class for numeric types @@ -485,73 +492,6 @@ public int compareTop(int doc) throws IOException { return Float.compare(docValue, topValue); } } - - /** Sorts by ascending docID */ - public static final class DocComparator extends FieldComparator implements LeafFieldComparator { - private final int[] docIDs; - private int docBase; - private int bottom; - private int topValue; - - /** Creates a new comparator based on document ids for {@code numHits} */ - public DocComparator(int numHits) { - docIDs = new int[numHits]; - } - - @Override - public int compare(int slot1, int slot2) { - // No overflow risk because docIDs are non-negative - return docIDs[slot1] - docIDs[slot2]; - } - - @Override - public int compareBottom(int doc) { - // No overflow risk because docIDs are non-negative - return bottom - (docBase + doc); - } - - @Override - public void copy(int slot, int doc) { - docIDs[slot] = docBase + doc; - } - - @Override - public LeafFieldComparator getLeafComparator(LeafReaderContext context) { - // TODO: can we "map" our docIDs to the current - // reader? saves having to then subtract on every - // compare call - this.docBase = context.docBase; - return this; - } - - @Override - public void setBottom(final int bottom) { - this.bottom = docIDs[bottom]; - } - - @Override - public void setTopValue(Integer value) { - topValue = value; - } - - public int getTopValue() { - return topValue; - } - - @Override - public Integer value(int slot) { - return Integer.valueOf(docIDs[slot]); - } - - @Override - public int compareTop(int doc) { - int docValue = docBase + doc; - return Integer.compare(topValue, docValue); - } - - @Override - public void setScorer(Scorable scorer) {} - } /** Sorts by field's natural Term sort order, using * ordinals. This is functionally equivalent to {@link diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldValueHitQueue.java b/lucene/core/src/java/org/apache/lucene/search/FieldValueHitQueue.java index 6bb14164197f..a5f683684311 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FieldValueHitQueue.java +++ b/lucene/core/src/java/org/apache/lucene/search/FieldValueHitQueue.java @@ -58,8 +58,8 @@ private static final class OneComparatorFieldValueHitQueue oneComparator; - public OneComparatorFieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs, boolean hasAfter) { - super(fields, size, filterNonCompetitiveDocs, hasAfter); + public OneComparatorFieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs) { + super(fields, size, filterNonCompetitiveDocs); assert fields.length == 1; oneComparator = comparators[0]; @@ -95,8 +95,8 @@ protected boolean lessThan(final Entry hitA, final Entry hitB) { */ private static final class MultiComparatorsFieldValueHitQueue extends FieldValueHitQueue { - public MultiComparatorsFieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs, boolean hasAfter) { - super(fields, size, filterNonCompetitiveDocs, hasAfter); + public MultiComparatorsFieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs) { + super(fields, size, filterNonCompetitiveDocs); } @Override @@ -119,9 +119,9 @@ protected boolean lessThan(final Entry hitA, final Entry hitB) { } } - + // prevent instantiation and extension. - private FieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs, boolean hasAfter) { + private FieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompetitiveDocs) { super(size); // When we get here, fields.length is guaranteed to be > 0, therefore no // need to check it again. @@ -140,10 +140,11 @@ private FieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompet // try to rewrite the 1st comparator to the comparator that can skip non-competitive documents // skipping functionality is beneficial only for the 1st comparator comparators[i] = FilteringFieldComparator.wrapToFilteringComparator(field.getComparator(size, i), - field.reverse, numComparators == 1, hasAfter); + field.reverse, numComparators == 1); } else { comparators[i] = field.getComparator(size, i); } + if (field.reverse) comparators[i].setReverse(); } } @@ -160,20 +161,18 @@ private FieldValueHitQueue(SortField[] fields, int size, boolean filterNonCompet * The number of hits to retain. Must be greater than zero. * @param filterNonCompetitiveDocs * {@code true} If comparators should be allowed to filter non-competitive documents, {@code false} otherwise - * @param hasAfter - * {@code true} If this sort has "after" FieldDoc */ public static FieldValueHitQueue create(SortField[] fields, int size, - boolean filterNonCompetitiveDocs, boolean hasAfter) { + boolean filterNonCompetitiveDocs) { if (fields.length == 0) { throw new IllegalArgumentException("Sort must contain at least one field"); } if (fields.length == 1) { - return new OneComparatorFieldValueHitQueue<>(fields, size, filterNonCompetitiveDocs, hasAfter); + return new OneComparatorFieldValueHitQueue<>(fields, size, filterNonCompetitiveDocs); } else { - return new MultiComparatorsFieldValueHitQueue<>(fields, size, filterNonCompetitiveDocs, hasAfter); + return new MultiComparatorsFieldValueHitQueue<>(fields, size, filterNonCompetitiveDocs); } } diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocComparator.java b/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocComparator.java deleted file mode 100644 index 393a82fc3880..000000000000 --- a/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocComparator.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.lucene.search; - -import org.apache.lucene.index.LeafReaderContext; - -import java.io.IOException; - -/** - * A wrapper over {@code DocComparator} that has "after" FieldDoc. - * This comparator can quickly skip to the desired "after" document. - */ -class FilteringAfterDocComparator extends FilteringFieldComparator { - public FilteringAfterDocComparator(FieldComparator in, boolean reverse, boolean singleSort) { - super(in, reverse, singleSort); - } - - @Override - public final FilteringLeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException { - DocComparator inLeafComparator = (DocComparator) in.getLeafComparator(context); - return new FilteringAfterDocLeafComparator(inLeafComparator, context); - } - -} diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocLeafComparator.java b/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocLeafComparator.java deleted file mode 100644 index ba75a62f7695..000000000000 --- a/lucene/core/src/java/org/apache/lucene/search/FilteringAfterDocLeafComparator.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.lucene.search; - -import org.apache.lucene.index.LeafReaderContext; - -import java.io.IOException; - -/** - * This comparator is used when there is sort by _doc asc together with "after" FieldDoc. - * The comparator provides an iterator that can quickly skip to the desired "after" document. - */ -public class FilteringAfterDocLeafComparator implements FilteringLeafFieldComparator { - private final FieldComparator.DocComparator in; - private DocIdSetIterator topValueIterator; // iterator that starts from topValue if possible - private final int minDoc; - private final int maxDoc; - private final int docBase; - private boolean iteratorUpdated = false; - - public FilteringAfterDocLeafComparator(FieldComparator.DocComparator in, LeafReaderContext context) { - this.in = in; - this.minDoc = this.in.getTopValue() + 1; - this.maxDoc = context.reader().maxDoc(); - this.docBase = context.docBase; - this.topValueIterator = DocIdSetIterator.all(maxDoc); - } - - @Override - public DocIdSetIterator competitiveIterator() { - return new DocIdSetIterator() { - private int doc; - - @Override - public int nextDoc() throws IOException { - return doc = topValueIterator.nextDoc(); - } - - @Override - public int docID() { - return doc; - } - - @Override - public long cost() { - return topValueIterator.cost(); - } - - @Override - public int advance(int target) throws IOException { - return doc = topValueIterator.advance(target); - } - }; - - } - - @Override - public boolean iteratorUpdated() { - return iteratorUpdated; - } - - @Override - public void setQueueFull() { - } - - @Override - public void setHitsThresholdReached() { - if (iteratorUpdated) return; - if (docBase + maxDoc <= minDoc) { - topValueIterator = DocIdSetIterator.empty(); // skip this segment - } - final int segmentMinDoc = Math.max(0, minDoc - docBase); - topValueIterator = new MinDocIterator(segmentMinDoc, maxDoc); - iteratorUpdated = true; - } - - @Override - public void setBottom(int slot) { - in.setBottom(slot); - } - - @Override - public int compareBottom(int doc) { - return in.compareBottom(doc); - } - - @Override - public int compareTop(int doc) { - return in.compareTop(doc); - } - - @Override - public void copy(int slot, int doc) throws IOException { - in.copy(slot, doc); - } - - @Override - public void setScorer(Scorable scorer) throws IOException { - in.setScorer(scorer); - } - - - static class MinDocIterator extends DocIdSetIterator { - final int segmentMinDoc; - final int maxDoc; - int doc = -1; - - MinDocIterator(int segmentMinDoc, int maxDoc) { - this.segmentMinDoc = segmentMinDoc; - this.maxDoc = maxDoc; - } - - @Override - public int docID() { - return doc; - } - - @Override - public int nextDoc() throws IOException { - return advance(doc + 1); - } - - @Override - public int advance(int target) throws IOException { - assert target > doc; - if (doc == -1) { - // skip directly to minDoc - doc = Math.max(target, segmentMinDoc); - } else { - doc = target; - } - if (doc >= maxDoc) { - doc = NO_MORE_DOCS; - } - return doc; - } - - @Override - public long cost() { - return maxDoc - segmentMinDoc; - } - } -} diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteringFieldComparator.java b/lucene/core/src/java/org/apache/lucene/search/FilteringFieldComparator.java index 3c71d2518575..57a7284e3f95 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FilteringFieldComparator.java +++ b/lucene/core/src/java/org/apache/lucene/search/FilteringFieldComparator.java @@ -68,12 +68,10 @@ public int compareValues(T first, T second) { * @param comparator – comparator to wrap * @param reverse – if this sort is reverse * @param singleSort – true if this sort is based on a single field and there are no other sort fields for tie breaking - * @param hasAfter – true if this sort has after FieldDoc * @return comparator wrapped as a filtering comparator or the original comparator if the filtering functionality * is not implemented for it */ - public static FieldComparator wrapToFilteringComparator(FieldComparator comparator, boolean reverse, boolean singleSort, - boolean hasAfter) { + public static FieldComparator wrapToFilteringComparator(FieldComparator comparator, boolean reverse, boolean singleSort) { Class comparatorClass = comparator.getClass(); if (comparatorClass == FieldComparator.LongComparator.class){ return new FilteringNumericComparator<>((FieldComparator.LongComparator) comparator, reverse, singleSort); @@ -87,9 +85,6 @@ public static FieldComparator wrapToFilteringComparator(FieldComparator co if (comparatorClass == FieldComparator.FloatComparator.class){ return new FilteringNumericComparator<>((FieldComparator.FloatComparator) comparator, reverse, singleSort); } - if (comparatorClass == FieldComparator.DocComparator.class && hasAfter && reverse == false) { // if SortField.DOC with after - return new FilteringAfterDocComparator<>((FieldComparator.DocComparator) comparator, reverse, singleSort); - } return comparator; } diff --git a/lucene/core/src/java/org/apache/lucene/search/SortField.java b/lucene/core/src/java/org/apache/lucene/search/SortField.java index 608ce0a786e3..7b10f5e16022 100644 --- a/lucene/core/src/java/org/apache/lucene/search/SortField.java +++ b/lucene/core/src/java/org/apache/lucene/search/SortField.java @@ -24,6 +24,7 @@ import org.apache.lucene.index.DocValues; import org.apache.lucene.index.IndexSorter; import org.apache.lucene.index.SortFieldProvider; +import org.apache.lucene.search.comparators.DocComparator; import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataOutput; import org.apache.lucene.util.BytesRef; @@ -452,7 +453,7 @@ public FieldComparator getComparator(final int numHits, final int sortPos) { return new FieldComparator.RelevanceComparator(numHits); case DOC: - return new FieldComparator.DocComparator(numHits); + return new DocComparator(numHits); case INT: return new FieldComparator.IntComparator(numHits, field, (Integer) missingValue); diff --git a/lucene/core/src/java/org/apache/lucene/search/TopFieldCollector.java b/lucene/core/src/java/org/apache/lucene/search/TopFieldCollector.java index c358f723f2cb..522f096cd606 100644 --- a/lucene/core/src/java/org/apache/lucene/search/TopFieldCollector.java +++ b/lucene/core/src/java/org/apache/lucene/search/TopFieldCollector.java @@ -343,8 +343,7 @@ private TopFieldCollector(FieldValueHitQueue pq, int numHits, } else { relevanceComparator = null; canSetMinScore = false; - if (firstComparator instanceof FilteringFieldComparator) { - assert hitsThresholdChecker.getHitsThreshold() != Integer.MAX_VALUE; + if (hitsThresholdChecker.getHitsThreshold() != Integer.MAX_VALUE) { scoreMode = needsScores ? ScoreMode.TOP_DOCS_WITH_SCORES : ScoreMode.TOP_DOCS; } else { scoreMode = needsScores ? ScoreMode.COMPLETE : ScoreMode.COMPLETE_NO_SCORES; @@ -475,7 +474,7 @@ static TopFieldCollector create(Sort sort, int numHits, FieldDoc after, // here we assume that if hitsThreshold was set, we let a comparator to skip non-competitive docs boolean filterNonCompetitiveDocs = hitsThresholdChecker.getHitsThreshold() == Integer.MAX_VALUE ? false : true; - FieldValueHitQueue queue = FieldValueHitQueue.create(sort.fields, numHits, filterNonCompetitiveDocs, after != null); + FieldValueHitQueue queue = FieldValueHitQueue.create(sort.fields, numHits, filterNonCompetitiveDocs); if (after == null) { return new SimpleFieldCollector(sort, queue, numHits, hitsThresholdChecker, minScoreAcc); diff --git a/lucene/core/src/java/org/apache/lucene/search/comparators/DocComparator.java b/lucene/core/src/java/org/apache/lucene/search/comparators/DocComparator.java new file mode 100644 index 000000000000..c8656219eaa8 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/comparators/DocComparator.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.search.comparators; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.FilteringLeafFieldComparator; +import org.apache.lucene.search.LeafFieldComparator; +import org.apache.lucene.search.Scorable; + +import java.io.IOException; + +/** + * Comparator that sorts by asc _doc + */ +public class DocComparator extends FieldComparator { + private final int[] docIDs; + private int topValue; + private boolean topValueSet; + private boolean reverse = false; // only used to check if skipping functionality should be enabled + + /** Creates a new comparator based on document ids for {@code numHits} */ + public DocComparator(int numHits) { + docIDs = new int[numHits]; + } + + @Override + public int compare(int slot1, int slot2) { + // No overflow risk because docIDs are non-negative + return docIDs[slot1] - docIDs[slot2]; + } + + + @Override + public LeafFieldComparator getLeafComparator(LeafReaderContext context) { + // TODO: can we "map" our docIDs to the current + // reader? saves having to then subtract on every + // compare call + return new DocLeafComparator(context); + } + + @Override + public void setTopValue(Integer value) { + topValue = value; + topValueSet = true; + } + + @Override + public Integer value(int slot) { + return Integer.valueOf(docIDs[slot]); + } + + @Override + public void setReverse() { + reverse = true; + } + + + /** + * DocLeafComparator with skipping functionality. + * When sort by _doc asc and "after" document is set, + * the comparator provides an iterator that can quickly skip to the desired "after" document. + */ + private class DocLeafComparator implements FilteringLeafFieldComparator { + private final int docBase; + private int bottom; + + private final boolean enableSkipping; + private final int minDoc; + private final int maxDoc; + private DocIdSetIterator topValueIterator; // iterator that starts from topValue + + private boolean iteratorUpdated = false; + + public DocLeafComparator(LeafReaderContext context) { + this.docBase = context.docBase; + // skipping functionality is enabled if topValue is set and sort is asc + this.enableSkipping = topValueSet && reverse == false ? true: false; + if (enableSkipping) { + this.minDoc = topValue + 1; + this.maxDoc = context.reader().maxDoc(); + this.topValueIterator = DocIdSetIterator.all(maxDoc); + } else { + this.minDoc = -1; + this.maxDoc = -1; + this.topValueIterator = null; + } + } + + @Override + public void setBottom(int slot) { + bottom = docIDs[slot]; + } + + @Override + public int compareBottom(int doc) { + // No overflow risk because docIDs are non-negative + return bottom - (docBase + doc); + } + + @Override + public int compareTop(int doc) { + int docValue = docBase + doc; + return Integer.compare(topValue, docValue); + } + + @Override + public void copy(int slot, int doc) throws IOException { + docIDs[slot] = docBase + doc; + } + + @Override + public void setScorer(Scorable scorer) throws IOException { + } + + @Override + public DocIdSetIterator competitiveIterator() { + if (enableSkipping == false) { + return null; + } else { + return new DocIdSetIterator() { + private int doc; + + @Override + public int nextDoc() throws IOException { + return doc = topValueIterator.nextDoc(); + } + + @Override + public int docID() { + return doc; + } + + @Override + public long cost() { + return topValueIterator.cost(); + } + + @Override + public int advance(int target) throws IOException { + return doc = topValueIterator.advance(target); + } + }; + } + } + + @Override + public void setHitsThresholdReached() { + if (enableSkipping == false) return; + if (iteratorUpdated) return; // iterator for a segment needs to be updated only once + if (docBase + maxDoc <= minDoc) { + topValueIterator = DocIdSetIterator.empty(); // skip this segment + } else { + int segmentMinDoc = Math.max(0, minDoc - docBase); + topValueIterator = new MinDocIterator(segmentMinDoc, maxDoc); + } + iteratorUpdated = true; + } + + @Override + public void setQueueFull() { + } + + @Override + public boolean iteratorUpdated() { + return enableSkipping ? iteratorUpdated : false; + } + } +} diff --git a/lucene/core/src/java/org/apache/lucene/search/comparators/MinDocIterator.java b/lucene/core/src/java/org/apache/lucene/search/comparators/MinDocIterator.java new file mode 100644 index 000000000000..1ac7390e9650 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/comparators/MinDocIterator.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.search.comparators; + +import org.apache.lucene.search.DocIdSetIterator; + +import java.io.IOException; + +/** + * Docs iterator that stats iterating from configurable minimum document + */ +public class MinDocIterator extends DocIdSetIterator { + final int segmentMinDoc; + final int maxDoc; + int doc = -1; + + MinDocIterator(int segmentMinDoc, int maxDoc) { + this.segmentMinDoc = segmentMinDoc; + this.maxDoc = maxDoc; + } + + @Override + public int docID() { + return doc; + } + + @Override + public int nextDoc() throws IOException { + return advance(doc + 1); + } + + @Override + public int advance(int target) throws IOException { + assert target > doc; + if (doc == -1) { + // skip directly to minDoc + doc = Math.max(target, segmentMinDoc); + } else { + doc = target; + } + if (doc >= maxDoc) { + doc = NO_MORE_DOCS; + } + return doc; + } + + @Override + public long cost() { + return maxDoc - segmentMinDoc; + } +} diff --git a/lucene/core/src/test/org/apache/lucene/search/TestFieldSortOptimizationSkipping.java b/lucene/core/src/test/org/apache/lucene/search/TestFieldSortOptimizationSkipping.java index b222a9645746..7a1deaa68148 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestFieldSortOptimizationSkipping.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestFieldSortOptimizationSkipping.java @@ -294,16 +294,19 @@ public void testFloatSortOptimization() throws IOException { public void testDocSortOptimizationWithAfter() throws IOException { final Directory dir = newDirectory(); final IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig()); - final int numDocs = atLeast(1000); + final int numDocs = atLeast(1500); for (int i = 0; i < numDocs; ++i) { final Document doc = new Document(); writer.addDocument(doc); + if ((i > 0) && (i % 500 == 0)) { + writer.commit(); + } } final IndexReader reader = DirectoryReader.open(writer); IndexSearcher searcher = new IndexSearcher(reader); final int numHits = 3; final int totalHitsThreshold = 3; - final int searchAfter = 990; + final int searchAfter = 1400; // sort by _doc with search after should trigger optimization { @@ -346,9 +349,14 @@ public void testDocSortOptimizationWithAfter() throws IOException { final TopFieldCollector collector = TopFieldCollector.create(sort, numHits, after, totalHitsThreshold); searcher.search(new MatchAllDocsQuery(), collector); TopDocs topDocs = collector.topDocs(); + for (int i = 0; i < numHits; i++) { + int expectedDocID = searchAfter - 1 - i; + assertEquals(expectedDocID, topDocs.scoreDocs[i].doc); + } assertEquals(topDocs.scoreDocs.length, numHits); // assert that many hits were collected including all hits before searchAfter assertTrue(topDocs.totalHits.value > searchAfter); + } writer.close(); @@ -356,5 +364,4 @@ public void testDocSortOptimizationWithAfter() throws IOException { dir.close(); } - }