LUCENE-8103: Use TwoPhaseIterator in DoubleValuesSource and QueryValueSource

Fixes #1343
This commit is contained in:
Michele Palmia 2020-03-15 11:50:07 -04:00 committed by David Smiley
parent bd16620706
commit 87b1bddf1c
5 changed files with 60 additions and 88 deletions

View File

@ -122,6 +122,9 @@ Optimizations
* LUCENE-9254: UniformSplit keeps FST off-heap. (Bruno Roustant)
* LUCENE-8103: DoubleValuesSource and QueryValueSource now use a TwoPhaseIterator if one is provided by the Query.
(Michele Palmia, David Smiley)
Bug Fixes
---------------------
* LUCENE-9259: Fix wrong NGramFilterFactory argument name for preserveOriginal option (Paul Pazderski)

View File

@ -604,8 +604,11 @@ public abstract class DoubleValuesSource implements SegmentCacheable {
Scorer scorer = weight.scorer(ctx);
if (scorer == null)
return DoubleValues.EMPTY;
DocIdSetIterator it = scorer.iterator();
return new DoubleValues() {
private final TwoPhaseIterator tpi = scorer.twoPhaseIterator();
private final DocIdSetIterator disi = (tpi == null) ? scorer.iterator() : tpi.approximation();
@Override
public double doubleValue() throws IOException {
return scorer.score();
@ -613,9 +616,10 @@ public abstract class DoubleValuesSource implements SegmentCacheable {
@Override
public boolean advanceExact(int doc) throws IOException {
if (it.docID() > doc)
return false;
return it.docID() == doc || it.advance(doc) == doc;
if (disi.docID() < doc) {
disi.advance(doc);
}
return disi.docID() == doc && (tpi == null || tpi.matches());
}
};
}

View File

@ -274,7 +274,17 @@ public class TestDoubleValuesSource extends LuceneTestCase {
}
public void testQueryDoubleValuesSource() throws Exception {
Query q = new TermQuery(new Term("english", "two"));
Query iteratingQuery = new TermQuery(new Term("english", "two"));
Query approximatingQuery = new PhraseQuery.Builder()
.add(new Term("english", "hundred"), 0)
.add(new Term("english", "one"), 1)
.build();
doTestQueryDoubleValuesSources(iteratingQuery);
doTestQueryDoubleValuesSources(approximatingQuery);
}
private void doTestQueryDoubleValuesSources(Query q) throws Exception {
DoubleValuesSource vs = DoubleValuesSource.fromQuery(q).rewrite(searcher);
searcher.search(q, new SimpleCollector() {

View File

@ -29,6 +29,7 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.TwoPhaseIterator;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.mutable.MutableValue;
import org.apache.lucene.util.mutable.MutableValueFloat;
@ -87,14 +88,13 @@ class QueryDocValues extends FloatDocValues {
final Query q;
Scorer scorer;
DocIdSetIterator it;
int scorerDoc; // the document the scorer is on
boolean noMatches=false;
DocIdSetIterator disi;
TwoPhaseIterator tpi;
Boolean thisDocMatches;
// the last document requested
int lastDocRequested=-1;
// the last document requested... start off with high value
// to trigger a scorer reset on first access.
int lastDocRequested=Integer.MAX_VALUE;
public QueryDocValues(QueryValueSource vs, LeafReaderContext readerContext, Map fcontext) throws IOException {
super(vs);
@ -124,30 +124,7 @@ class QueryDocValues extends FloatDocValues {
@Override
public float floatVal(int doc) {
try {
if (doc < lastDocRequested) {
if (noMatches) return defVal;
scorer = weight.scorer(readerContext);
if (scorer==null) {
noMatches = true;
return defVal;
}
it = scorer.iterator();
scorerDoc = -1;
}
lastDocRequested = doc;
if (scorerDoc < doc) {
scorerDoc = it.advance(doc);
}
if (scorerDoc > doc) {
// query doesn't match this document... either because we hit the
// end, or because the next doc is after this doc.
return defVal;
}
// a match!
return scorer.score();
return exists(doc) ? scorer.score() : defVal;
} catch (IOException e) {
throw new RuntimeException("caught exception in QueryDocVals("+q+") doc="+doc, e);
}
@ -155,31 +132,33 @@ class QueryDocValues extends FloatDocValues {
@Override
public boolean exists(int doc) {
if (doc < lastDocRequested) {
throw new IllegalArgumentException("docs were sent out-of-order: lastDocID=" + lastDocRequested + " vs docID=" + doc);
}
lastDocRequested = doc;
try {
if (doc < lastDocRequested) {
if (noMatches) return false;
if (disi == null) {
scorer = weight.scorer(readerContext);
scorerDoc = -1;
if (scorer==null) {
noMatches = true;
return false;
if (scorer == null) {
disi = DocIdSetIterator.empty();
} else {
tpi = scorer.twoPhaseIterator();
disi = tpi == null ? scorer.iterator() : tpi.approximation();
}
it = scorer.iterator();
}
lastDocRequested = doc;
if (scorerDoc < doc) {
scorerDoc = it.advance(doc);
thisDocMatches = null;
}
if (scorerDoc > doc) {
// query doesn't match this document... either because we hit the
// end, or because the next doc is after this doc.
return false;
if (disi.docID() < doc) {
disi.advance(doc);
thisDocMatches = null;
}
// a match!
return true;
if (disi.docID() == doc) {
if (thisDocMatches == null) {
thisDocMatches = tpi == null || tpi.matches();
}
return thisDocMatches;
} else return false;
} catch (IOException e) {
throw new RuntimeException("caught exception in QueryDocVals("+q+") doc="+doc, e);
}
@ -187,11 +166,7 @@ class QueryDocValues extends FloatDocValues {
@Override
public Object objectVal(int doc) {
try {
return exists(doc) ? scorer.score() : null;
} catch (IOException e) {
throw new RuntimeException("caught exception in QueryDocVals("+q+") doc="+doc, e);
}
return floatVal(doc);
}
@Override
@ -212,37 +187,13 @@ class QueryDocValues extends FloatDocValues {
@Override
public void fillValue(int doc) {
try {
if (noMatches) {
if (exists(doc)) {
mval.value = scorer.score();
mval.exists = true;
} else {
mval.value = defVal;
mval.exists = false;
return;
}
scorer = weight.scorer(readerContext);
scorerDoc = -1;
if (scorer==null) {
noMatches = true;
mval.value = defVal;
mval.exists = false;
return;
}
it = scorer.iterator();
lastDocRequested = doc;
if (scorerDoc < doc) {
scorerDoc = it.advance(doc);
}
if (scorerDoc > doc) {
// query doesn't match this document... either because we hit the
// end, or because the next doc is after this doc.
mval.value = defVal;
mval.exists = false;
return;
}
// a match!
mval.value = scorer.score();
mval.exists = true;
} catch (IOException e) {
throw new RuntimeException("caught exception in QueryDocVals("+q+") doc="+doc, e);
}

View File

@ -379,6 +379,10 @@ public class TestValueSources extends LuceneTestCase {
ValueSource vs = new QueryValueSource(new FunctionQuery(new ConstValueSource(2f)), 0f);
assertHits(new FunctionQuery(vs), new float[] { 2f, 2f });
assertAllExist(vs);
vs = new QueryValueSource(new FunctionRangeQuery(new IntFieldSource("int"), Integer.MIN_VALUE, Integer.MAX_VALUE, true, true), 0f);
assertHits(new FunctionQuery(vs), new float[] { 35f, 54f });
assertAllExist(vs);
}
public void testQuery() throws Exception {