You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by nk...@apache.org on 2016/03/31 01:15:15 UTC

lucene-solr:master: * 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, s

Repository: lucene-solr
Updated Branches:
  refs/heads/master 5e5fd6625 -> e1b45568b


* 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.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/e1b45568
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/e1b45568
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/e1b45568

Branch: refs/heads/master
Commit: e1b45568b41bf67b48baae7b8fec5793300a6814
Parents: 5e5fd66
Author: nknize <nk...@apache.org>
Authored: Wed Mar 30 18:14:20 2016 -0500
Committer: nknize <nk...@apache.org>
Committed: Wed Mar 30 18:14:20 2016 -0500

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   6 +
 .../lucene/spatial/bbox/BBoxStrategy.java       | 318 +++++++++++++------
 .../spatial/vector/PointVectorStrategy.java     | 177 +++++++++--
 .../lucene/spatial/DistanceStrategyTest.java    |  40 +--
 .../apache/lucene/spatial/PortedSolr3Test.java  |  13 +-
 .../lucene/spatial/QueryEqualsHashCodeTest.java |  10 +-
 .../apache/lucene/spatial/SpatialTestCase.java  |  42 +--
 .../lucene/spatial/bbox/TestBBoxStrategy.java   |  68 ++--
 .../composite/CompositeStrategyTest.java        |  17 +-
 .../serialized/SerializedStrategyTest.java      |   7 +-
 .../lucene/spatial/spatial4j/Geo3dRptTest.java  |  15 +-
 .../spatial/vector/TestPointVectorStrategy.java |  55 +++-
 .../java/org/apache/solr/schema/BBoxField.java  |   6 +-
 .../schema/SpatialPointVectorFieldType.java     |  23 +-
 14 files changed, 528 insertions(+), 269 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index ba112ae..e86f44d 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -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)
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
index 253b290..63a1138 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
@@ -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
+
+  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;
 
-  protected FieldType fieldType;//for the 4 numbers
-  protected FieldType xdlFieldType;
+  /**
+   * 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);
+  }
 
-  public BBoxStrategy(SpatialContext ctx, String fieldNamePrefix) {
+  /**
+   * 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);
-  }
+    fieldType.freeze();
+    this.optionsFieldType = fieldType;
 
-  private int getPrecisionStep() {
-    return fieldType.numericPrecisionStep();
-  }
+    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;
+    }
 
-  public FieldType getFieldType() {
-    return fieldType;
+    if (hasPointVals || legacyNumericFieldType != null) { // if we have an index...
+      xdlFieldType = new FieldType(StringField.TYPE_NOT_STORED);
+      xdlFieldType.setIndexOptions(IndexOptions.DOCS);
+      xdlFieldType.freeze();
+    } else {
+      xdlFieldType = null;
+    }
+
+    this.fieldsLen = numQuads * 4 + (xdlFieldType != null ? 1 : 0);
   }
 
-  /** 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.
-    xdlFieldType = new FieldType(StringField.TYPE_NOT_STORED);
-    xdlFieldType.setStored(fieldType.stored());
-    xdlFieldType.setIndexOptions(fieldType.indexOptions());
-    xdlFieldType.freeze();
+  /** 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,40 +218,41 @@ 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);
-    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;
+  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());
     }
-
-    //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();
+    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;
   }
 
+
   //---------------------------------
   // 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) {
-    BytesRefBuilder bytes = new BytesRefBuilder();
-    LegacyNumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(number), 0, bytes);
-    return new TermQuery(new Term(field, bytes.get()));
+    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.");
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
index f5f5f34..197547c 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
@@ -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 &amp; 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,26 +76,105 @@ 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.
+
+  /**
+   * 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);
+  }
 
-  public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix) {
+  /**
+   * 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;
-  }
 
-  public void setPrecisionStep( int p ) {
-    precisionStep = p;
-    if (precisionStep<=0 || precisionStep>=64)
-      precisionStep=Integer.MAX_VALUE;
+    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;
   }
 
+
   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.");
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java
index 1602679..d54e1c9 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java
@@ -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});
+
+//  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 = new BBoxStrategy(ctx, "bbox");
+    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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/PortedSolr3Test.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/PortedSolr3Test.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/PortedSolr3Test.java
index a081497..f55daf6 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/PortedSolr3Test.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/PortedSolr3Test.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java
index 5dbb8f8..c14fe54 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/QueryEqualsHashCodeTest.java
@@ -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));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialTestCase.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialTestCase.java
index 8ccb9af..9f4ba02 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialTestCase.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/SpatialTestCase.java
@@ -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);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
index 20a7202..01e9259 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
@@ -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,15 +30,16 @@ 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();
     if (random().nextInt(10) == 0) // increased chance of getting one of these
@@ -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
   }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java
index 7d49e8b..e74c2b5 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/composite/CompositeStrategyTest.java
@@ -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;
@@ -102,11 +102,6 @@ public class CompositeStrategyTest extends RandomSpatialOpStrategyTestCase {
   }
 
   @Override
-  protected boolean needsDocValues() {
-    return true;//due to SerializedDVStrategy
-  }
-
-  @Override
   protected Shape randomIndexedShape() {
     return randomShape();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
index 6a73d23..8466ae1 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/serialized/SerializedStrategyTest.java
@@ -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);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
index e62b857..2bef2b7 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
@@ -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();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
index 69f8c4d..ac5ab95 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/vector/TestPointVectorStrategy.java
@@ -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);
+    });
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/solr/core/src/java/org/apache/solr/schema/BBoxField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/BBoxField.java b/solr/core/src/java/org/apache/solr/schema/BBoxField.java
index f655e68..e41d3c6 100644
--- a/solr/core/src/java/org/apache/solr/schema/BBoxField.java
+++ b/solr/core/src/java/org/apache/solr/schema/BBoxField.java
@@ -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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e1b45568/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java b/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
index 99bf3fe..18d80a3 100644
--- a/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
@@ -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
@@ -78,10 +78,23 @@ 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);
+    }
   }
 
 }