mirror of https://github.com/apache/lucene.git
LUCENE-8769: Introduce Range Query Type With Multiple Ranges
Currently, multiple ranges need to be specified in different PointRangeQueries, thus leading to performance implications when the BKD tree is deep, since each range query will need a traversal. This commit introduces a new range query type which has multiple ranges logically connected. All ranges are logically connected by OR operators.
This commit is contained in:
parent
16ec64f7b2
commit
94c76c790c
|
@ -44,7 +44,6 @@ import org.apache.lucene.util.NumericUtils;
|
|||
* @see PointValues
|
||||
*/
|
||||
public final class DoublePoint extends Field {
|
||||
|
||||
/**
|
||||
* Return the least double that compares greater than {@code d} consistently
|
||||
* with {@link Double#compare}. The only difference with
|
||||
|
@ -106,7 +105,13 @@ public final class DoublePoint extends Field {
|
|||
return decodeDimension(bytes.bytes, bytes.offset);
|
||||
}
|
||||
|
||||
private static BytesRef pack(double... point) {
|
||||
/**
|
||||
* Pack a double point into a BytesRef
|
||||
*
|
||||
* @param point double[] value
|
||||
* @throws IllegalArgumentException is the value is null or of zero length
|
||||
*/
|
||||
public static BytesRef pack(double... point) {
|
||||
if (point == null) {
|
||||
throw new IllegalArgumentException("point must not be null");
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.apache.lucene.util.NumericUtils;
|
|||
* @see PointValues
|
||||
*/
|
||||
public final class FloatPoint extends Field {
|
||||
|
||||
/**
|
||||
* Return the least float that compares greater than {@code f} consistently
|
||||
* with {@link Float#compare}. The only difference with
|
||||
|
@ -106,7 +105,13 @@ public final class FloatPoint extends Field {
|
|||
return decodeDimension(bytes.bytes, bytes.offset);
|
||||
}
|
||||
|
||||
private static BytesRef pack(float... point) {
|
||||
/**
|
||||
* Pack a float point into a BytesRef
|
||||
*
|
||||
* @param point float[] value
|
||||
* @throws IllegalArgumentException is the value is null or of zero length
|
||||
*/
|
||||
public static BytesRef pack(float... point) {
|
||||
if (point == null) {
|
||||
throw new IllegalArgumentException("point must not be null");
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.apache.lucene.util.NumericUtils;
|
|||
* @see PointValues
|
||||
*/
|
||||
public final class IntPoint extends Field {
|
||||
|
||||
private static FieldType getType(int numDims) {
|
||||
FieldType type = new FieldType();
|
||||
type.setDimensions(numDims, Integer.BYTES);
|
||||
|
@ -80,7 +79,13 @@ public final class IntPoint extends Field {
|
|||
return decodeDimension(bytes.bytes, bytes.offset);
|
||||
}
|
||||
|
||||
private static BytesRef pack(int... point) {
|
||||
/**
|
||||
* Pack an integer point into a BytesRef
|
||||
*
|
||||
* @param point int[] value
|
||||
* @throws IllegalArgumentException is the value is null or of zero length
|
||||
*/
|
||||
public static BytesRef pack(int... point) {
|
||||
if (point == null) {
|
||||
throw new IllegalArgumentException("point must not be null");
|
||||
}
|
||||
|
|
|
@ -47,7 +47,6 @@ import org.apache.lucene.util.NumericUtils;
|
|||
* @see PointValues
|
||||
*/
|
||||
public final class LongPoint extends Field {
|
||||
|
||||
private static FieldType getType(int numDims) {
|
||||
FieldType type = new FieldType();
|
||||
type.setDimensions(numDims, Long.BYTES);
|
||||
|
@ -83,7 +82,13 @@ public final class LongPoint extends Field {
|
|||
return decodeDimension(bytes.bytes, bytes.offset);
|
||||
}
|
||||
|
||||
private static BytesRef pack(long... point) {
|
||||
/**
|
||||
* Pack a long point into a BytesRef
|
||||
*
|
||||
* @param point long[] value
|
||||
* @throws IllegalArgumentException is the value is null or of zero length
|
||||
*/
|
||||
public static BytesRef pack(long... point) {
|
||||
if (point == null) {
|
||||
throw new IllegalArgumentException("point must not be null");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.document;
|
||||
|
||||
import org.apache.lucene.search.MultiRangeQuery;
|
||||
|
||||
import static org.apache.lucene.document.DoublePoint.decodeDimension;
|
||||
import static org.apache.lucene.document.DoublePoint.pack;
|
||||
|
||||
/** Builder for multi range queries for DoublePoints */
|
||||
public class DoublePointMultiRangeBuilder extends MultiRangeQuery.Builder {
|
||||
public DoublePointMultiRangeBuilder(String field, int numDims) {
|
||||
super(field, Double.BYTES, numDims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiRangeQuery build() {
|
||||
return new MultiRangeQuery(field, numDims, bytesPerDim, clauses) {
|
||||
@Override
|
||||
protected String toString(int dimension, byte[] value) {
|
||||
return Double.toString(decodeDimension(value, 0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void add(double[] lowerValue, double[] upperValue) {
|
||||
if (upperValue.length != numDims || lowerValue.length != numDims) {
|
||||
throw new IllegalArgumentException("Passed in range does not conform to specified dimensions");
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
if (upperValue[i] < lowerValue[i]) {
|
||||
throw new IllegalArgumentException("Upper value of range should be greater than lower value of range");
|
||||
}
|
||||
}
|
||||
add(pack(lowerValue).bytes, pack(upperValue).bytes);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.document;
|
||||
|
||||
import org.apache.lucene.search.MultiRangeQuery;
|
||||
|
||||
import static org.apache.lucene.document.FloatPoint.decodeDimension;
|
||||
import static org.apache.lucene.document.FloatPoint.pack;
|
||||
|
||||
/**
|
||||
* Builder for multi range queries for FloatPoints
|
||||
*/
|
||||
public class FloatPointMultiRangeBuilder extends MultiRangeQuery.Builder {
|
||||
public FloatPointMultiRangeBuilder(String field, int numDims) {
|
||||
super(field, Float.BYTES, numDims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiRangeQuery build() {
|
||||
return new MultiRangeQuery(field, numDims, bytesPerDim, clauses) {
|
||||
@Override
|
||||
protected String toString(int dimension, byte[] value) {
|
||||
return Float.toString(decodeDimension(value, 0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void add(float[] lowerValue, float[] upperValue) {
|
||||
if (upperValue.length != numDims || lowerValue.length != numDims) {
|
||||
throw new IllegalArgumentException("Passed in range does not conform to specified dimensions");
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
if (upperValue[i] < lowerValue[i]) {
|
||||
throw new IllegalArgumentException("Upper value of range should be greater than lower value of range");
|
||||
}
|
||||
}
|
||||
add(pack(lowerValue).bytes, pack(upperValue).bytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.document;
|
||||
|
||||
import org.apache.lucene.search.MultiRangeQuery;
|
||||
|
||||
import static org.apache.lucene.document.IntPoint.decodeDimension;
|
||||
import static org.apache.lucene.document.IntPoint.pack;
|
||||
|
||||
/** Builder for multi range queries for IntPoints */
|
||||
public class IntPointMultiRangeBuilder extends MultiRangeQuery.Builder {
|
||||
public IntPointMultiRangeBuilder(String field, int numDims) {
|
||||
super(field, Integer.BYTES, numDims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiRangeQuery build() {
|
||||
return new MultiRangeQuery(field, numDims, bytesPerDim, clauses) {
|
||||
@Override
|
||||
protected String toString(int dimension, byte[] value) {
|
||||
return Integer.toString(decodeDimension(value, 0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void add(int[] lowerValue, int[] upperValue) {
|
||||
if (upperValue.length != numDims || lowerValue.length != numDims) {
|
||||
throw new IllegalArgumentException("Passed in range does not conform to specified dimensions");
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
if (upperValue[i] < lowerValue[i]) {
|
||||
throw new IllegalArgumentException("Upper value of range should be greater than lower value of range");
|
||||
}
|
||||
}
|
||||
add(pack(lowerValue).bytes, pack(upperValue).bytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.document;
|
||||
|
||||
import org.apache.lucene.search.MultiRangeQuery;
|
||||
|
||||
import static org.apache.lucene.document.LongPoint.decodeDimension;
|
||||
import static org.apache.lucene.document.LongPoint.pack;
|
||||
|
||||
/** Builder for multi range queries for LongPoints */
|
||||
public class LongPointMultiRangeBuilder extends MultiRangeQuery.Builder {
|
||||
public LongPointMultiRangeBuilder(String field, int numDims) {
|
||||
super(field, Long.BYTES, numDims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiRangeQuery build() {
|
||||
return new MultiRangeQuery(field, numDims, bytesPerDim, clauses) {
|
||||
@Override
|
||||
protected String toString(int dimension, byte[] value) {
|
||||
return Long.toString(decodeDimension(value, 0));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void add(long[] lowerValue, long[] upperValue) {
|
||||
if (upperValue.length != numDims || lowerValue.length != numDims) {
|
||||
throw new IllegalArgumentException("Passed in range does not conform to specified dimensions");
|
||||
}
|
||||
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
if (upperValue[i] < lowerValue[i]) {
|
||||
throw new IllegalArgumentException("Upper value of range should be greater than lower value of range");
|
||||
}
|
||||
}
|
||||
add(pack(lowerValue).bytes, pack(upperValue).bytes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.PointValues;
|
||||
import org.apache.lucene.util.ArrayUtil;
|
||||
import org.apache.lucene.util.DocIdSetBuilder;
|
||||
|
||||
/**
|
||||
* Abstract class for range queries involving multiple ranges against physical points such as {@code IntPoints}
|
||||
* All ranges are logically ORed together
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public abstract class MultiRangeQuery extends Query {
|
||||
/**
|
||||
* Representation of a single clause in a MultiRangeQuery
|
||||
*/
|
||||
public static class RangeClause {
|
||||
byte[] lowerValue;
|
||||
byte[] upperValue;
|
||||
|
||||
public RangeClause(byte[] lowerValue, byte[] upperValue) {
|
||||
this.lowerValue = lowerValue;
|
||||
this.upperValue = upperValue;
|
||||
}
|
||||
}
|
||||
|
||||
/** A builder for multirange queries. */
|
||||
public static abstract class Builder {
|
||||
|
||||
protected final String field;
|
||||
protected final int bytesPerDim;
|
||||
protected final int numDims;
|
||||
protected final List<RangeClause> clauses = new ArrayList<>();
|
||||
|
||||
/** Sole constructor. */
|
||||
public Builder(String field, int bytesPerDim, int numDims) {
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("field should not be null");
|
||||
}
|
||||
if (bytesPerDim <= 0) {
|
||||
throw new IllegalArgumentException("bytesPerDim should be a valid value");
|
||||
}
|
||||
if (numDims <= 0) {
|
||||
throw new IllegalArgumentException("numDims should be a valid value");
|
||||
}
|
||||
|
||||
this.field = field;
|
||||
this.bytesPerDim = bytesPerDim;
|
||||
this.numDims = numDims;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new clause to this {@link Builder}.
|
||||
*/
|
||||
public Builder add(RangeClause clause) {
|
||||
clauses.add(clause);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new clause to this {@link Builder}.
|
||||
*/
|
||||
public Builder add(byte[] lowerValue, byte[] upperValue) {
|
||||
checkArgs(lowerValue, upperValue);
|
||||
return add(new RangeClause(lowerValue, upperValue));
|
||||
}
|
||||
|
||||
/** Create a new {@link MultiRangeQuery} based on the parameters that have
|
||||
* been set on this builder. */
|
||||
public abstract MultiRangeQuery build();
|
||||
|
||||
/**
|
||||
* Check preconditions for all factory methods
|
||||
* @throws IllegalArgumentException if {@code field}, {@code lowerPoint} or {@code upperPoint} are null.
|
||||
*/
|
||||
private void checkArgs(Object lowerPoint, Object upperPoint) {
|
||||
if (lowerPoint == null) {
|
||||
throw new IllegalArgumentException("lowerPoint must not be null");
|
||||
}
|
||||
if (upperPoint == null) {
|
||||
throw new IllegalArgumentException("upperPoint must not be null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String field;
|
||||
final int numDims;
|
||||
final int bytesPerDim;
|
||||
final List<RangeClause> rangeClauses;
|
||||
/**
|
||||
* Expert: create a multidimensional range query with multiple connected ranges
|
||||
*
|
||||
* @param field field name. must not be {@code null}.
|
||||
* @param rangeClauses Range Clauses for this query
|
||||
* @param numDims number of dimensions.
|
||||
*/
|
||||
protected MultiRangeQuery(String field, int numDims, int bytesPerDim, List<RangeClause> rangeClauses) {
|
||||
this.field = field;
|
||||
this.numDims = numDims;
|
||||
this.bytesPerDim = bytesPerDim;
|
||||
this.rangeClauses = rangeClauses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(QueryVisitor visitor) {
|
||||
if (visitor.acceptField(field)) {
|
||||
visitor.visitLeaf(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
|
||||
|
||||
// We don't use RandomAccessWeight here: it's no good to approximate with "match all docs".
|
||||
// This is an inverted structure and should be used in the first pass:
|
||||
|
||||
return new ConstantScoreWeight(this, boost) {
|
||||
|
||||
private PointValues.IntersectVisitor getIntersectVisitor(DocIdSetBuilder result) {
|
||||
return new PointValues.IntersectVisitor() {
|
||||
|
||||
DocIdSetBuilder.BulkAdder adder;
|
||||
|
||||
@Override
|
||||
public void grow(int count) {
|
||||
adder = result.grow(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int docID) {
|
||||
adder.add(docID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int docID, byte[] packedValue) {
|
||||
// If a single OR clause has the value in range, the entire query accepts the value
|
||||
for (RangeClause rangeClause : rangeClauses) {
|
||||
for (int dim = 0; dim < numDims; dim++) {
|
||||
int offset = dim * bytesPerDim;
|
||||
if ((Arrays.compareUnsigned(packedValue, offset, offset + bytesPerDim, rangeClause.lowerValue, offset, offset + bytesPerDim) >= 0) &&
|
||||
(Arrays.compareUnsigned(packedValue, offset, offset + bytesPerDim, rangeClause.upperValue, offset, offset + bytesPerDim) <= 0)) {
|
||||
// Doc is in-bounds. Add and short circuit
|
||||
adder.add(docID);
|
||||
return;
|
||||
}
|
||||
// Iterate till we have any OR clauses remaining
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PointValues.Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
|
||||
|
||||
boolean crosses = false;
|
||||
|
||||
/**
|
||||
* CROSSES and INSIDE take priority over OUTSIDE. How we calculate the position is:
|
||||
* 1) If any range sees the point as inside, return INSIDE.
|
||||
* 2) If no range sees the point as inside and atleast one range sees the point as CROSSES, return CROSSES
|
||||
* 3) If none of the above, return OUTSIDE
|
||||
*/
|
||||
for (RangeClause rangeClause : rangeClauses) {
|
||||
for (int dim = 0; dim < numDims; dim++) {
|
||||
int offset = dim * bytesPerDim;
|
||||
|
||||
if ((Arrays.compareUnsigned(minPackedValue, offset, offset + bytesPerDim, rangeClause.lowerValue, offset, offset + bytesPerDim) >= 0) &&
|
||||
(Arrays.compareUnsigned(maxPackedValue, offset, offset + bytesPerDim, rangeClause.upperValue, offset, offset + bytesPerDim) <= 0)) {
|
||||
return PointValues.Relation.CELL_INSIDE_QUERY;
|
||||
}
|
||||
|
||||
crosses |= Arrays.compareUnsigned(minPackedValue, offset, offset + bytesPerDim, rangeClause.lowerValue, offset, offset + bytesPerDim) < 0 ||
|
||||
Arrays.compareUnsigned(maxPackedValue, offset, offset + bytesPerDim, rangeClause.upperValue, offset, offset + bytesPerDim) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (crosses) {
|
||||
return PointValues.Relation.CELL_CROSSES_QUERY;
|
||||
} else {
|
||||
return PointValues.Relation.CELL_OUTSIDE_QUERY;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException {
|
||||
LeafReader reader = context.reader();
|
||||
|
||||
PointValues values = reader.getPointValues(field);
|
||||
if (values == null) {
|
||||
// No docs in this segment/field indexed any points
|
||||
return null;
|
||||
}
|
||||
|
||||
if (values.getNumIndexDimensions() != numDims) {
|
||||
throw new IllegalArgumentException("field=\"" + field + "\" was indexed with numIndexDimensions=" + values.getNumIndexDimensions() + " but this query has numDims=" + numDims);
|
||||
}
|
||||
if (bytesPerDim != values.getBytesPerDimension()) {
|
||||
throw new IllegalArgumentException("field=\"" + field + "\" was indexed with bytesPerDim=" + values.getBytesPerDimension() + " but this query has bytesPerDim=" + bytesPerDim);
|
||||
}
|
||||
|
||||
boolean allDocsMatch;
|
||||
if (values.getDocCount() == reader.maxDoc()) {
|
||||
final byte[] fieldPackedLower = values.getMinPackedValue();
|
||||
final byte[] fieldPackedUpper = values.getMaxPackedValue();
|
||||
allDocsMatch = true;
|
||||
for (RangeClause rangeClause : rangeClauses) {
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
int offset = i * bytesPerDim;
|
||||
if (Arrays.compareUnsigned(rangeClause.lowerValue, offset, offset + bytesPerDim, fieldPackedLower, offset, offset + bytesPerDim) > 0
|
||||
|| Arrays.compareUnsigned(rangeClause.upperValue, offset, offset + bytesPerDim, fieldPackedUpper, offset, offset + bytesPerDim) < 0) {
|
||||
allDocsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
allDocsMatch = false;
|
||||
}
|
||||
|
||||
final Weight weight = this;
|
||||
if (allDocsMatch) {
|
||||
// all docs have a value and all points are within bounds, so everything matches
|
||||
return new ScorerSupplier() {
|
||||
@Override
|
||||
public Scorer get(long leadCost) {
|
||||
return new ConstantScoreScorer(weight, score(), scoreMode, DocIdSetIterator.all(reader.maxDoc()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cost() {
|
||||
return reader.maxDoc();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return new ScorerSupplier() {
|
||||
|
||||
final DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
|
||||
final PointValues.IntersectVisitor visitor = getIntersectVisitor(result);
|
||||
long cost = -1;
|
||||
|
||||
@Override
|
||||
public Scorer get(long leadCost) throws IOException {
|
||||
values.intersect(visitor);
|
||||
DocIdSetIterator iterator = result.build().iterator();
|
||||
return new ConstantScoreScorer(weight, score(), scoreMode, iterator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cost() {
|
||||
if (cost == -1) {
|
||||
// Computing the cost may be expensive, so only do it if necessary
|
||||
cost = values.estimatePointCount(visitor) * rangeClauses.size();
|
||||
assert cost >= 0;
|
||||
}
|
||||
return cost;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||
ScorerSupplier scorerSupplier = scorerSupplier(context);
|
||||
if (scorerSupplier == null) {
|
||||
return null;
|
||||
}
|
||||
return scorerSupplier.get(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheable(LeafReaderContext ctx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public int getNumDims() {
|
||||
return numDims;
|
||||
}
|
||||
|
||||
public int getBytesPerDim() {
|
||||
return bytesPerDim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
int hash = classHash();
|
||||
hash = 31 * hash + field.hashCode();
|
||||
for (RangeClause rangeClause : rangeClauses) {
|
||||
hash = 31 * hash + Arrays.hashCode(rangeClause.lowerValue);
|
||||
hash = 31 * hash + Arrays.hashCode(rangeClause.lowerValue);
|
||||
}
|
||||
hash = 31 * hash + numDims;
|
||||
hash = 31 * hash + Objects.hashCode(bytesPerDim);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
return sameClassAs(o) &&
|
||||
equalsTo(getClass().cast(o));
|
||||
}
|
||||
|
||||
private boolean equalsTo(MultiRangeQuery other) {
|
||||
return Objects.equals(field, other.field) &&
|
||||
numDims == other.numDims &&
|
||||
bytesPerDim == other.bytesPerDim &&
|
||||
rangeClauses.equals(other.rangeClauses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString(String field) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (this.field.equals(field) == false) {
|
||||
sb.append(this.field);
|
||||
sb.append(':');
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
// print ourselves as "range per dimension per value"
|
||||
for (RangeClause rangeClause : rangeClauses) {
|
||||
if (count > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append('{');
|
||||
for (int i = 0; i < numDims; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
|
||||
int startOffset = bytesPerDim * i;
|
||||
|
||||
sb.append('[');
|
||||
sb.append(toString(i, ArrayUtil.copyOfSubArray(rangeClause.lowerValue, startOffset, startOffset + bytesPerDim)));
|
||||
sb.append(" TO ");
|
||||
sb.append(toString(i, ArrayUtil.copyOfSubArray(rangeClause.upperValue, startOffset, startOffset + bytesPerDim)));
|
||||
sb.append(']');
|
||||
}
|
||||
sb.append('}');
|
||||
++count;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string of a single value in a human-readable format for debugging.
|
||||
* This is used by {@link #toString()}.
|
||||
*
|
||||
* @param dimension dimension of the particular value
|
||||
* @param value single value, never null
|
||||
* @return human readable value for debugging
|
||||
*/
|
||||
protected abstract String toString(int dimension, byte[] value);
|
||||
}
|
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
* 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 java.io.IOException;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.DoublePointMultiRangeBuilder;
|
||||
import org.apache.lucene.document.FloatPoint;
|
||||
import org.apache.lucene.document.FloatPointMultiRangeBuilder;
|
||||
import org.apache.lucene.document.IntPoint;
|
||||
import org.apache.lucene.document.IntPointMultiRangeBuilder;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.LongPointMultiRangeBuilder;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
|
||||
public class TestMultiRangeQueries extends LuceneTestCase {
|
||||
|
||||
public void testDoubleRandomMultiRangeQuery() throws IOException {
|
||||
final int numDims = TestUtil.nextInt(random(), 1, 3);
|
||||
final int numVals = TestUtil.nextInt(random(), 3, 8);
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(random(), dir);
|
||||
Document doc = new Document();
|
||||
double[] value = new double[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
value[i] = TestUtil.nextInt(random(), 1, 10);
|
||||
}
|
||||
doc.add(new DoublePoint("point", value));
|
||||
w.addDocument(doc);
|
||||
IndexReader reader = w.getReader();
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
searcher.setQueryCache(null);
|
||||
DoublePointMultiRangeBuilder builder = new DoublePointMultiRangeBuilder("point", numDims);
|
||||
for (int j = 0;j < numVals; j++) {
|
||||
double[] lowerBound = new double[numDims];
|
||||
double[] upperBound = new double[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
lowerBound[i] = value[i] - random().nextInt(1);
|
||||
upperBound[i] = value[i] + random().nextInt(1);
|
||||
}
|
||||
builder.add(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
Query query = builder.build();
|
||||
searcher.search(query, Integer.MAX_VALUE);
|
||||
|
||||
reader.close();
|
||||
w.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testDoublePointMultiRangeQuery() throws IOException {
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
double[] firstPoint = {112.4, 296.2, 512.7};
|
||||
double[] secondPoint = {219.3, 514.7, 624.2};
|
||||
|
||||
Document doc = new Document();
|
||||
doc.add(new DoublePoint("point", firstPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new DoublePoint("point", secondPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
// One range matches
|
||||
double[] firstLowerRange= {111.3, 294.2, 502.8};
|
||||
double[] firstUpperRange = {117.3, 301.4, 514.5};
|
||||
|
||||
double[] secondLowerRange = {15.3, 4.5, 415.7};
|
||||
double[] secondUpperRange = {200.2, 402.4, 583.6};
|
||||
|
||||
DoublePointMultiRangeBuilder builder = new DoublePointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder.add(firstLowerRange, firstUpperRange);
|
||||
builder.add(secondLowerRange, secondUpperRange);
|
||||
|
||||
Query query = builder.build();
|
||||
|
||||
IndexReader reader = iw.getReader();
|
||||
final IndexSearcher searcher = newSearcher(reader);
|
||||
iw.close();
|
||||
|
||||
assertEquals(searcher.count(query), 1);
|
||||
|
||||
// Both ranges match
|
||||
double[] firstMatchingLowerRange= {111.3, 294.2, 502.4};
|
||||
double[] firstMatchingUpperRange = {117.6, 301.8, 514.2};
|
||||
|
||||
double[] secondMatchingLowerRange = {212.4, 512.3, 415.7};
|
||||
double[] secondMatchingUpperRange = {228.3, 538.7, 647.1};
|
||||
|
||||
DoublePointMultiRangeBuilder builder2 = new DoublePointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder2.add(firstMatchingLowerRange, firstMatchingUpperRange);
|
||||
builder2.add(secondMatchingLowerRange, secondMatchingUpperRange);
|
||||
|
||||
query = builder2.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
// None match
|
||||
double[] nonMatchingFirstRangeLower = {1.3, 3.5, 2.7};
|
||||
double[] nonMatchingFirstRangeUpper = {5.2, 8.3, 7.8};
|
||||
|
||||
double[] nonMatchingSecondRangeLower = {11246.3, 19388.7, 21248.4};
|
||||
double[] nonMatchingSecondRangeUpper = {13242.9, 20214.2, 23236.5};
|
||||
DoublePointMultiRangeBuilder builder3 = new DoublePointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder3.add(nonMatchingFirstRangeLower, nonMatchingFirstRangeUpper);
|
||||
builder3.add(nonMatchingSecondRangeLower, nonMatchingSecondRangeUpper);
|
||||
|
||||
query = builder3.build();
|
||||
|
||||
assertEquals(searcher.count(query), 0);
|
||||
|
||||
// Lower point is equal to a point
|
||||
double[] firstEqualLowerRange= {112.4, 296.2, 512.7};
|
||||
double[] firstEqualUpperRange = {117.6, 301.8, 514.2};
|
||||
|
||||
double[] secondEqualLowerRange = {219.3, 514.7, 624.2};
|
||||
double[] secondEqualUpperRange = {228.3, 538.7, 647.1};
|
||||
|
||||
DoublePointMultiRangeBuilder builder4 = new DoublePointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder4.add(firstEqualLowerRange, firstEqualUpperRange);
|
||||
builder4.add(secondEqualLowerRange, secondEqualUpperRange);
|
||||
|
||||
query = builder4.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
reader.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testLongRandomMultiRangeQuery() throws IOException {
|
||||
final int numDims = TestUtil.nextInt(random(), 1, 3);
|
||||
final int numVals = TestUtil.nextInt(random(), 3, 8);
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(random(), dir);
|
||||
Document doc = new Document();
|
||||
long[] value = new long[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
value[i] = TestUtil.nextLong(random(), 1, 10);
|
||||
}
|
||||
doc.add(new LongPoint("point", value));
|
||||
w.addDocument(doc);
|
||||
IndexReader reader = w.getReader();
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
searcher.setQueryCache(null);
|
||||
LongPointMultiRangeBuilder builder = new LongPointMultiRangeBuilder("point", numDims);
|
||||
for (int j = 0;j < numVals; j++) {
|
||||
long[] lowerBound = new long[numDims];
|
||||
long[] upperBound = new long[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
lowerBound[i] = value[i] - random().nextInt(1);
|
||||
upperBound[i] = value[i] + random().nextInt(1);
|
||||
}
|
||||
builder.add(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
Query query = builder.build();
|
||||
searcher.search(query, Integer.MAX_VALUE);
|
||||
|
||||
reader.close();
|
||||
w.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testLongPointMultiRangeQuery() throws IOException {
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
long[] firstPoint = {112, 296, 512};
|
||||
long[] secondPoint = {219, 514, 624};
|
||||
|
||||
Document doc = new Document();
|
||||
doc.add(new LongPoint("point", firstPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new LongPoint("point", secondPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
// One range matches
|
||||
long[] firstLowerRange= {111, 294, 502};
|
||||
long[] firstUpperRange = {117, 301, 514};
|
||||
|
||||
long[] secondLowerRange = {15, 4, 415};
|
||||
long[] secondUpperRange = {200, 402, 583};
|
||||
|
||||
LongPointMultiRangeBuilder builder = new LongPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder.add(firstLowerRange, firstUpperRange);
|
||||
builder.add(secondLowerRange, secondUpperRange);
|
||||
|
||||
Query query = builder.build();
|
||||
|
||||
IndexReader reader = iw.getReader();
|
||||
final IndexSearcher searcher = newSearcher(reader);
|
||||
iw.close();
|
||||
|
||||
assertEquals(searcher.count(query), 1);
|
||||
|
||||
// Both ranges match
|
||||
long[] firstMatchingLowerRange= {111, 294, 502};
|
||||
long[] firstMatchingUpperRange = {117, 301, 514};
|
||||
|
||||
long[] secondMatchingLowerRange = {212, 512, 415};
|
||||
long[] secondMatchingUpperRange = {228, 538, 647};
|
||||
|
||||
|
||||
LongPointMultiRangeBuilder builder2 = new LongPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder2.add(firstMatchingLowerRange, firstMatchingUpperRange);
|
||||
builder2.add(secondMatchingLowerRange, secondMatchingUpperRange);
|
||||
|
||||
query = builder2.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
// None match
|
||||
long[] nonMatchingFirstRangeLower = {1, 3, 2};
|
||||
long[] nonMatchingFirstRangeUpper = {5, 8, 7};
|
||||
|
||||
long[] nonMatchingSecondRangeLower = {11246, 19388, 21248};
|
||||
long[] nonMatchingSecondRangeUpper = {13242, 20214, 23236};
|
||||
LongPointMultiRangeBuilder builder3 = new LongPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder3.add(nonMatchingFirstRangeLower, nonMatchingFirstRangeUpper);
|
||||
builder3.add(nonMatchingSecondRangeLower, nonMatchingSecondRangeUpper);
|
||||
|
||||
query = builder3.build();
|
||||
|
||||
assertEquals(searcher.count(query), 0);
|
||||
|
||||
// Lower point is equal to a point
|
||||
long[] firstEqualsLowerPoint= {112, 296, 512};
|
||||
long[] firstEqualsUpperPoint = {219, 514, 624};
|
||||
|
||||
long[] secondEqualsLowerPoint = {11246, 19388, 21248};
|
||||
long[] secondEqualsUpperPoint = {13242, 20214, 23236};
|
||||
|
||||
LongPointMultiRangeBuilder builder4 = new LongPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder4.add(firstEqualsLowerPoint, firstEqualsUpperPoint);
|
||||
builder4.add(secondEqualsLowerPoint, secondEqualsUpperPoint);
|
||||
|
||||
query = builder4.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
reader.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testFloatRandomMultiRangeQuery() throws IOException {
|
||||
final int numDims = TestUtil.nextInt(random(), 1, 3);
|
||||
final int numVals = TestUtil.nextInt(random(), 3, 8);
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(random(), dir);
|
||||
Document doc = new Document();
|
||||
float[] value = new float[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
value[i] = TestUtil.nextInt(random(), 1, 10);
|
||||
}
|
||||
doc.add(new FloatPoint("point", value));
|
||||
w.addDocument(doc);
|
||||
IndexReader reader = w.getReader();
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
searcher.setQueryCache(null);
|
||||
FloatPointMultiRangeBuilder builder = new FloatPointMultiRangeBuilder("point", numDims);
|
||||
for (int j = 0;j < numVals; j++) {
|
||||
float[] lowerBound = new float[numDims];
|
||||
float[] upperBound = new float[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
lowerBound[i] = value[i] - random().nextInt(1);
|
||||
upperBound[i] = value[i] + random().nextInt(1);
|
||||
}
|
||||
builder.add(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
Query query = builder.build();
|
||||
searcher.search(query, Integer.MAX_VALUE);
|
||||
|
||||
reader.close();
|
||||
w.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testFloatPointMultiRangeQuery() throws IOException {
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
float[] firstPoint = {112.4f, 296.3f, 512.1f};
|
||||
float[] secondPoint = {219.7f, 514.2f, 624.6f};
|
||||
|
||||
Document doc = new Document();
|
||||
doc.add(new FloatPoint("point", firstPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new FloatPoint("point", secondPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
// One range matches
|
||||
float[] firstLowerRange= {111.3f, 294.7f, 502.1f};
|
||||
float[] firstUpperRange = {117.2f, 301.6f, 514.3f};
|
||||
|
||||
float[] secondLowerRange = {15.2f, 4.3f, 415.2f};
|
||||
float[] secondUpperRange = {200.6f, 402.3f, 583.8f};
|
||||
|
||||
FloatPointMultiRangeBuilder builder = new FloatPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder.add(firstLowerRange, firstUpperRange);
|
||||
builder.add(secondLowerRange, secondUpperRange);
|
||||
|
||||
Query query = builder.build();
|
||||
|
||||
IndexReader reader = iw.getReader();
|
||||
final IndexSearcher searcher = newSearcher(reader);
|
||||
iw.close();
|
||||
|
||||
assertEquals(searcher.count(query), 1);
|
||||
|
||||
// Both ranges match
|
||||
float[] firstMatchingLowerRange= {111f, 294f, 502f};
|
||||
float[] firstMatchingUpperRange = {117f, 301f, 514f};
|
||||
|
||||
float[] secondMatchingLowerRange = {212f, 512f, 415f};
|
||||
float[] secondMatchingUpperRange = {228f, 538f, 647f};
|
||||
|
||||
FloatPointMultiRangeBuilder builder2 = new FloatPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder2.add(firstMatchingLowerRange, firstMatchingUpperRange);
|
||||
builder2.add(secondMatchingLowerRange, secondMatchingUpperRange);
|
||||
|
||||
query = builder2.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
// None Match
|
||||
float[] nonMatchingFirstRangeLower = {1.4f, 3.3f, 2.7f};
|
||||
float[] nonMatchingFirstRangeUpper = {5.4f, 8.2f, 7.3f};
|
||||
|
||||
float[] nonMatchingSecondRangeLower = {11246.2f, 19388.6f, 21248.3f};
|
||||
float[] nonMatchingSecondRangeUpper = {13242.4f, 20214.7f, 23236.3f};
|
||||
FloatPointMultiRangeBuilder builder3 = new FloatPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder3.add(nonMatchingFirstRangeLower, nonMatchingFirstRangeUpper);
|
||||
builder3.add(nonMatchingSecondRangeLower, nonMatchingSecondRangeUpper);
|
||||
|
||||
query = builder3.build();
|
||||
|
||||
assertEquals(searcher.count(query), 0);
|
||||
|
||||
// Lower point is equal to a point
|
||||
float[] firstEqualsLowerPoint= {112.4f, 296.3f, 512.1f};
|
||||
float[] firstEqualsUpperPoint = {117.3f, 299.4f, 519.3f};
|
||||
|
||||
float[] secondEqualsLowerPoint = {219.7f, 514.2f, 624.6f};
|
||||
float[] secondEqualsUpperPoint = {13242.4f, 20214.7f, 23236.3f};
|
||||
|
||||
FloatPointMultiRangeBuilder builder4 = new FloatPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder4.add(firstEqualsLowerPoint, firstEqualsUpperPoint);
|
||||
builder4.add(secondEqualsLowerPoint, secondEqualsUpperPoint);
|
||||
|
||||
query = builder4.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
reader.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testIntRandomMultiRangeQuery() throws IOException {
|
||||
final int numDims = TestUtil.nextInt(random(), 1, 3);
|
||||
final int numVals = TestUtil.nextInt(random(), 3, 8);
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(random(), dir);
|
||||
Document doc = new Document();
|
||||
int[] value = new int[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
value[i] = TestUtil.nextInt(random(), 1, 10);
|
||||
}
|
||||
doc.add(new IntPoint("point", value));
|
||||
w.addDocument(doc);
|
||||
IndexReader reader = w.getReader();
|
||||
IndexSearcher searcher = new IndexSearcher(reader);
|
||||
searcher.setQueryCache(null);
|
||||
IntPointMultiRangeBuilder builder = new IntPointMultiRangeBuilder("point", numDims);
|
||||
for (int j = 0;j < numVals; j++) {
|
||||
int[] lowerBound = new int[numDims];
|
||||
int[] upperBound = new int[numDims];
|
||||
for (int i = 0; i < numDims; ++i) {
|
||||
lowerBound[i] = value[i] - random().nextInt(1);
|
||||
upperBound[i] = value[i] + random().nextInt(1);
|
||||
}
|
||||
builder.add(lowerBound, upperBound);
|
||||
}
|
||||
|
||||
Query query = builder.build();
|
||||
searcher.search(query, Integer.MAX_VALUE);
|
||||
|
||||
reader.close();
|
||||
w.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testIntPointMultiRangeQuery() throws IOException {
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
|
||||
int[] firstPoint = {112, 296, 512};
|
||||
int[] secondPoint = {219, 514, 624};
|
||||
|
||||
Document doc = new Document();
|
||||
doc.add(new IntPoint("point", firstPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
doc = new Document();
|
||||
doc.add(new IntPoint("point", secondPoint));
|
||||
iw.addDocument(doc);
|
||||
iw.commit();
|
||||
|
||||
// One range matches
|
||||
int[] firstLowerRange= {111, 294, 502};
|
||||
int[] firstUpperRange = {117, 301, 514};
|
||||
|
||||
int[] secondLowerRange = {15, 4, 415};
|
||||
int[] secondUpperRange = {200, 402, 583};
|
||||
|
||||
IntPointMultiRangeBuilder builder = new IntPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder.add(firstLowerRange, firstUpperRange);
|
||||
builder.add(secondLowerRange, secondUpperRange);
|
||||
|
||||
Query query = builder.build();
|
||||
|
||||
IndexReader reader = iw.getReader();
|
||||
final IndexSearcher searcher = newSearcher(reader);
|
||||
iw.close();
|
||||
|
||||
assertEquals(searcher.count(query), 1);
|
||||
|
||||
// Both ranges match
|
||||
int[] firstMatchingLowerRange= {111, 294, 502};
|
||||
int[] firstMatchingUpperRange = {117, 301, 514};
|
||||
|
||||
int[] secondMatchingLowerRange = {212, 512, 415};
|
||||
int[] secondMatchingUpperRange = {228, 538, 647};
|
||||
|
||||
|
||||
IntPointMultiRangeBuilder builder2 = new IntPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder2.add(firstMatchingLowerRange, firstMatchingUpperRange);
|
||||
builder2.add(secondMatchingLowerRange, secondMatchingUpperRange);
|
||||
|
||||
query = builder2.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
// None match
|
||||
int[] nonMatchingFirstRangeLower = {1, 3, 2};
|
||||
int[] nonMatchingFirstRangeUpper = {5, 8, 7};
|
||||
|
||||
int[] nonMatchingSecondRangeLower = {11246, 19388, 21248};
|
||||
int[] nonMatchingSecondRangeUpper = {13242, 20214, 23236};
|
||||
IntPointMultiRangeBuilder builder3 = new IntPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder3.add(nonMatchingFirstRangeLower, nonMatchingFirstRangeUpper);
|
||||
builder3.add(nonMatchingSecondRangeLower, nonMatchingSecondRangeUpper);
|
||||
|
||||
query = builder3.build();
|
||||
|
||||
assertEquals(searcher.count(query), 0);
|
||||
|
||||
// None match
|
||||
int[] firstEqualsPointLower= {112, 296, 512};
|
||||
int[] firstEqualsPointUpper = {117, 299, 517};
|
||||
|
||||
int[] secondEqualsPointLower = {219, 514, 624};
|
||||
int[] secondEqualsPointUpper = {13242, 20214, 23236};
|
||||
|
||||
IntPointMultiRangeBuilder builder4 = new IntPointMultiRangeBuilder("point", 3);
|
||||
|
||||
builder4.add(firstEqualsPointLower, firstEqualsPointUpper);
|
||||
builder4.add(secondEqualsPointLower, secondEqualsPointUpper);
|
||||
|
||||
query = builder4.build();
|
||||
|
||||
assertEquals(searcher.count(query), 2);
|
||||
|
||||
reader.close();
|
||||
dir.close();
|
||||
}
|
||||
|
||||
public void testToString() {
|
||||
double[] firstDoubleLowerRange= {111, 294.3, 502.4};
|
||||
double[] firstDoubleUpperRange = {117.3, 301.8, 514.3};
|
||||
|
||||
double[] secondDoubleLowerRange = {15.3, 412.8, 415.1};
|
||||
double[] secondDoubleUpperRange = {200.4, 567.4, 642.2};
|
||||
|
||||
DoublePointMultiRangeBuilder stringTestbuilder = new DoublePointMultiRangeBuilder("point", 3);
|
||||
|
||||
stringTestbuilder.add(firstDoubleLowerRange, firstDoubleUpperRange);
|
||||
stringTestbuilder.add(secondDoubleLowerRange, secondDoubleUpperRange);
|
||||
|
||||
Query query = stringTestbuilder.build();
|
||||
|
||||
assertEquals("point:{[111.0 TO 117.3],[294.3 TO 301.8],[502.4 TO 514.3]},{[15.3 TO 200.4],[412.8 TO 567.4],[415.1 TO 642.2]}",
|
||||
query.toString());
|
||||
|
||||
long[] firstLongLowerRange= {111, 294, 502};
|
||||
long[] firstLongUpperRange = {117, 301, 514};
|
||||
|
||||
long[] secondLongLowerRange = {15, 412, 415};
|
||||
long[] secondLongUpperRange = {200, 567, 642};
|
||||
|
||||
LongPointMultiRangeBuilder stringLongTestbuilder = new LongPointMultiRangeBuilder("point", 3);
|
||||
|
||||
stringLongTestbuilder.add(firstLongLowerRange, firstLongUpperRange);
|
||||
stringLongTestbuilder.add(secondLongLowerRange, secondLongUpperRange);
|
||||
|
||||
query = stringLongTestbuilder.build();
|
||||
|
||||
assertEquals("point:{[111 TO 117],[294 TO 301],[502 TO 514]},{[15 TO 200],[412 TO 567],[415 TO 642]}",
|
||||
query.toString());
|
||||
|
||||
float[] firstFloatLowerRange= {111.3f, 294.4f, 502.2f};
|
||||
float[] firstFloatUpperRange = {117.7f, 301.2f, 514.4f};
|
||||
|
||||
float[] secondFloatLowerRange = {15.3f, 412.2f, 415.9f};
|
||||
float[] secondFloatUpperRange = {200.2f, 567.4f, 642.3f};
|
||||
|
||||
FloatPointMultiRangeBuilder stringFloatTestbuilder = new FloatPointMultiRangeBuilder("point", 3);
|
||||
|
||||
stringFloatTestbuilder.add(firstFloatLowerRange, firstFloatUpperRange);
|
||||
stringFloatTestbuilder.add(secondFloatLowerRange, secondFloatUpperRange);
|
||||
|
||||
query = stringFloatTestbuilder.build();
|
||||
|
||||
assertEquals("point:{[111.3 TO 117.7],[294.4 TO 301.2],[502.2 TO 514.4]},{[15.3 TO 200.2],[412.2 TO 567.4],[415.9 TO 642.3]}",
|
||||
query.toString());
|
||||
|
||||
int[] firstIntLowerRange= {111, 294, 502};
|
||||
int[] firstIntUpperRange = {117, 301, 514};
|
||||
|
||||
int[] secondIntLowerRange = {15, 412, 415};
|
||||
int[] secondIntUpperRange = {200, 567, 642};
|
||||
|
||||
IntPointMultiRangeBuilder stringIntTestbuilder = new IntPointMultiRangeBuilder("point", 3);
|
||||
|
||||
stringIntTestbuilder.add(firstIntLowerRange, firstIntUpperRange);
|
||||
stringIntTestbuilder.add(secondIntLowerRange, secondIntUpperRange);
|
||||
|
||||
query = stringIntTestbuilder.build();
|
||||
|
||||
assertEquals("point:{[111 TO 117],[294 TO 301],[502 TO 514]},{[15 TO 200],[412 TO 567],[415 TO 642]}",
|
||||
query.toString());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue