LUCENE-7847: Fix the all-docs-match optimization of range queries on range fields.

This commit is contained in:
Adrien Grand 2017-05-23 18:37:01 +02:00
parent 31e02e93a5
commit 14320a584c
8 changed files with 104 additions and 45 deletions

View File

@ -155,6 +155,9 @@ Bug Fixes
* LUCENE-7833: ToParentBlockJoinQuery computed the min score instead of the max
score with ScoreMode.MAX. (Adrien Grand)
* LUCENE-7847: Fixed all-docs-match optimization of range queries on range
fields. (Adrien Grand)
Improvements
* LUCENE-7782: OfflineSorter now passes the total number of items it

View File

@ -112,6 +112,7 @@ abstract class RangeFieldQuery extends Query {
public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
return new ConstantScoreWeight(this, boost) {
final RangeFieldComparator target = new RangeFieldComparator();
private DocIdSet buildMatchingDocIdSet(LeafReader reader, PointValues values) throws IOException {
DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
values.intersect(
@ -133,25 +134,29 @@ abstract class RangeFieldQuery extends Query {
}
@Override
public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
byte[] node = getInternalRange(minPackedValue, maxPackedValue);
// compute range relation for BKD traversal
if (target.intersects(node) == false) {
return Relation.CELL_OUTSIDE_QUERY;
} else if (target.within(node)) {
// target within cell; continue traversing:
return Relation.CELL_CROSSES_QUERY;
} else if (target.contains(node)) {
// target contains cell; add iff queryType is not a CONTAINS or CROSSES query:
return (queryType == QueryType.CONTAINS || queryType == QueryType.CROSSES) ?
Relation.CELL_OUTSIDE_QUERY : Relation.CELL_INSIDE_QUERY;
}
// target intersects cell; continue traversing:
return Relation.CELL_CROSSES_QUERY;
return compareRange(minPackedValue, maxPackedValue);
}
});
return result.build();
}
private Relation compareRange(byte[] minPackedValue, byte[] maxPackedValue) {
byte[] node = getInternalRange(minPackedValue, maxPackedValue);
// compute range relation for BKD traversal
if (target.intersects(node) == false) {
return Relation.CELL_OUTSIDE_QUERY;
} else if (target.within(node)) {
// target within cell; continue traversing:
return Relation.CELL_CROSSES_QUERY;
} else if (target.contains(node)) {
// target contains cell; add iff queryType is not a CONTAINS or CROSSES query:
return (queryType == QueryType.CONTAINS || queryType == QueryType.CROSSES) ?
Relation.CELL_OUTSIDE_QUERY : Relation.CELL_INSIDE_QUERY;
}
// target intersects cell; continue traversing:
return Relation.CELL_CROSSES_QUERY;
}
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
LeafReader reader = context.reader();
@ -166,17 +171,10 @@ abstract class RangeFieldQuery extends Query {
return null;
}
checkFieldInfo(fieldInfo);
boolean allDocsMatch = true;
if (values.getDocCount() == reader.maxDoc()) {
// if query crosses, docs need to be further scrutinized
byte[] range = getInternalRange(values.getMinPackedValue(), values.getMaxPackedValue());
// if the internal node is not equal and not contained by the query, all docs do not match
if (queryType == QueryType.CROSSES || (!Arrays.equals(ranges, range)
&& (target.contains(range) == false || queryType != QueryType.WITHIN))) {
allDocsMatch = false;
}
} else {
allDocsMatch = false;
boolean allDocsMatch = false;
if (values.getDocCount() == reader.maxDoc()
&& compareRange(values.getMinPackedValue(), values.getMaxPackedValue()) == Relation.CELL_INSIDE_QUERY) {
allDocsMatch = true;
}
DocIdSetIterator iterator = allDocsMatch == true ?

View File

@ -31,11 +31,18 @@ public class TestDoubleRangeFieldQueries extends BaseRangeFieldQueryTestCase {
private static final String FIELD_NAME = "doubleRangeField";
private double nextDoubleInternal() {
if (rarely()) {
return random().nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
switch (random().nextInt(5)) {
case 0:
return Double.NEGATIVE_INFINITY;
case 1:
return Double.POSITIVE_INFINITY;
default:
if (random().nextBoolean()) {
return random().nextDouble();
} else {
return (random().nextInt(15) - 7) / 3d;
}
}
double max = Double.MAX_VALUE / 2;
return (max + max) * random().nextDouble() - max;
}
@Override

View File

@ -31,11 +31,18 @@ public class TestFloatRangeFieldQueries extends BaseRangeFieldQueryTestCase {
private static final String FIELD_NAME = "floatRangeField";
private float nextFloatInternal() {
if (rarely()) {
return random().nextBoolean() ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
switch (random().nextInt(5)) {
case 0:
return Float.NEGATIVE_INFINITY;
case 1:
return Float.POSITIVE_INFINITY;
default:
if (random().nextBoolean()) {
return random().nextFloat();
} else {
return (random().nextInt(15) - 7) / 3f;
}
}
float max = Float.MAX_VALUE / 2;
return (max + max) * random().nextFloat() - max;
}
@Override

View File

@ -23,6 +23,7 @@ import org.apache.lucene.document.IntRange;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.TestUtil;
/**
* Random testing for IntRange Queries.
@ -31,11 +32,25 @@ public class TestIntRangeFieldQueries extends BaseRangeFieldQueryTestCase {
private static final String FIELD_NAME = "intRangeField";
private int nextIntInternal() {
if (rarely()) {
return random().nextBoolean() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
switch (random().nextInt(5)) {
case 0:
return Integer.MIN_VALUE;
case 1:
return Integer.MAX_VALUE;
default:
int bpv = random().nextInt(32);
switch (bpv) {
case 32:
return random().nextInt();
default:
int v = TestUtil.nextInt(random(), 0, (1 << bpv) - 1);
if (bpv > 0) {
// negative values sometimes
v -= 1 << (bpv - 1);
}
return v;
}
}
int max = Integer.MAX_VALUE / 2;
return (max + max) * random().nextInt() - max;
}
@Override

View File

@ -23,6 +23,7 @@ import org.apache.lucene.document.LongRange;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.TestUtil;
/**
* Random testing for LongRange Queries.
@ -31,11 +32,25 @@ public class TestLongRangeFieldQueries extends BaseRangeFieldQueryTestCase {
private static final String FIELD_NAME = "longRangeField";
private long nextLongInternal() {
if (rarely()) {
return random().nextBoolean() ? Long.MAX_VALUE : Long.MIN_VALUE;
switch (random().nextInt(5)) {
case 0:
return Long.MIN_VALUE;
case 1:
return Long.MAX_VALUE;
default:
int bpv = random().nextInt(64);
switch (bpv) {
case 64:
return random().nextLong();
default:
long v = TestUtil.nextLong(random(), 0, (1L << bpv) - 1);
if (bpv > 0) {
// negative values sometimes
v -= 1L << (bpv - 1);
}
return v;
}
}
long max = Long.MAX_VALUE / 2;
return (max + max) * random().nextLong() - max;
}
@Override

View File

@ -18,6 +18,7 @@ package org.apache.lucene.search;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import org.apache.lucene.document.InetAddressPoint;
import org.apache.lucene.document.InetAddressRange;
@ -44,8 +45,19 @@ public class TestInetAddressRangeQueries extends BaseRangeFieldQueryTestCase {
/** return random IPv4 or IPv6 address */
private InetAddress nextInetaddress() throws UnknownHostException {
byte[] b = random().nextBoolean() ? new byte[4] : new byte[16];
random().nextBytes(b);
return InetAddress.getByAddress(b);
switch (random().nextInt(5)) {
case 0:
return InetAddress.getByAddress(b);
case 1:
Arrays.fill(b, (byte) 0xff);
return InetAddress.getByAddress(b);
case 2:
Arrays.fill(b, (byte) 42);
return InetAddress.getByAddress(b);
default:
random().nextBytes(b);
return InetAddress.getByAddress(b);
}
}
@Override
@ -159,7 +171,7 @@ public class TestInetAddressRangeQueries extends BaseRangeFieldQueryTestCase {
@Override
protected boolean isEqual(Range o) {
IpRange other = (IpRange)o;
return this.min.equals(other.min) && this.max.equals(other.max);
return Arrays.equals(min, other.min) && Arrays.equals(max, other.max);
}
@Override

View File

@ -61,7 +61,9 @@ public abstract class BaseRangeFieldQueryTestCase extends LuceneTestCase {
public void testRandomTiny() throws Exception {
// Make sure single-leaf-node case is OK:
doTestRandom(10, false);
for (int i = 0; i < 10; ++i) {
doTestRandom(10, false);
}
}
public void testRandomMedium() throws Exception {