You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ry...@apache.org on 2016/03/16 20:53:59 UTC
lucene-solr:branch_6x: SOLR-8859: read/write Shapes to String
Repository: lucene-solr
Updated Branches:
refs/heads/branch_6x 5731331be -> 7687667b5
SOLR-8859: read/write Shapes to String
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7687667b
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7687667b
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7687667b
Branch: refs/heads/branch_6x
Commit: 7687667b5ff7867249762d104707a91834d30ce3
Parents: 5731331
Author: Ryan McKinley <ry...@apache.org>
Authored: Wed Mar 16 12:52:00 2016 -0700
Committer: Ryan McKinley <ry...@apache.org>
Committed: Wed Mar 16 12:52:51 2016 -0700
----------------------------------------------------------------------
solr/CHANGES.txt | 2 +
.../solr/schema/AbstractSpatialFieldType.java | 80 +++++++++++++-------
.../org/apache/solr/schema/DateRangeField.java | 4 +-
.../solr/schema/SpatialRPTFieldTypeTest.java | 38 +++++++++-
4 files changed, 95 insertions(+), 29 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7687667b/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 6adee44..b89aae1 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -42,6 +42,8 @@ New Features
https://github.com/locationtech/spatial4j/blob/master/FORMATS.md
To return the FeatureCollection as the root element, add '&omitHeader=true" (ryan)
+* SOLR-8859: AbstractSpatialFieldType will now convert Shapes to/from Strings
+ using the SpatialContext. (ryan)
Bug Fixes
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7687667b/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java b/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
index e5fd8c6..7addb20 100644
--- a/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
@@ -28,7 +28,6 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.IndexableField;
@@ -44,6 +43,7 @@ import org.apache.lucene.spatial.query.SpatialArgsParser;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.uninverting.UninvertingReader.Type;
import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.response.TextResponseWriter;
import org.apache.solr.search.QParser;
@@ -60,6 +60,9 @@ import com.google.common.cache.CacheBuilder;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.context.SpatialContextFactory;
import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.io.ShapeReader;
+import org.locationtech.spatial4j.io.ShapeWriter;
+import org.locationtech.spatial4j.io.SupportedFormats;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
@@ -83,11 +86,17 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
public static final String RECIP_DISTANCE = "recipDistance";
public static final String NONE = "none";
+ /** Optional param to pick the string conversion */
+ public static final String FORMAT = "format";
+
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
protected SpatialContext ctx;
protected SpatialArgsParser argsParser;
+ protected ShapeWriter shapeWriter;
+ protected ShapeReader shapeReader;
+
private final Cache<String, T> fieldStrategyCache = CacheBuilder.newBuilder().build();
protected DistanceUnits distanceUnits;
@@ -130,6 +139,25 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
" on field types with class "+getClass().getSimpleName());
}
+ final SupportedFormats fmts = ctx.getFormats();
+ final String format = args.remove(FORMAT);
+ if (format != null) {
+ shapeWriter = fmts.getWriter(format);
+ 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 {
+ // Otherwise, pick the first supported reader/writer
+ shapeWriter = fmts.getWriters().get(0);
+ shapeReader = fmts.getReaders().get(0);
+ }
argsParser = newSpatialArgsParser();
}
@@ -203,38 +231,38 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
return (shapeStr == null) ? shapeToString(shape) : shapeStr;
}
- protected Shape parseShape(String str) {
+ /** Create a {@link Shape} from the input string */
+ public Shape parseShape(String str) {
if (str.length() == 0)
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "empty string shape");
- if (Character.isLetter(str.charAt(0))) {//WKT starts with a letter
- try {
- return ctx.readShapeFromWkt(str);
- } catch (Exception e) {
- String message = e.getMessage();
- if (!message.contains(str))
- message = "Couldn't parse shape '" + str + "' because: " + message;
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, e);
- }
- } else {
- return SpatialUtils.parsePointSolrException(str, ctx);
+
+ Shape shape = null;
+ if(shapeReader!=null) {
+ shape = shapeReader.readIfSupported(str);
+ }
+
+ if(shape==null) {
+ // Try all supported formats
+ shape = ctx.getFormats().read(str);
+ }
+
+ if(shape==null) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse shape from: "+str);
}
+ return shape;
}
/**
- * Returns a String version of a shape to be used for the stored value. This method in Solr is only called if for some
- * reason a Shape object is passed to the field type (perhaps via a custom UpdateRequestProcessor),
- * *and* the field is marked as stored. <em>The default implementation throws an exception.</em>
- * <p>
- * Spatial4j 0.4 is probably the last release to support SpatialContext.toString(shape) but it's deprecated with no
- * planned replacement. Shapes do have a toString() method but they are generally internal/diagnostic and not
- * standard WKT.
- * The solution is subclassing and calling ctx.toString(shape) or directly using LegacyShapeReadWriterFormat or
- * passing in some sort of custom wrapped shape that holds a reference to a String or can generate it.
+ * Returns a String version of a shape to be used for the stored value.
+ *
+ * The format can be selected using the initParam <code>format={WKT|GeoJSON}</code>
*/
- protected String shapeToString(Shape shape) {
-// return ctx.toString(shape);
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
- "Getting a String from a Shape is no longer possible. See javadocs for commentary.");
+ public String shapeToString(Shape shape) {
+ if(shapeWriter!=null) {
+ 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. } */
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7687667b/solr/core/src/java/org/apache/solr/schema/DateRangeField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DateRangeField.java b/solr/core/src/java/org/apache/solr/schema/DateRangeField.java
index 95b441a..faf049b 100644
--- a/solr/core/src/java/org/apache/solr/schema/DateRangeField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DateRangeField.java
@@ -82,7 +82,7 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRan
}
@Override
- protected NRShape parseShape(String str) {
+ public NRShape parseShape(String str) {
if (str.contains(" TO ")) {
//TODO parsing range syntax doesn't support DateMath on either side or exclusive/inclusive
try {
@@ -121,7 +121,7 @@ public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRan
}
@Override
- protected String shapeToString(Shape shape) {
+ public String shapeToString(Shape shape) {
if (shape instanceof UnitNRShape) {
UnitNRShape unitShape = (UnitNRShape) shape;
if (unitShape.getLevel() == tree.getMaxLevels()) {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7687667b/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java b/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java
index 479a7fe..f341832 100644
--- a/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java
@@ -24,6 +24,7 @@ import org.apache.commons.io.FileUtils;
import org.apache.solr.core.AbstractBadConfigTestBase;
import org.junit.After;
import org.junit.Before;
+import org.locationtech.spatial4j.shape.Shape;
public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
@@ -201,7 +202,35 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
);
}
- private void setupRPTField(String distanceUnits, String geo) throws Exception {
+ public void testShapeToFromStringWKT() throws Exception {
+ // Check WKT
+ setupRPTField("miles", "true", "WKT");
+
+ AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
+ h.getCore().getLatestSchema().getField("geo").getType();
+
+ String wkt = "POINT (1 2)";
+ Shape shape = ftype.parseShape(wkt);
+ String out = ftype.shapeToString(shape);
+
+ assertEquals(wkt, out);
+ }
+
+ public void testShapeToFromStringGeoJSON() throws Exception {
+ // Check WKT
+ setupRPTField("miles", "true", "GeoJSON");
+
+ AbstractSpatialFieldType ftype = (AbstractSpatialFieldType)
+ h.getCore().getLatestSchema().getField("geo").getType();
+
+ String json = "{\"type\":\"Point\",\"coordinates\":[1,2]}";
+ Shape shape = ftype.parseShape(json);
+ String out = ftype.shapeToString(shape);
+
+ assertEquals(json, out);
+ }
+
+ private void setupRPTField(String distanceUnits, String geo, String format) throws Exception {
deleteCore();
File managedSchemaFile = new File(tmpConfDir, "managed-schema");
Files.delete(managedSchemaFile.toPath()); // Delete managed-schema so it won't block parsing a new schema
@@ -220,6 +249,9 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
rptMap.put("distanceUnits", distanceUnits);
if(geo!=null)
rptMap.put("geo", geo);
+ if(format!=null) {
+ rptMap.put("format", format);
+ }
rptFieldType.init(oldSchema, rptMap);
rptFieldType.setTypeName("location_rpt");
SchemaField newField = new SchemaField("geo", rptFieldType, SchemaField.STORED | SchemaField.INDEXED, null);
@@ -229,4 +261,8 @@ public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase {
assertU(delQ("*:*"));
}
+
+ private void setupRPTField(String distanceUnits, String geo) throws Exception {
+ setupRPTField(distanceUnits, geo, null);
+ }
}