mirror of https://github.com/apache/lucene.git
* LUCENE-7094: BBoxStrategy and PointVectorStrategy now support PointValues (in addition to legacy numeric trie). Their APIs were changed a little and also made more consistent. PointValues/Trie is optional, DocValues is optional, stored value is optional.
This commit is contained in:
parent
5e5fd66257
commit
e1b45568b4
|
@ -118,6 +118,12 @@ New Features
|
|||
|
||||
API Changes
|
||||
|
||||
* LUCENE-7094: BBoxStrategy and PointVectorStrategy now support
|
||||
PointValues (in addition to legacy numeric trie). Their APIs
|
||||
were changed a little and also made more consistent. PointValues/Trie
|
||||
is optional, DocValues is optional, stored value is optional.
|
||||
(Nick Knize, David Smiley)
|
||||
|
||||
* LUCENE-6067: Accountable.getChildResources has a default
|
||||
implementation returning the empty list. (Robert Muir)
|
||||
|
||||
|
|
|
@ -16,15 +16,15 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.bbox;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.LegacyDoubleField;
|
||||
import org.apache.lucene.document.DoubleDocValuesField;
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.LegacyDoubleField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
|
@ -41,6 +41,10 @@ import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
|
|||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.util.LegacyNumericUtils;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -63,7 +67,7 @@ import org.apache.lucene.util.NumericUtils;
|
|||
* <p>
|
||||
* This uses 4 double fields for minX, maxX, minY, maxY
|
||||
* and a boolean to mark a dateline cross. Depending on the particular {@link
|
||||
* SpatialOperation}s, there are a variety of {@link org.apache.lucene.search.LegacyNumericRangeQuery}s to be
|
||||
* SpatialOperation}s, there are a variety of range queries on {@link DoublePoint}s to be
|
||||
* done.
|
||||
* The {@link #makeOverlapRatioValueSource(org.locationtech.spatial4j.shape.Rectangle, double)}
|
||||
* works by calculating the query bbox overlap percentage against the indexed
|
||||
|
@ -74,6 +78,35 @@ import org.apache.lucene.util.NumericUtils;
|
|||
*/
|
||||
public class BBoxStrategy extends SpatialStrategy {
|
||||
|
||||
// note: we use a FieldType to articulate the options we want on the field. We don't use it as-is with a Field, we
|
||||
// create more than one Field.
|
||||
|
||||
/**
|
||||
* pointValues, docValues, and nothing else.
|
||||
*/
|
||||
public static FieldType DEFAULT_FIELDTYPE;
|
||||
|
||||
@Deprecated
|
||||
public static FieldType LEGACY_FIELDTYPE;
|
||||
static {
|
||||
// Default: pointValues + docValues
|
||||
FieldType type = new FieldType();
|
||||
type.setDimensions(1, Double.BYTES);//pointValues (assume Double)
|
||||
type.setDocValuesType(DocValuesType.NUMERIC);//docValues
|
||||
type.setStored(false);
|
||||
type.freeze();
|
||||
DEFAULT_FIELDTYPE = type;
|
||||
// Legacy default: legacyNumerics + docValues
|
||||
type = new FieldType();
|
||||
type.setIndexOptions(IndexOptions.DOCS);
|
||||
type.setNumericType(FieldType.LegacyNumericType.DOUBLE);
|
||||
type.setNumericPrecisionStep(8);// same as solr default
|
||||
type.setDocValuesType(DocValuesType.NUMERIC);//docValues
|
||||
type.setStored(false);
|
||||
type.freeze();
|
||||
LEGACY_FIELDTYPE = type;
|
||||
}
|
||||
|
||||
public static final String SUFFIX_MINX = "__minX";
|
||||
public static final String SUFFIX_MAXX = "__maxX";
|
||||
public static final String SUFFIX_MINY = "__minY";
|
||||
|
@ -84,17 +117,45 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
* The Bounding Box gets stored as four fields for x/y min/max and a flag
|
||||
* that says if the box crosses the dateline (xdl).
|
||||
*/
|
||||
protected final String field_bbox;
|
||||
protected final String field_minX;
|
||||
protected final String field_minY;
|
||||
protected final String field_maxX;
|
||||
protected final String field_maxY;
|
||||
protected final String field_xdl; // crosses dateline
|
||||
final String field_bbox;
|
||||
final String field_minX;
|
||||
final String field_minY;
|
||||
final String field_maxX;
|
||||
final String field_maxY;
|
||||
final String field_xdl; // crosses dateline
|
||||
|
||||
protected FieldType fieldType;//for the 4 numbers
|
||||
protected FieldType xdlFieldType;
|
||||
private final FieldType optionsFieldType;//from constructor; aggregate field type used to express all options
|
||||
private final int fieldsLen;
|
||||
private final boolean hasStored;
|
||||
private final boolean hasDocVals;
|
||||
private final boolean hasPointVals;
|
||||
// equiv to "hasLegacyNumerics":
|
||||
private final FieldType legacyNumericFieldType; // not stored; holds precision step.
|
||||
private final FieldType xdlFieldType;
|
||||
|
||||
public BBoxStrategy(SpatialContext ctx, String fieldNamePrefix) {
|
||||
/**
|
||||
* Creates a new {@link BBoxStrategy} instance that uses {@link DoublePoint} and {@link DoublePoint#newRangeQuery}
|
||||
*/
|
||||
public static BBoxStrategy newInstance(SpatialContext ctx, String fieldNamePrefix) {
|
||||
return new BBoxStrategy(ctx, fieldNamePrefix, DEFAULT_FIELDTYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link BBoxStrategy} instance that uses {@link LegacyDoubleField} for backwards compatibility
|
||||
* @deprecated LegacyNumerics will be removed
|
||||
*/
|
||||
@Deprecated
|
||||
public static BBoxStrategy newLegacyInstance(SpatialContext ctx, String fieldNamePrefix) {
|
||||
return new BBoxStrategy(ctx, fieldNamePrefix, LEGACY_FIELDTYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates this strategy.
|
||||
* {@code fieldType} is used to customize the indexing options of the 4 number fields, and to a lesser degree the XDL
|
||||
* field too. Search requires pointValues (or legacy numerics), and relevancy requires docValues. If these features
|
||||
* aren't needed then disable them.
|
||||
*/
|
||||
public BBoxStrategy(SpatialContext ctx, String fieldNamePrefix, FieldType fieldType) {
|
||||
super(ctx, fieldNamePrefix);
|
||||
field_bbox = fieldNamePrefix;
|
||||
field_minX = fieldNamePrefix + SUFFIX_MINX;
|
||||
|
@ -103,34 +164,49 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
field_maxY = fieldNamePrefix + SUFFIX_MAXY;
|
||||
field_xdl = fieldNamePrefix + SUFFIX_XDL;
|
||||
|
||||
FieldType fieldType = new FieldType(LegacyDoubleField.TYPE_NOT_STORED);
|
||||
fieldType.setNumericPrecisionStep(8);//Solr's default
|
||||
fieldType.setDocValuesType(DocValuesType.NUMERIC);
|
||||
setFieldType(fieldType);
|
||||
}
|
||||
|
||||
private int getPrecisionStep() {
|
||||
return fieldType.numericPrecisionStep();
|
||||
}
|
||||
|
||||
public FieldType getFieldType() {
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
/** Used to customize the indexing options of the 4 number fields, and to a lesser degree the XDL field too. Search
|
||||
* requires indexed=true, and relevancy requires docValues. If these features aren't needed then disable them.
|
||||
* {@link FieldType#freeze()} is called on the argument. */
|
||||
public void setFieldType(FieldType fieldType) {
|
||||
fieldType.freeze();
|
||||
this.fieldType = fieldType;
|
||||
//only double's supported right now
|
||||
if (fieldType.numericType() != FieldType.LegacyNumericType.DOUBLE)
|
||||
throw new IllegalArgumentException("BBoxStrategy only supports doubles at this time.");
|
||||
//for xdlFieldType, copy some similar options. Don't do docValues since it isn't needed here.
|
||||
this.optionsFieldType = fieldType;
|
||||
|
||||
int numQuads = 0;
|
||||
if ((this.hasStored = fieldType.stored())) {
|
||||
numQuads++;
|
||||
}
|
||||
if ((this.hasDocVals = fieldType.docValuesType() != DocValuesType.NONE)) {
|
||||
numQuads++;
|
||||
}
|
||||
if ((this.hasPointVals = fieldType.pointDimensionCount() > 0)) {
|
||||
numQuads++;
|
||||
}
|
||||
if (fieldType.indexOptions() != IndexOptions.NONE && fieldType.numericType() != null) {
|
||||
if (hasPointVals) {
|
||||
throw new IllegalArgumentException("pointValues and LegacyNumericType are mutually exclusive");
|
||||
}
|
||||
if (fieldType.numericType() != FieldType.LegacyNumericType.DOUBLE) {
|
||||
throw new IllegalArgumentException(getClass() + " does not support " + fieldType.numericType());
|
||||
}
|
||||
numQuads++;
|
||||
legacyNumericFieldType = new FieldType(LegacyDoubleField.TYPE_NOT_STORED);
|
||||
legacyNumericFieldType.setNumericPrecisionStep(fieldType.numericPrecisionStep());
|
||||
legacyNumericFieldType.freeze();
|
||||
} else {
|
||||
legacyNumericFieldType = null;
|
||||
}
|
||||
|
||||
if (hasPointVals || legacyNumericFieldType != null) { // if we have an index...
|
||||
xdlFieldType = new FieldType(StringField.TYPE_NOT_STORED);
|
||||
xdlFieldType.setStored(fieldType.stored());
|
||||
xdlFieldType.setIndexOptions(fieldType.indexOptions());
|
||||
xdlFieldType.setIndexOptions(IndexOptions.DOCS);
|
||||
xdlFieldType.freeze();
|
||||
} else {
|
||||
xdlFieldType = null;
|
||||
}
|
||||
|
||||
this.fieldsLen = numQuads * 4 + (xdlFieldType != null ? 1 : 0);
|
||||
}
|
||||
|
||||
/** Returns a field type representing the set of field options. This is identical to what was passed into the
|
||||
* constructor. It's frozen. */
|
||||
public FieldType getFieldType() {
|
||||
return optionsFieldType;
|
||||
}
|
||||
|
||||
//---------------------------------
|
||||
|
@ -142,39 +218,40 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
return createIndexableFields(shape.getBoundingBox());
|
||||
}
|
||||
|
||||
public Field[] createIndexableFields(Rectangle bbox) {
|
||||
Field[] fields = new Field[5];
|
||||
fields[0] = new ComboField(field_minX, bbox.getMinX(), fieldType);
|
||||
fields[1] = new ComboField(field_maxX, bbox.getMaxX(), fieldType);
|
||||
fields[2] = new ComboField(field_minY, bbox.getMinY(), fieldType);
|
||||
fields[3] = new ComboField(field_maxY, bbox.getMaxY(), fieldType);
|
||||
fields[4] = new ComboField(field_xdl, bbox.getCrossesDateLine()?"T":"F", xdlFieldType);
|
||||
private Field[] createIndexableFields(Rectangle bbox) {
|
||||
Field[] fields = new Field[fieldsLen];
|
||||
int idx = -1;
|
||||
if (hasStored) {
|
||||
fields[++idx] = new StoredField(field_minX, bbox.getMinX());
|
||||
fields[++idx] = new StoredField(field_minY, bbox.getMinY());
|
||||
fields[++idx] = new StoredField(field_maxX, bbox.getMaxX());
|
||||
fields[++idx] = new StoredField(field_maxY, bbox.getMaxY());
|
||||
}
|
||||
if (hasDocVals) {
|
||||
fields[++idx] = new DoubleDocValuesField(field_minX, bbox.getMinX());
|
||||
fields[++idx] = new DoubleDocValuesField(field_minY, bbox.getMinY());
|
||||
fields[++idx] = new DoubleDocValuesField(field_maxX, bbox.getMaxX());
|
||||
fields[++idx] = new DoubleDocValuesField(field_maxY, bbox.getMaxY());
|
||||
}
|
||||
if (hasPointVals) {
|
||||
fields[++idx] = new DoublePoint(field_minX, bbox.getMinX());
|
||||
fields[++idx] = new DoublePoint(field_minY, bbox.getMinY());
|
||||
fields[++idx] = new DoublePoint(field_maxX, bbox.getMaxX());
|
||||
fields[++idx] = new DoublePoint(field_maxY, bbox.getMaxY());
|
||||
}
|
||||
if (legacyNumericFieldType != null) {
|
||||
fields[++idx] = new LegacyDoubleField(field_minX, bbox.getMinX(), legacyNumericFieldType);
|
||||
fields[++idx] = new LegacyDoubleField(field_minY, bbox.getMinY(), legacyNumericFieldType);
|
||||
fields[++idx] = new LegacyDoubleField(field_maxX, bbox.getMaxX(), legacyNumericFieldType);
|
||||
fields[++idx] = new LegacyDoubleField(field_maxY, bbox.getMaxY(), legacyNumericFieldType);
|
||||
}
|
||||
if (xdlFieldType != null) {
|
||||
fields[++idx] = new Field(field_xdl, bbox.getCrossesDateLine()?"T":"F", xdlFieldType);
|
||||
}
|
||||
assert idx == fields.length - 1;
|
||||
return fields;
|
||||
}
|
||||
|
||||
/** Field subclass circumventing Field limitations. This one instance can have any combination of indexed, stored,
|
||||
* and docValues.
|
||||
*/
|
||||
private static class ComboField extends Field {
|
||||
private ComboField(String name, Object value, FieldType type) {
|
||||
super(name, type);//this expert constructor allows us to have a field that has docValues & indexed/stored
|
||||
super.fieldsData = value;
|
||||
}
|
||||
|
||||
//Is this a hack? We assume that numericValue() is only called for DocValues purposes.
|
||||
@Override
|
||||
public Number numericValue() {
|
||||
//Numeric DocValues only supports Long,
|
||||
final Number number = super.numericValue();
|
||||
if (number == null)
|
||||
return null;
|
||||
if (fieldType().numericType() == FieldType.LegacyNumericType.DOUBLE)
|
||||
return Double.doubleToLongBits(number.doubleValue());
|
||||
if (fieldType().numericType() == FieldType.LegacyNumericType.FLOAT)
|
||||
return Float.floatToIntBits(number.floatValue());
|
||||
return number.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------
|
||||
// Value Source / Relevancy
|
||||
|
@ -253,8 +330,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
|
||||
// Y conditions
|
||||
// docMinY <= queryExtent.getMinY() AND docMaxY >= queryExtent.getMaxY()
|
||||
Query qMinY = LegacyNumericRangeQuery.newDoubleRange(field_minY, getPrecisionStep(), null, bbox.getMinY(), false, true);
|
||||
Query qMaxY = LegacyNumericRangeQuery.newDoubleRange(field_maxY, getPrecisionStep(), bbox.getMaxY(), null, true, false);
|
||||
Query qMinY = this.makeNumericRangeQuery(field_minY, null, bbox.getMinY(), false, true);
|
||||
Query qMaxY = this.makeNumericRangeQuery(field_maxY, bbox.getMaxY(), null, true, false);
|
||||
Query yConditions = this.makeQuery(BooleanClause.Occur.MUST, qMinY, qMaxY);
|
||||
|
||||
// X conditions
|
||||
|
@ -266,8 +343,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// X Conditions for documents that do not cross the date line,
|
||||
// documents that contain the min X and max X of the query envelope,
|
||||
// docMinX <= queryExtent.getMinX() AND docMaxX >= queryExtent.getMaxX()
|
||||
Query qMinX = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), null, bbox.getMinX(), false, true);
|
||||
Query qMaxX = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), bbox.getMaxX(), null, true, false);
|
||||
Query qMinX = this.makeNumericRangeQuery(field_minX, null, bbox.getMinX(), false, true);
|
||||
Query qMaxX = this.makeNumericRangeQuery(field_maxX, bbox.getMaxX(), null, true, false);
|
||||
Query qMinMax = this.makeQuery(BooleanClause.Occur.MUST, qMinX, qMaxX);
|
||||
Query qNonXDL = this.makeXDL(false, qMinMax);
|
||||
|
||||
|
@ -278,8 +355,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// the left portion of the document contains the min X of the query
|
||||
// OR the right portion of the document contains the max X of the query,
|
||||
// docMinXLeft <= queryExtent.getMinX() OR docMaxXRight >= queryExtent.getMaxX()
|
||||
Query qXDLLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), null, bbox.getMinX(), false, true);
|
||||
Query qXDLRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), bbox.getMaxX(), null, true, false);
|
||||
Query qXDLLeft = this.makeNumericRangeQuery(field_minX, null, bbox.getMinX(), false, true);
|
||||
Query qXDLRight = this.makeNumericRangeQuery(field_maxX, bbox.getMaxX(), null, true, false);
|
||||
Query qXDLLeftRight = this.makeQuery(BooleanClause.Occur.SHOULD, qXDLLeft, qXDLRight);
|
||||
Query qXDL = this.makeXDL(true, qXDLLeftRight);
|
||||
|
||||
|
@ -302,8 +379,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// the left portion of the document contains the min X of the query
|
||||
// AND the right portion of the document contains the max X of the query,
|
||||
// docMinXLeft <= queryExtent.getMinX() AND docMaxXRight >= queryExtent.getMaxX()
|
||||
Query qXDLLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), null, bbox.getMinX(), false, true);
|
||||
Query qXDLRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), bbox.getMaxX(), null, true, false);
|
||||
Query qXDLLeft = this.makeNumericRangeQuery(field_minX, null, bbox.getMinX(), false, true);
|
||||
Query qXDLRight = this.makeNumericRangeQuery(field_maxX, bbox.getMaxX(), null, true, false);
|
||||
Query qXDLLeftRight = this.makeXDL(true, this.makeQuery(BooleanClause.Occur.MUST, qXDLLeft, qXDLRight));
|
||||
|
||||
Query qWorld = makeQuery(BooleanClause.Occur.MUST,
|
||||
|
@ -328,8 +405,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
|
||||
// Y conditions
|
||||
// docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY()
|
||||
Query qMinY = LegacyNumericRangeQuery.newDoubleRange(field_minY, getPrecisionStep(), bbox.getMaxY(), null, false, false);
|
||||
Query qMaxY = LegacyNumericRangeQuery.newDoubleRange(field_maxY, getPrecisionStep(), null, bbox.getMinY(), false, false);
|
||||
Query qMinY = this.makeNumericRangeQuery(field_minY, bbox.getMaxY(), null, false, false);
|
||||
Query qMaxY = this.makeNumericRangeQuery(field_maxY, null, bbox.getMinY(), false, false);
|
||||
Query yConditions = this.makeQuery(BooleanClause.Occur.SHOULD, qMinY, qMaxY);
|
||||
|
||||
// X conditions
|
||||
|
@ -340,14 +417,15 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
|
||||
// X Conditions for documents that do not cross the date line,
|
||||
// docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()
|
||||
Query qMinX = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMaxX(), null, false, false);
|
||||
Query qMinX = this.makeNumericRangeQuery(field_minX, bbox.getMaxX(), null, false, false);
|
||||
if (bbox.getMinX() == -180.0 && ctx.isGeo()) {//touches dateline; -180 == 180
|
||||
BooleanQuery.Builder bq = new BooleanQuery.Builder();
|
||||
bq.add(qMinX, BooleanClause.Occur.MUST);
|
||||
bq.add(makeNumberTermQuery(field_maxX, 180.0), BooleanClause.Occur.MUST_NOT);
|
||||
qMinX = bq.build();
|
||||
}
|
||||
Query qMaxX = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMinX(), false, false);
|
||||
Query qMaxX = this.makeNumericRangeQuery(field_maxX, null, bbox.getMinX(), false, false);
|
||||
|
||||
if (bbox.getMaxX() == 180.0 && ctx.isGeo()) {//touches dateline; -180 == 180
|
||||
BooleanQuery.Builder bq = new BooleanQuery.Builder();
|
||||
bq.add(qMaxX, BooleanClause.Occur.MUST);
|
||||
|
@ -368,8 +446,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// where: docMaxXLeft = 180.0, docMinXRight = -180.0
|
||||
// (docMaxXLeft < queryExtent.getMinX()) equates to (180.0 < queryExtent.getMinX()) and is ignored
|
||||
// (docMinXRight > queryExtent.getMaxX()) equates to (-180.0 > queryExtent.getMaxX()) and is ignored
|
||||
Query qMinXLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMaxX(), null, false, false);
|
||||
Query qMaxXRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMinX(), false, false);
|
||||
Query qMinXLeft = this.makeNumericRangeQuery(field_minX, bbox.getMaxX(), null, false, false);
|
||||
Query qMaxXRight = this.makeNumericRangeQuery(field_maxX, null, bbox.getMinX(), false, false);
|
||||
Query qLeftRight = this.makeQuery(BooleanClause.Occur.MUST, qMinXLeft, qMaxXRight);
|
||||
Query qXDL = this.makeXDL(true, qLeftRight);
|
||||
|
||||
|
@ -383,10 +461,10 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// the document must be disjoint to both the left and right query portions
|
||||
// (docMinX > queryExtent.getMaxX()Left OR docMaxX < queryExtent.getMinX()) AND (docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()Left)
|
||||
// where: queryExtent.getMaxX()Left = 180.0, queryExtent.getMinX()Left = -180.0
|
||||
Query qMinXLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), 180.0, null, false, false);
|
||||
Query qMaxXLeft = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMinX(), false, false);
|
||||
Query qMinXRight = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMaxX(), null, false, false);
|
||||
Query qMaxXRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, -180.0, false, false);
|
||||
Query qMinXLeft = this.makeNumericRangeQuery(field_minX, 180.0, null, false, false);
|
||||
Query qMaxXLeft = this.makeNumericRangeQuery(field_maxX, null, bbox.getMinX(), false, false);
|
||||
Query qMinXRight = this.makeNumericRangeQuery(field_minX, bbox.getMaxX(), null, false, false);
|
||||
Query qMaxXRight = this.makeNumericRangeQuery(field_maxX, null, -180.0, false, false);
|
||||
Query qLeft = this.makeQuery(BooleanClause.Occur.SHOULD, qMinXLeft, qMaxXLeft);
|
||||
Query qRight = this.makeQuery(BooleanClause.Occur.SHOULD, qMinXRight, qMaxXRight);
|
||||
Query qLeftRight = this.makeQuery(BooleanClause.Occur.MUST, qLeft, qRight);
|
||||
|
@ -478,8 +556,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
|
||||
// Y conditions
|
||||
// docMinY >= queryExtent.getMinY() AND docMaxY <= queryExtent.getMaxY()
|
||||
Query qMinY = LegacyNumericRangeQuery.newDoubleRange(field_minY, getPrecisionStep(), bbox.getMinY(), null, true, false);
|
||||
Query qMaxY = LegacyNumericRangeQuery.newDoubleRange(field_maxY, getPrecisionStep(), null, bbox.getMaxY(), false, true);
|
||||
Query qMinY = this.makeNumericRangeQuery(field_minY, bbox.getMinY(), null, true, false);
|
||||
Query qMaxY = this.makeNumericRangeQuery(field_maxY, null, bbox.getMaxY(), false, true);
|
||||
Query yConditions = this.makeQuery(BooleanClause.Occur.MUST, qMinY, qMaxY);
|
||||
|
||||
// X conditions
|
||||
|
@ -493,8 +571,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// queries that do not cross the date line
|
||||
|
||||
// docMinX >= queryExtent.getMinX() AND docMaxX <= queryExtent.getMaxX()
|
||||
Query qMinX = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMinX(), null, true, false);
|
||||
Query qMaxX = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMaxX(), false, true);
|
||||
Query qMinX = this.makeNumericRangeQuery(field_minX, bbox.getMinX(), null, true, false);
|
||||
Query qMaxX = this.makeNumericRangeQuery(field_maxX, null, bbox.getMaxX(), false, true);
|
||||
Query qMinMax = this.makeQuery(BooleanClause.Occur.MUST, qMinX, qMaxX);
|
||||
|
||||
double edge = 0;//none, otherwise opposite dateline of query
|
||||
|
@ -517,14 +595,14 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
|
||||
// the document should be within the left portion of the query
|
||||
// docMinX >= queryExtent.getMinX() AND docMaxX <= 180.0
|
||||
Query qMinXLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMinX(), null, true, false);
|
||||
Query qMaxXLeft = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, 180.0, false, true);
|
||||
Query qMinXLeft = this.makeNumericRangeQuery(field_minX, bbox.getMinX(), null, true, false);
|
||||
Query qMaxXLeft = this.makeNumericRangeQuery(field_maxX, null, 180.0, false, true);
|
||||
Query qLeft = this.makeQuery(BooleanClause.Occur.MUST, qMinXLeft, qMaxXLeft);
|
||||
|
||||
// the document should be within the right portion of the query
|
||||
// docMinX >= -180.0 AND docMaxX <= queryExtent.getMaxX()
|
||||
Query qMinXRight = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), -180.0, null, true, false);
|
||||
Query qMaxXRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMaxX(), false, true);
|
||||
Query qMinXRight = this.makeNumericRangeQuery(field_minX, -180.0, null, true, false);
|
||||
Query qMaxXRight = this.makeNumericRangeQuery(field_maxX, null, bbox.getMaxX(), false, true);
|
||||
Query qRight = this.makeQuery(BooleanClause.Occur.MUST, qMinXRight, qMaxXRight);
|
||||
|
||||
// either left or right conditions should occur,
|
||||
|
@ -537,8 +615,8 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
// AND the right portion of the document must be within the right portion of the query
|
||||
// docMinXLeft >= queryExtent.getMinX() AND docMaxXLeft <= 180.0
|
||||
// AND docMinXRight >= -180.0 AND docMaxXRight <= queryExtent.getMaxX()
|
||||
Query qXDLLeft = LegacyNumericRangeQuery.newDoubleRange(field_minX, getPrecisionStep(), bbox.getMinX(), null, true, false);
|
||||
Query qXDLRight = LegacyNumericRangeQuery.newDoubleRange(field_maxX, getPrecisionStep(), null, bbox.getMaxX(), false, true);
|
||||
Query qXDLLeft = this.makeNumericRangeQuery(field_minX, bbox.getMinX(), null, true, false);
|
||||
Query qXDLRight = this.makeNumericRangeQuery(field_maxX, null, bbox.getMaxX(), false, true);
|
||||
Query qXDLLeftRight = this.makeQuery(BooleanClause.Occur.MUST, qXDLLeft, qXDLRight);
|
||||
Query qXDL = this.makeXDL(true, qXDLLeftRight);
|
||||
|
||||
|
@ -581,9 +659,49 @@ public class BBoxStrategy extends SpatialStrategy {
|
|||
}
|
||||
|
||||
private Query makeNumberTermQuery(String field, double number) {
|
||||
if (hasPointVals) {
|
||||
return DoublePoint.newExactQuery(field, number);
|
||||
} else if (legacyNumericFieldType != null) {
|
||||
BytesRefBuilder bytes = new BytesRefBuilder();
|
||||
LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(number), 0, bytes);
|
||||
return new TermQuery(new Term(field, bytes.get()));
|
||||
}
|
||||
|
||||
throw new UnsupportedOperationException("An index is required for this operation.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a numeric range query based on FieldType
|
||||
* {@link LegacyNumericRangeQuery} is used for indexes created using {@code FieldType.LegacyNumericType}
|
||||
* {@link DoublePoint#newRangeQuery} is used for indexes created using {@link DoublePoint} fields
|
||||
*
|
||||
* @param fieldname field name. must not be <code>null</code>.
|
||||
* @param min minimum value of the range.
|
||||
* @param max maximum value of the range.
|
||||
* @param minInclusive include the minimum value if <code>true</code>.
|
||||
* @param maxInclusive include the maximum value if <code>true</code>
|
||||
*/
|
||||
private Query makeNumericRangeQuery(String fieldname, Double min, Double max, boolean minInclusive, boolean maxInclusive) {
|
||||
if (hasPointVals) {
|
||||
if (min == null) {
|
||||
min = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
if (max == null) {
|
||||
max = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
if (minInclusive == false) {
|
||||
min = Math.nextUp(min);
|
||||
}
|
||||
|
||||
if (maxInclusive == false) {
|
||||
max = Math.nextDown(max);
|
||||
}
|
||||
|
||||
return DoublePoint.newRangeQuery(fieldname, min, max);
|
||||
} else if (legacyNumericFieldType != null) {// todo remove legacy numeric support in 7.0
|
||||
return LegacyNumericRangeQuery.newDoubleRange(fieldname, legacyNumericFieldType.numericPrecisionStep(), min, max, minInclusive, maxInclusive);
|
||||
}
|
||||
throw new UnsupportedOperationException("An index is required for this operation.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.LegacyDoubleField;
|
||||
import org.apache.lucene.document.DoubleDocValuesField;
|
||||
import org.apache.lucene.document.DoublePoint;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.LegacyDoubleField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.queries.function.FunctionRangeQuery;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
|
@ -35,10 +35,15 @@ import org.apache.lucene.spatial.SpatialStrategy;
|
|||
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* Simple {@link SpatialStrategy} which represents Points in two numeric {@link
|
||||
* org.apache.lucene.document.LegacyDoubleField}s. The Strategy's best feature is decent distance sort.
|
||||
* Simple {@link SpatialStrategy} which represents Points in two numeric fields.
|
||||
* The Strategy's best feature is decent distance sort.
|
||||
*
|
||||
* <p>
|
||||
* <b>Characteristics:</b>
|
||||
|
@ -49,7 +54,7 @@ import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
|
|||
* <li>{@link
|
||||
* org.apache.lucene.spatial.query.SpatialOperation#Intersects} and {@link
|
||||
* SpatialOperation#IsWithin} is supported.</li>
|
||||
* <li>Uses the FieldCache for
|
||||
* <li>Requires DocValues for
|
||||
* {@link #makeDistanceValueSource(org.locationtech.spatial4j.shape.Point)} and for
|
||||
* searching with a Circle.</li>
|
||||
* </ul>
|
||||
|
@ -57,8 +62,8 @@ import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
|
|||
* <p>
|
||||
* <b>Implementation:</b>
|
||||
* <p>
|
||||
* This is a simple Strategy. Search works with {@link org.apache.lucene.search.LegacyNumericRangeQuery}s on
|
||||
* an x and y pair of fields. A Circle query does the same bbox query but adds a
|
||||
* This is a simple Strategy. Search works with a pair of range queries on two {@link DoublePoint}s representing
|
||||
* x & y fields. A Circle query does the same bbox query but adds a
|
||||
* ValueSource filter on
|
||||
* {@link #makeDistanceValueSource(org.locationtech.spatial4j.shape.Point)}.
|
||||
* <p>
|
||||
|
@ -71,25 +76,104 @@ import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
|
|||
*/
|
||||
public class PointVectorStrategy extends SpatialStrategy {
|
||||
|
||||
// note: we use a FieldType to articulate the options we want on the field. We don't use it as-is with a Field, we
|
||||
// create more than one Field.
|
||||
|
||||
/**
|
||||
* pointValues, docValues, and nothing else.
|
||||
*/
|
||||
public static FieldType DEFAULT_FIELDTYPE;
|
||||
|
||||
@Deprecated
|
||||
public static FieldType LEGACY_FIELDTYPE;
|
||||
static {
|
||||
// Default: pointValues + docValues
|
||||
FieldType type = new FieldType();
|
||||
type.setDimensions(1, Double.BYTES);//pointValues (assume Double)
|
||||
type.setDocValuesType(DocValuesType.NUMERIC);//docValues
|
||||
type.setStored(false);
|
||||
type.freeze();
|
||||
DEFAULT_FIELDTYPE = type;
|
||||
// Legacy default: legacyNumerics
|
||||
type = new FieldType();
|
||||
type.setIndexOptions(IndexOptions.DOCS);
|
||||
type.setNumericType(FieldType.LegacyNumericType.DOUBLE);
|
||||
type.setNumericPrecisionStep(8);// same as solr default
|
||||
type.setDocValuesType(DocValuesType.NONE);//no docValues!
|
||||
type.setStored(false);
|
||||
type.freeze();
|
||||
LEGACY_FIELDTYPE = type;
|
||||
}
|
||||
|
||||
public static final String SUFFIX_X = "__x";
|
||||
public static final String SUFFIX_Y = "__y";
|
||||
|
||||
private final String fieldNameX;
|
||||
private final String fieldNameY;
|
||||
|
||||
public int precisionStep = 8; // same as solr default
|
||||
private final int fieldsLen;
|
||||
private final boolean hasStored;
|
||||
private final boolean hasDocVals;
|
||||
private final boolean hasPointVals;
|
||||
// equiv to "hasLegacyNumerics":
|
||||
private final FieldType legacyNumericFieldType; // not stored; holds precision step.
|
||||
|
||||
public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix) {
|
||||
/**
|
||||
* Create a new {@link PointVectorStrategy} instance that uses {@link DoublePoint} and {@link DoublePoint#newRangeQuery}
|
||||
*/
|
||||
public static PointVectorStrategy newInstance(SpatialContext ctx, String fieldNamePrefix) {
|
||||
return new PointVectorStrategy(ctx, fieldNamePrefix, DEFAULT_FIELDTYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link PointVectorStrategy} instance that uses {@link LegacyDoubleField} for backwards compatibility.
|
||||
* However, back-compat is limited; we don't support circle queries or {@link #makeDistanceValueSource(Point, double)}
|
||||
* since that requires docValues (the legacy config didn't have that).
|
||||
*
|
||||
* @deprecated LegacyNumerics will be removed
|
||||
*/
|
||||
@Deprecated
|
||||
public static PointVectorStrategy newLegacyInstance(SpatialContext ctx, String fieldNamePrefix) {
|
||||
return new PointVectorStrategy(ctx, fieldNamePrefix, LEGACY_FIELDTYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance configured with the provided FieldType options. See {@link #DEFAULT_FIELDTYPE}.
|
||||
* a field type is used to articulate the desired options (namely pointValues, docValues, stored). Legacy numerics
|
||||
* is configurable this way too.
|
||||
*/
|
||||
public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix, FieldType fieldType) {
|
||||
super(ctx, fieldNamePrefix);
|
||||
this.fieldNameX = fieldNamePrefix+SUFFIX_X;
|
||||
this.fieldNameY = fieldNamePrefix+SUFFIX_Y;
|
||||
|
||||
int numPairs = 0;
|
||||
if ((this.hasStored = fieldType.stored())) {
|
||||
numPairs++;
|
||||
}
|
||||
if ((this.hasDocVals = fieldType.docValuesType() != DocValuesType.NONE)) {
|
||||
numPairs++;
|
||||
}
|
||||
if ((this.hasPointVals = fieldType.pointDimensionCount() > 0)) {
|
||||
numPairs++;
|
||||
}
|
||||
if (fieldType.indexOptions() != IndexOptions.NONE && fieldType.numericType() != null) {
|
||||
if (hasPointVals) {
|
||||
throw new IllegalArgumentException("pointValues and LegacyNumericType are mutually exclusive");
|
||||
}
|
||||
if (fieldType.numericType() != FieldType.LegacyNumericType.DOUBLE) {
|
||||
throw new IllegalArgumentException(getClass() + " does not support " + fieldType.numericType());
|
||||
}
|
||||
numPairs++;
|
||||
legacyNumericFieldType = new FieldType(LegacyDoubleField.TYPE_NOT_STORED);
|
||||
legacyNumericFieldType.setNumericPrecisionStep(fieldType.numericPrecisionStep());
|
||||
legacyNumericFieldType.freeze();
|
||||
} else {
|
||||
legacyNumericFieldType = null;
|
||||
}
|
||||
this.fieldsLen = numPairs * 2;
|
||||
}
|
||||
|
||||
public void setPrecisionStep( int p ) {
|
||||
precisionStep = p;
|
||||
if (precisionStep<=0 || precisionStep>=64)
|
||||
precisionStep=Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
String getFieldNameX() {
|
||||
return fieldNameX;
|
||||
|
@ -108,12 +192,26 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
|
||||
/** @see #createIndexableFields(org.locationtech.spatial4j.shape.Shape) */
|
||||
public Field[] createIndexableFields(Point point) {
|
||||
FieldType doubleFieldType = new FieldType(LegacyDoubleField.TYPE_NOT_STORED);
|
||||
doubleFieldType.setNumericPrecisionStep(precisionStep);
|
||||
Field[] f = new Field[2];
|
||||
f[0] = new LegacyDoubleField(fieldNameX, point.getX(), doubleFieldType);
|
||||
f[1] = new LegacyDoubleField(fieldNameY, point.getY(), doubleFieldType);
|
||||
return f;
|
||||
Field[] fields = new Field[fieldsLen];
|
||||
int idx = -1;
|
||||
if (hasStored) {
|
||||
fields[++idx] = new StoredField(fieldNameX, point.getX());
|
||||
fields[++idx] = new StoredField(fieldNameY, point.getY());
|
||||
}
|
||||
if (hasDocVals) {
|
||||
fields[++idx] = new DoubleDocValuesField(fieldNameX, point.getX());
|
||||
fields[++idx] = new DoubleDocValuesField(fieldNameY, point.getY());
|
||||
}
|
||||
if (hasPointVals) {
|
||||
fields[++idx] = new DoublePoint(fieldNameX, point.getX());
|
||||
fields[++idx] = new DoublePoint(fieldNameY, point.getY());
|
||||
}
|
||||
if (legacyNumericFieldType != null) {
|
||||
fields[++idx] = new LegacyDoubleField(fieldNameX, point.getX(), legacyNumericFieldType);
|
||||
fields[++idx] = new LegacyDoubleField(fieldNameY, point.getY(), legacyNumericFieldType);
|
||||
}
|
||||
assert idx == fields.length - 1;
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -165,14 +263,27 @@ public class PointVectorStrategy extends SpatialStrategy {
|
|||
return bq.build();
|
||||
}
|
||||
|
||||
private LegacyNumericRangeQuery<Double> rangeQuery(String fieldName, Double min, Double max) {
|
||||
return LegacyNumericRangeQuery.newDoubleRange(
|
||||
fieldName,
|
||||
precisionStep,
|
||||
min,
|
||||
max,
|
||||
true,
|
||||
true);//inclusive
|
||||
/**
|
||||
* Returns a numeric range query based on FieldType
|
||||
* {@link LegacyNumericRangeQuery} is used for indexes created using {@code FieldType.LegacyNumericType}
|
||||
* {@link DoublePoint#newRangeQuery} is used for indexes created using {@link DoublePoint} fields
|
||||
*/
|
||||
private Query rangeQuery(String fieldName, Double min, Double max) {
|
||||
if (hasPointVals) {
|
||||
if (min == null) {
|
||||
min = Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
if (max == null) {
|
||||
max = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
return DoublePoint.newRangeQuery(fieldName, min, max);
|
||||
|
||||
} else if (legacyNumericFieldType != null) {// todo remove legacy numeric support in 7.0
|
||||
return LegacyNumericRangeQuery.newDoubleRange(fieldName, legacyNumericFieldType.numericPrecisionStep(), min, max, true, true);//inclusive
|
||||
}
|
||||
//TODO try doc-value range query?
|
||||
throw new UnsupportedOperationException("An index is required for this operation.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,6 @@ import java.util.Arrays;
|
|||
import java.util.List;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.spatial.bbox.BBoxStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
||||
|
@ -37,6 +32,9 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
|||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial.vector.PointVectorStrategy;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
public class DistanceStrategyTest extends StrategyTestCase {
|
||||
@ParametersFactory(argumentFormatting = "strategy=%s")
|
||||
|
@ -59,10 +57,18 @@ public class DistanceStrategyTest extends StrategyTestCase {
|
|||
strategy = new RecursivePrefixTreeStrategy(grid, "recursive_packedquad");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = new PointVectorStrategy(ctx, "pointvector");
|
||||
strategy = PointVectorStrategy.newInstance(ctx, "pointvector");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = new BBoxStrategy(ctx, "bbox");
|
||||
// Can't test this without un-inverting since PVS legacy config didn't have docValues.
|
||||
// However, note that Solr's tests use UninvertingReader and thus test this.
|
||||
// strategy = PointVectorStrategy.newLegacyInstance(ctx, "pointvector_legacy");
|
||||
// ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = BBoxStrategy.newInstance(ctx, "bbox");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = BBoxStrategy.newLegacyInstance(ctx, "bbox_legacy");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = new SerializedDVStrategy(ctx, "serialized");
|
||||
|
@ -76,22 +82,6 @@ public class DistanceStrategyTest extends StrategyTestCase {
|
|||
this.strategy = strategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
if (strategy instanceof BBoxStrategy && random().nextBoolean()) {//disable indexing sometimes
|
||||
BBoxStrategy bboxStrategy = (BBoxStrategy)strategy;
|
||||
final FieldType fieldType = new FieldType(bboxStrategy.getFieldType());
|
||||
fieldType.setIndexOptions(IndexOptions.NONE);
|
||||
bboxStrategy.setFieldType(fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsDocValues() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistanceOrder() throws IOException {
|
||||
adoc("100", ctx.makePoint(2, 1));
|
||||
|
@ -108,9 +98,9 @@ public class DistanceStrategyTest extends StrategyTestCase {
|
|||
|
||||
@Test
|
||||
public void testRecipScore() throws IOException {
|
||||
Point p100 = ctx.makePoint(2, 1);
|
||||
Point p100 = ctx.makePoint(2.02, 0.98);
|
||||
adoc("100", p100);
|
||||
Point p101 = ctx.makePoint(-1, 4);
|
||||
Point p101 = ctx.makePoint(-1.001, 4.001);
|
||||
adoc("101", p101);
|
||||
adoc("103", (Shape)null);//test score for nothing
|
||||
adoc("999", ctx.makePoint(2, 1));//test deleted
|
||||
|
|
|
@ -22,10 +22,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.TermQueryPrefixTreeStrategy;
|
||||
|
@ -36,6 +32,10 @@ import org.apache.lucene.spatial.query.SpatialArgs;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.vector.PointVectorStrategy;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
/**
|
||||
* Based off of Solr 3's SpatialFilterTest.
|
||||
|
@ -62,7 +62,10 @@ public class PortedSolr3Test extends StrategyTestCase {
|
|||
strategy = new TermQueryPrefixTreeStrategy(grid, "termquery_geohash");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = new PointVectorStrategy(ctx, "pointvector");
|
||||
strategy = PointVectorStrategy.newInstance(ctx, "pointvector");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
strategy = PointVectorStrategy.newInstance(ctx, "pointvector_legacy");
|
||||
ctorArgs.add(new Object[]{strategy.getFieldName(), strategy});
|
||||
|
||||
return ctorArgs;
|
||||
|
|
|
@ -19,8 +19,6 @@ package org.apache.lucene.spatial;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.spatial.bbox.BBoxStrategy;
|
||||
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
|
@ -34,6 +32,8 @@ import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
|||
import org.apache.lucene.spatial.vector.PointVectorStrategy;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
public class QueryEqualsHashCodeTest extends LuceneTestCase {
|
||||
|
||||
|
@ -57,8 +57,10 @@ public class QueryEqualsHashCodeTest extends LuceneTestCase {
|
|||
RecursivePrefixTreeStrategy recursive_geohash = new RecursivePrefixTreeStrategy(gridGeohash, "recursive_geohash");
|
||||
strategies.add(recursive_geohash);
|
||||
strategies.add(new TermQueryPrefixTreeStrategy(gridQuad, "termquery_quad"));
|
||||
strategies.add(new PointVectorStrategy(ctx, "pointvector"));
|
||||
strategies.add(new BBoxStrategy(ctx, "bbox"));
|
||||
strategies.add(PointVectorStrategy.newInstance(ctx, "pointvector"));
|
||||
strategies.add(PointVectorStrategy.newLegacyInstance(ctx, "pointvector_legacy"));
|
||||
strategies.add(BBoxStrategy.newInstance(ctx, "bbox"));
|
||||
strategies.add(BBoxStrategy.newLegacyInstance(ctx, "bbox_legacy"));
|
||||
final SerializedDVStrategy serialized = new SerializedDVStrategy(ctx, "serialized");
|
||||
strategies.add(serialized);
|
||||
strategies.add(new CompositeSpatialStrategy("composite", recursive_geohash, serialized));
|
||||
|
|
|
@ -18,34 +18,26 @@ package org.apache.lucene.spatial;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.uninverting.UninvertingReader;
|
||||
import org.apache.lucene.uninverting.UninvertingReader.Type;
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.LuceneTestCase.SuppressSysoutChecks;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomDouble;
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomGaussian;
|
||||
|
@ -66,36 +58,16 @@ public abstract class SpatialTestCase extends LuceneTestCase {
|
|||
|
||||
protected SpatialContext ctx;//subclass must initialize
|
||||
|
||||
protected Map<String,Type> uninvertMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// TODO: change this module to index docvalues instead of uninverting
|
||||
uninvertMap.clear();
|
||||
uninvertMap.put("pointvector__x", Type.LEGACY_DOUBLE);
|
||||
uninvertMap.put("pointvector__y", Type.LEGACY_DOUBLE);
|
||||
|
||||
directory = newDirectory();
|
||||
final Random random = random();
|
||||
analyzer = new MockAnalyzer(random);
|
||||
indexWriter = new RandomIndexWriter(random,directory, newIWConfig(random, analyzer));
|
||||
indexReader = UninvertingReader.wrap(indexWriter.getReader(), uninvertMap);
|
||||
analyzer = new MockAnalyzer(random());
|
||||
indexWriter = new RandomIndexWriter(random(), directory, LuceneTestCase.newIndexWriterConfig(random(), analyzer));
|
||||
indexReader = indexWriter.getReader();
|
||||
indexSearcher = newSearcher(indexReader);
|
||||
}
|
||||
|
||||
protected IndexWriterConfig newIWConfig(Random random, Analyzer analyzer) {
|
||||
final IndexWriterConfig indexWriterConfig = LuceneTestCase.newIndexWriterConfig(random, analyzer);
|
||||
//TODO can we randomly choose a doc-values supported format?
|
||||
if (needsDocValues())
|
||||
indexWriterConfig.setCodec( TestUtil.getDefaultCodec());
|
||||
return indexWriterConfig;
|
||||
}
|
||||
|
||||
protected boolean needsDocValues() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
IOUtils.close(indexWriter, indexReader, analyzer, directory);
|
||||
|
|
|
@ -19,12 +19,6 @@ package org.apache.lucene.spatial.bbox;
|
|||
import java.io.IOException;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
|
@ -36,14 +30,15 @@ import org.apache.lucene.spatial.query.SpatialOperation;
|
|||
import org.apache.lucene.spatial.util.ShapeAreaValueSource;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
|
||||
public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
||||
|
||||
@Override
|
||||
protected boolean needsDocValues() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Shape randomIndexedShape() {
|
||||
Rectangle world = ctx.getWorldBounds();
|
||||
|
@ -97,13 +92,17 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
|||
factory.worldBounds = new RectangleImpl(-300, 300, -100, 100, null);
|
||||
this.ctx = factory.newSpatialContext();
|
||||
}
|
||||
this.strategy = new BBoxStrategy(ctx, "bbox");
|
||||
// randomly test legacy (numeric) and point based bbox strategy
|
||||
if (random().nextBoolean()) {
|
||||
this.strategy = BBoxStrategy.newInstance(ctx, "bbox");
|
||||
} else {
|
||||
this.strategy = BBoxStrategy.newLegacyInstance(ctx, "bbox");
|
||||
}
|
||||
//test we can disable docValues for predicate tests
|
||||
if (random().nextBoolean()) {
|
||||
BBoxStrategy bboxStrategy = (BBoxStrategy) strategy;
|
||||
FieldType fieldType = new FieldType(bboxStrategy.getFieldType());
|
||||
FieldType fieldType = new FieldType(((BBoxStrategy)strategy).getFieldType());
|
||||
fieldType.setDocValuesType(DocValuesType.NONE);
|
||||
bboxStrategy.setFieldType(fieldType);
|
||||
strategy = new BBoxStrategy(ctx, strategy.getFieldName(), fieldType);
|
||||
}
|
||||
for (SpatialOperation operation : SpatialOperation.values()) {
|
||||
if (operation == SpatialOperation.Overlaps)
|
||||
|
@ -189,7 +188,11 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
|||
|
||||
private void setupGeo() {
|
||||
this.ctx = SpatialContext.GEO;
|
||||
this.strategy = new BBoxStrategy(ctx, "bbox");
|
||||
if (random().nextBoolean()) {
|
||||
this.strategy = BBoxStrategy.newInstance(ctx, "bbox");
|
||||
} else {
|
||||
this.strategy = BBoxStrategy.newLegacyInstance(ctx, "bbox");
|
||||
}
|
||||
}
|
||||
|
||||
// OLD STATIC TESTS (worthless?)
|
||||
|
@ -225,8 +228,29 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
|||
return shape.getBoundingBox();
|
||||
}
|
||||
|
||||
private BBoxStrategy setupNeedsDocValuesOnly() throws IOException {
|
||||
this.ctx = SpatialContext.GEO;
|
||||
FieldType fieldType;
|
||||
// random legacy or not legacy
|
||||
String FIELD_PREFIX = "bbox";
|
||||
if (random().nextBoolean()) {
|
||||
fieldType = new FieldType(BBoxStrategy.DEFAULT_FIELDTYPE);
|
||||
if (random().nextBoolean()) {
|
||||
fieldType.setDimensions(0, 0);
|
||||
}
|
||||
} else {
|
||||
fieldType = new FieldType(BBoxStrategy.LEGACY_FIELDTYPE);
|
||||
if (random().nextBoolean()) {
|
||||
fieldType.setIndexOptions(IndexOptions.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
strategy = new BBoxStrategy(ctx, FIELD_PREFIX, fieldType);
|
||||
return (BBoxStrategy)strategy;
|
||||
}
|
||||
|
||||
public void testOverlapRatio() throws IOException {
|
||||
setupGeo();
|
||||
setupNeedsDocValuesOnly();
|
||||
|
||||
//Simply assert null shape results in 0
|
||||
adoc("999", (Shape) null);
|
||||
|
@ -279,14 +303,7 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
|||
}
|
||||
|
||||
public void testAreaValueSource() throws IOException {
|
||||
setupGeo();
|
||||
//test we can disable indexed for this test
|
||||
BBoxStrategy bboxStrategy = (BBoxStrategy) strategy;
|
||||
if (random().nextBoolean()) {
|
||||
FieldType fieldType = new FieldType(bboxStrategy.getFieldType());
|
||||
fieldType.setIndexOptions(IndexOptions.NONE);
|
||||
bboxStrategy.setFieldType(fieldType);
|
||||
}
|
||||
BBoxStrategy bboxStrategy = setupNeedsDocValuesOnly();
|
||||
|
||||
adoc("100", ctx.makeRectangle(0, 20, 40, 80));
|
||||
adoc("999", (Shape) null);
|
||||
|
@ -298,4 +315,5 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase {
|
|||
checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, true, 2.0),
|
||||
new float[]{783.86f, 0f}, 0.01f); // testing with a different multiplier
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,12 +19,6 @@ package org.apache.lucene.spatial.composite;
|
|||
import java.io.IOException;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
|
@ -33,6 +27,12 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.SpatialContextFactory;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomBoolean;
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomDouble;
|
||||
|
@ -101,11 +101,6 @@ public class CompositeStrategyTest extends RandomSpatialOpStrategyTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsDocValues() {
|
||||
return true;//due to SerializedDVStrategy
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Shape randomIndexedShape() {
|
||||
return randomShape();
|
||||
|
|
|
@ -18,11 +18,11 @@ package org.apache.lucene.spatial.serialized;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.apache.lucene.spatial.SpatialMatchConcern;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
|
||||
public class SerializedStrategyTest extends StrategyTestCase {
|
||||
|
||||
|
@ -34,11 +34,6 @@ public class SerializedStrategyTest extends StrategyTestCase {
|
|||
this.strategy = new SerializedDVStrategy(ctx, "serialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsDocValues() {
|
||||
return (strategy instanceof SerializedDVStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicOperaions() throws IOException {
|
||||
getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
|
||||
|
|
|
@ -21,10 +21,6 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
|
@ -33,13 +29,17 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoStandardCircle;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPath;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPoint;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
|
||||
import org.apache.lucene.spatial3d.geom.GeoShape;
|
||||
import org.apache.lucene.spatial3d.geom.GeoStandardCircle;
|
||||
import org.apache.lucene.spatial3d.geom.PlanetModel;
|
||||
import org.junit.Test;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
|
||||
import static org.locationtech.spatial4j.distance.DistanceUtils.DEGREES_TO_RADIANS;
|
||||
|
||||
|
@ -63,11 +63,6 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
return rpt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsDocValues() {
|
||||
return true;//due to SerializedDVStrategy
|
||||
}
|
||||
|
||||
private void setupStrategy() {
|
||||
//setup
|
||||
setupGeohashGrid();
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.vector;
|
||||
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.SpatialMatchConcern;
|
||||
import org.apache.lucene.spatial.StrategyTestCase;
|
||||
|
@ -26,8 +29,9 @@ import org.apache.lucene.spatial.query.SpatialArgs;
|
|||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
|
||||
public class TestPointVectorStrategy extends StrategyTestCase {
|
||||
|
||||
|
@ -36,11 +40,11 @@ public class TestPointVectorStrategy extends StrategyTestCase {
|
|||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
this.ctx = SpatialContext.GEO;
|
||||
this.strategy = new PointVectorStrategy(ctx, getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCircleShapeSupport() {
|
||||
this.strategy = PointVectorStrategy.newInstance(ctx, getClass().getSimpleName());
|
||||
Circle circle = ctx.makeCircle(ctx.makePoint(0, 0), 10);
|
||||
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, circle);
|
||||
Query query = this.strategy.makeQuery(args);
|
||||
|
@ -50,6 +54,7 @@ public class TestPointVectorStrategy extends StrategyTestCase {
|
|||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void testInvalidQueryShape() {
|
||||
this.strategy = PointVectorStrategy.newInstance(ctx, getClass().getSimpleName());
|
||||
Point point = ctx.makePoint(0, 0);
|
||||
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, point);
|
||||
this.strategy.makeQuery(args);
|
||||
|
@ -57,7 +62,45 @@ public class TestPointVectorStrategy extends StrategyTestCase {
|
|||
|
||||
@Test
|
||||
public void testCitiesIntersectsBBox() throws IOException {
|
||||
// note: does not require docValues
|
||||
if (random().nextBoolean()) {
|
||||
this.strategy = PointVectorStrategy.newInstance(ctx, getClass().getSimpleName());
|
||||
} else {
|
||||
// switch to legacy instance sometimes, which has no docValues
|
||||
this.strategy = PointVectorStrategy.newLegacyInstance(ctx, getClass().getSimpleName());
|
||||
}
|
||||
getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
|
||||
executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_Intersects_BBox);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldOptions() throws IOException, ParseException {
|
||||
// It's not stored; test it isn't.
|
||||
this.strategy = PointVectorStrategy.newInstance(ctx, getClass().getSimpleName());
|
||||
adoc("99", "POINT(-5.0 8.2)");
|
||||
commit();
|
||||
SearchResults results = executeQuery(new MatchAllDocsQuery(), 1);
|
||||
Document document = results.results.get(0).document;
|
||||
assertNull("not stored", document.getField(strategy.getFieldName() + PointVectorStrategy.SUFFIX_X));
|
||||
assertNull("not stored", document.getField(strategy.getFieldName() + PointVectorStrategy.SUFFIX_Y));
|
||||
deleteAll();
|
||||
|
||||
// Now we mark it stored. We also disable pointvalues...
|
||||
FieldType fieldType = new FieldType(PointVectorStrategy.DEFAULT_FIELDTYPE);
|
||||
fieldType.setStored(true);
|
||||
fieldType.setDimensions(0, 0);//disable point values
|
||||
this.strategy = new PointVectorStrategy(ctx, getClass().getSimpleName(), fieldType);
|
||||
adoc("99", "POINT(-5.0 8.2)");
|
||||
commit();
|
||||
results = executeQuery(new MatchAllDocsQuery(), 1);
|
||||
document = results.results.get(0).document;
|
||||
assertEquals("stored", -5.0, document.getField(strategy.getFieldName() + PointVectorStrategy.SUFFIX_X).numericValue());
|
||||
assertEquals("stored", 8.2, document.getField(strategy.getFieldName() + PointVectorStrategy.SUFFIX_Y).numericValue());
|
||||
|
||||
// Test a query fails without point values
|
||||
expectThrows(UnsupportedOperationException.class, () -> {
|
||||
SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects, ctx.makeRectangle(-10.0, 10.0, -5.0, 5.0));
|
||||
this.strategy.makeQuery(args);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.spatial.bbox.BBoxOverlapRatioValueSource;
|
||||
|
@ -31,6 +30,7 @@ import org.apache.lucene.spatial.query.SpatialArgs;
|
|||
import org.apache.lucene.spatial.util.ShapeAreaValueSource;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.search.QParser;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
|
||||
public class BBoxField extends AbstractSpatialFieldType<BBoxStrategy> implements SchemaAware {
|
||||
private static final String PARAM_QUERY_TARGET_PROPORTION = "queryTargetProportion";
|
||||
|
@ -133,7 +133,6 @@ public class BBoxField extends AbstractSpatialFieldType<BBoxStrategy> implements
|
|||
registerSubFields(schema, fieldName, numberType, booleanType);
|
||||
}
|
||||
|
||||
BBoxStrategy strategy = new BBoxStrategy(ctx, fieldName);
|
||||
//Solr's FieldType ought to expose Lucene FieldType. Instead as a hack we create a Field with a dummy value.
|
||||
final SchemaField solrNumField = new SchemaField("_", numberType);//dummy temp
|
||||
org.apache.lucene.document.FieldType luceneType =
|
||||
|
@ -145,8 +144,7 @@ public class BBoxField extends AbstractSpatialFieldType<BBoxStrategy> implements
|
|||
luceneType = new org.apache.lucene.document.FieldType(luceneType);
|
||||
luceneType.setDocValuesType(DocValuesType.NUMERIC);
|
||||
}
|
||||
strategy.setFieldType(luceneType);
|
||||
return strategy;
|
||||
return new BBoxStrategy(ctx, fieldName, luceneType);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
*/
|
||||
package org.apache.solr.schema;
|
||||
|
||||
import org.apache.lucene.spatial.vector.PointVectorStrategy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.spatial.vector.PointVectorStrategy;
|
||||
|
||||
/**
|
||||
* @see PointVectorStrategy
|
||||
* @lucene.experimental
|
||||
|
@ -77,11 +77,24 @@ public class SpatialPointVectorFieldType extends AbstractSpatialFieldType<PointV
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.apache.lucene.document.FieldType.LegacyNumericType getNumericType() {
|
||||
return org.apache.lucene.document.FieldType.LegacyNumericType.DOUBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PointVectorStrategy newSpatialStrategy(String fieldName) {
|
||||
PointVectorStrategy strategy = new PointVectorStrategy(ctx, fieldName);
|
||||
strategy.setPrecisionStep(precisionStep);
|
||||
return strategy;
|
||||
// TODO update to how BBoxField does things
|
||||
if (this.getNumericType() != null) {
|
||||
// create strategy based on legacy numerics
|
||||
// todo remove in 7.0
|
||||
org.apache.lucene.document.FieldType fieldType =
|
||||
new org.apache.lucene.document.FieldType(PointVectorStrategy.LEGACY_FIELDTYPE);
|
||||
fieldType.setNumericPrecisionStep(precisionStep);
|
||||
return new PointVectorStrategy(ctx, fieldName, fieldType);
|
||||
} else {
|
||||
return PointVectorStrategy.newInstance(ctx, fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue