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);
+  }
 }