mirror of https://github.com/apache/lucene.git
SOLR-8859: Fix AbstractSpatialFieldType to not cycle through all Spatial4j provided formats.
And Fix RptWithGeometrySpatialField to be less brittle on init()
This commit is contained in:
parent
66cd0edc52
commit
fb37b3eb8c
|
@ -81,8 +81,8 @@ New Features
|
||||||
https://github.com/locationtech/spatial4j/blob/master/FORMATS.md
|
https://github.com/locationtech/spatial4j/blob/master/FORMATS.md
|
||||||
To return the FeatureCollection as the root element, add '&omitHeader=true" (ryan)
|
To return the FeatureCollection as the root element, add '&omitHeader=true" (ryan)
|
||||||
|
|
||||||
* SOLR-8859: AbstractSpatialFieldType will now convert Shapes to/from Strings
|
* SOLR-8859: Spatial fields like RPT can now be configured to use Spatial4j registered shape formats
|
||||||
using the SpatialContext. (ryan)
|
e.g. via format="GeoJSON". (ryan, David Smiley)
|
||||||
|
|
||||||
* SOLR-445: new ToleranteUpdateProcessorFactory to support skipping update commands that cause
|
* SOLR-445: new ToleranteUpdateProcessorFactory to support skipping update commands that cause
|
||||||
failures when sending multiple updates in a single request.
|
failures when sending multiple updates in a single request.
|
||||||
|
|
|
@ -26,9 +26,11 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.document.StoredField;
|
import org.apache.lucene.document.StoredField;
|
||||||
import org.apache.lucene.index.IndexableField;
|
import org.apache.lucene.index.IndexableField;
|
||||||
|
@ -42,7 +44,6 @@ import org.apache.lucene.spatial.SpatialStrategy;
|
||||||
import org.apache.lucene.spatial.query.SpatialArgs;
|
import org.apache.lucene.spatial.query.SpatialArgs;
|
||||||
import org.apache.lucene.spatial.query.SpatialArgsParser;
|
import org.apache.lucene.spatial.query.SpatialArgsParser;
|
||||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.params.SolrParams;
|
import org.apache.solr.common.params.SolrParams;
|
||||||
import org.apache.solr.response.TextResponseWriter;
|
import org.apache.solr.response.TextResponseWriter;
|
||||||
|
@ -64,10 +65,6 @@ import org.locationtech.spatial4j.shape.Shape;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract base class for Solr FieldTypes based on a Lucene 4 {@link SpatialStrategy}.
|
* Abstract base class for Solr FieldTypes based on a Lucene 4 {@link SpatialStrategy}.
|
||||||
*
|
*
|
||||||
|
@ -141,24 +138,21 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
|
||||||
}
|
}
|
||||||
|
|
||||||
final SupportedFormats fmts = ctx.getFormats();
|
final SupportedFormats fmts = ctx.getFormats();
|
||||||
final String format = args.remove(FORMAT);
|
String format = args.remove(FORMAT);
|
||||||
if (format != null) {
|
if (format == null) {
|
||||||
shapeWriter = fmts.getWriter(format);
|
format = "WKT";
|
||||||
shapeReader = fmts.getReader(format);
|
|
||||||
if(shapeWriter==null) {
|
|
||||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
|
||||||
"Unknown Shape Format: "+ format);
|
|
||||||
}
|
|
||||||
if(shapeReader==null) {
|
|
||||||
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
|
||||||
"Unknown Shape Format: "+ format);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
shapeWriter = fmts.getWriter(format);
|
||||||
// Otherwise, pick the first supported reader/writer
|
shapeReader = fmts.getReader(format);
|
||||||
shapeWriter = fmts.getWriters().get(0);
|
if(shapeWriter==null) {
|
||||||
shapeReader = fmts.getReaders().get(0);
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
|
"Unknown Shape Format: "+ format);
|
||||||
}
|
}
|
||||||
|
if(shapeReader==null) {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
|
||||||
|
"Unknown Shape Format: "+ format);
|
||||||
|
}
|
||||||
|
|
||||||
argsParser = newSpatialArgsParser();
|
argsParser = newSpatialArgsParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,23 +228,29 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
|
||||||
|
|
||||||
/** Create a {@link Shape} from the input string */
|
/** Create a {@link Shape} from the input string */
|
||||||
public Shape parseShape(String str) {
|
public Shape parseShape(String str) {
|
||||||
|
str = str.trim();
|
||||||
if (str.length() == 0)
|
if (str.length() == 0)
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "empty string shape");
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "empty string shape");
|
||||||
|
|
||||||
Shape shape = null;
|
// If the first char is promising, try to parse with SpatialUtils.parsePoint
|
||||||
if(shapeReader!=null) {
|
char firstChar = str.charAt(0);
|
||||||
shape = shapeReader.readIfSupported(str);
|
if (firstChar == '+' || firstChar == '-' || (firstChar >= '0' && firstChar <= '9')) {
|
||||||
|
try {
|
||||||
|
return SpatialUtils.parsePoint(str, ctx);
|
||||||
|
} catch (Exception e) {//ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(shape==null) {
|
try {
|
||||||
// Try all supported formats
|
return shapeReader.read(str);
|
||||||
shape = ctx.getFormats().read(str);
|
} catch (Exception e) {
|
||||||
|
String msg = "Unable to parse shape given formats" +
|
||||||
|
" \"lat,lon\", \"x y\" or as " + shapeReader.getFormatName() + " because " + e;
|
||||||
|
if (!msg.contains(str)) {
|
||||||
|
msg += " input: " + str;
|
||||||
|
}
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, msg, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(shape==null) {
|
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse shape from: "+str);
|
|
||||||
}
|
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,11 +259,7 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
|
||||||
* The format can be selected using the initParam <code>format={WKT|GeoJSON}</code>
|
* The format can be selected using the initParam <code>format={WKT|GeoJSON}</code>
|
||||||
*/
|
*/
|
||||||
public String shapeToString(Shape shape) {
|
public String shapeToString(Shape shape) {
|
||||||
if(shapeWriter!=null) {
|
return shapeWriter.toString(shape);
|
||||||
return shapeWriter.toString(shape);
|
|
||||||
}
|
|
||||||
// This will only happen if the context does not have any writers
|
|
||||||
throw new SolrException(ErrorCode.SERVER_ERROR, "ShapeWriter not configured");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called from {@link #getStrategy(String)} upon first use by fieldName. } */
|
/** Called from {@link #getStrategy(String)} upon first use by fieldName. } */
|
||||||
|
|
|
@ -18,11 +18,9 @@ package org.apache.solr.schema;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.locationtech.spatial4j.context.SpatialContext;
|
|
||||||
import org.locationtech.spatial4j.shape.Shape;
|
|
||||||
import org.locationtech.spatial4j.shape.jts.JtsGeometry;
|
|
||||||
import org.apache.lucene.analysis.Analyzer;
|
import org.apache.lucene.analysis.Analyzer;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.queries.function.FunctionValues;
|
import org.apache.lucene.queries.function.FunctionValues;
|
||||||
|
@ -36,6 +34,9 @@ import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.core.SolrCore;
|
import org.apache.solr.core.SolrCore;
|
||||||
import org.apache.solr.request.SolrRequestInfo;
|
import org.apache.solr.request.SolrRequestInfo;
|
||||||
import org.apache.solr.search.SolrCache;
|
import org.apache.solr.search.SolrCache;
|
||||||
|
import org.locationtech.spatial4j.context.SpatialContext;
|
||||||
|
import org.locationtech.spatial4j.shape.Shape;
|
||||||
|
import org.locationtech.spatial4j.shape.jts.JtsGeometry;
|
||||||
|
|
||||||
/** A Solr Spatial FieldType based on {@link CompositeSpatialStrategy}.
|
/** A Solr Spatial FieldType based on {@link CompositeSpatialStrategy}.
|
||||||
* @lucene.experimental */
|
* @lucene.experimental */
|
||||||
|
@ -48,7 +49,8 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init(IndexSchema schema, Map<String, String> args) {
|
protected void init(IndexSchema schema, Map<String, String> args) {
|
||||||
// Do NOT call super.init(); instead we delegate to an RPT field. Admittedly this is error prone.
|
Map<String, String> origArgs = new HashMap<>(args); // clone so we can feed it to an aggregated field type
|
||||||
|
super.init(schema, origArgs);
|
||||||
|
|
||||||
//TODO Move this check to a call from AbstractSpatialFieldType.createFields() so the type can declare
|
//TODO Move this check to a call from AbstractSpatialFieldType.createFields() so the type can declare
|
||||||
// if it supports multi-valued or not. It's insufficient here; we can't see if you set multiValued on the field.
|
// if it supports multi-valued or not. It's insufficient here; we can't see if you set multiValued on the field.
|
||||||
|
@ -65,6 +67,7 @@ public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<Compos
|
||||||
rptFieldType.setTypeName(getTypeName());
|
rptFieldType.setTypeName(getTypeName());
|
||||||
rptFieldType.properties = properties;
|
rptFieldType.properties = properties;
|
||||||
rptFieldType.init(schema, args);
|
rptFieldType.init(schema, args);
|
||||||
|
|
||||||
rptFieldType.argsParser = argsParser = newSpatialArgsParser();
|
rptFieldType.argsParser = argsParser = newSpatialArgsParser();
|
||||||
this.ctx = rptFieldType.ctx;
|
this.ctx = rptFieldType.ctx;
|
||||||
this.distanceUnits = rptFieldType.distanceUnits;
|
this.distanceUnits = rptFieldType.distanceUnits;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.core.AbstractBadConfigTestBase;
|
import org.apache.solr.core.AbstractBadConfigTestBase;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -203,8 +204,8 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testShapeToFromStringWKT() throws Exception {
|
public void testShapeToFromStringWKT() throws Exception {
|
||||||
// Check WKT
|
setupRPTField("miles", "true", "WKT", random().nextBoolean()
|
||||||
setupRPTField("miles", "true", "WKT");
|
? new SpatialRecursivePrefixTreeFieldType() : new RptWithGeometrySpatialField());
|
||||||
|
|
||||||
AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
|
AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
|
||||||
h.getCore().getLatestSchema().getField("geo").getType();
|
h.getCore().getLatestSchema().getField("geo").getType();
|
||||||
|
@ -214,11 +215,20 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
String out = ftype.shapeToString(shape);
|
String out = ftype.shapeToString(shape);
|
||||||
|
|
||||||
assertEquals(wkt, out);
|
assertEquals(wkt, out);
|
||||||
|
|
||||||
|
//assert fails GeoJSON
|
||||||
|
try {
|
||||||
|
ftype.parseShape("{\"type\":\"Point\",\"coordinates\":[1,2]}");
|
||||||
|
fail("Should not parse GeoJSON if told format is WKT");
|
||||||
|
} catch (SolrException e) {// expected
|
||||||
|
System.out.println(e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testShapeToFromStringGeoJSON() throws Exception {
|
public void testShapeToFromStringGeoJSON() throws Exception {
|
||||||
// Check WKT
|
setupRPTField("miles", "true", "GeoJSON", random().nextBoolean()
|
||||||
setupRPTField("miles", "true", "GeoJSON");
|
? new SpatialRecursivePrefixTreeFieldType() : new RptWithGeometrySpatialField());
|
||||||
|
|
||||||
AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
|
AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
|
||||||
h.getCore().getLatestSchema().getField("geo").getType();
|
h.getCore().getLatestSchema().getField("geo").getType();
|
||||||
|
@ -230,7 +240,7 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
assertEquals(json, out);
|
assertEquals(json, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRPTField(String distanceUnits, String geo, String format) throws Exception {
|
private void setupRPTField(String distanceUnits, String geo, String format, FieldType fieldType) throws Exception {
|
||||||
deleteCore();
|
deleteCore();
|
||||||
File managedSchemaFile = new File(tmpConfDir, "managed-schema");
|
File managedSchemaFile = new File(tmpConfDir, "managed-schema");
|
||||||
Files.delete(managedSchemaFile.toPath()); // Delete managed-schema so it won't block parsing a new schema
|
Files.delete(managedSchemaFile.toPath()); // Delete managed-schema so it won't block parsing a new schema
|
||||||
|
@ -243,7 +253,9 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
|
|
||||||
IndexSchema oldSchema = h.getCore().getLatestSchema();
|
IndexSchema oldSchema = h.getCore().getLatestSchema();
|
||||||
|
|
||||||
SpatialRecursivePrefixTreeFieldType rptFieldType = new SpatialRecursivePrefixTreeFieldType();
|
if (fieldType == null) {
|
||||||
|
fieldType = new SpatialRecursivePrefixTreeFieldType();
|
||||||
|
}
|
||||||
Map<String, String> rptMap = new HashMap<String,String>();
|
Map<String, String> rptMap = new HashMap<String,String>();
|
||||||
if(distanceUnits!=null)
|
if(distanceUnits!=null)
|
||||||
rptMap.put("distanceUnits", distanceUnits);
|
rptMap.put("distanceUnits", distanceUnits);
|
||||||
|
@ -252,9 +264,9 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
if(format!=null) {
|
if(format!=null) {
|
||||||
rptMap.put("format", format);
|
rptMap.put("format", format);
|
||||||
}
|
}
|
||||||
rptFieldType.init(oldSchema, rptMap);
|
fieldType.init(oldSchema, rptMap);
|
||||||
rptFieldType.setTypeName("location_rpt");
|
fieldType.setTypeName("location_rpt");
|
||||||
SchemaField newField = new SchemaField("geo", rptFieldType, SchemaField.STORED | SchemaField.INDEXED, null);
|
SchemaField newField = new SchemaField("geo", fieldType, SchemaField.STORED | SchemaField.INDEXED, null);
|
||||||
IndexSchema newSchema = oldSchema.addField(newField);
|
IndexSchema newSchema = oldSchema.addField(newField);
|
||||||
|
|
||||||
h.getCore().setLatestSchema(newSchema);
|
h.getCore().setLatestSchema(newSchema);
|
||||||
|
@ -263,6 +275,6 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRPTField(String distanceUnits, String geo) throws Exception {
|
private void setupRPTField(String distanceUnits, String geo) throws Exception {
|
||||||
setupRPTField(distanceUnits, geo, null);
|
setupRPTField(distanceUnits, geo, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue