You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2017/06/28 06:51:47 UTC

[08/18] lucene-solr:feature/autoscaling: LUCENE-7737: Remove spatial-extras dependency on queries module

LUCENE-7737: Remove spatial-extras dependency on queries module


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

Branch: refs/heads/feature/autoscaling
Commit: 2f2e00ffe235ba590f43bf07dabd4201e6da53c6
Parents: 1a278ae
Author: Alan Woodward <ro...@apache.org>
Authored: Tue Jun 13 09:41:23 2017 +0100
Committer: Alan Woodward <ro...@apache.org>
Committed: Tue Jun 27 15:53:39 2017 +0100

----------------------------------------------------------------------
 .../lucene/spatial-extras/spatial-extras.iml    |   5 +-
 lucene/CHANGES.txt                              |   5 +
 .../byTask/feeds/SpatialFileQueryMaker.java     |  15 +--
 .../org/apache/lucene/search/DoubleValues.java  |  21 ++++
 .../lucene/search/DoubleValuesSource.java       |   7 +-
 .../lucene/queries/function/ValueSource.java    |  63 +++++++++++
 lucene/spatial-extras/build.xml                 |   8 +-
 .../org/apache/lucene/spatial/ShapeValues.java  |  41 +++++++
 .../lucene/spatial/ShapeValuesSource.java       |  34 ++++++
 .../apache/lucene/spatial/SpatialStrategy.java  |  19 ++--
 .../bbox/BBoxOverlapRatioValueSource.java       |   7 +-
 .../spatial/bbox/BBoxSimilarityValueSource.java |  79 +++++++------
 .../lucene/spatial/bbox/BBoxStrategy.java       |  13 +--
 .../lucene/spatial/bbox/BBoxValueSource.java    |  74 +++---------
 .../composite/CompositeSpatialStrategy.java     |  14 +--
 .../spatial/composite/CompositeVerifyQuery.java |  32 ++----
 .../composite/IntersectsRPTVerifyQuery.java     |  21 ++--
 .../prefix/NumberRangePrefixTreeStrategy.java   |   4 +-
 .../spatial/prefix/PrefixTreeStrategy.java      |   4 +-
 .../serialized/SerializedDVStrategy.java        | 110 ++++--------------
 .../spatial/util/CachingDoubleValueSource.java  |  61 +++++-----
 .../util/DistanceToShapeValueSource.java        |  68 +++++------
 .../util/ReciprocalDoubleValuesSource.java      |  96 ++++++++++++++++
 .../spatial/util/ShapeAreaValueSource.java      |  67 ++++-------
 .../ShapeFieldCacheDistanceValueSource.java     |  59 +++++-----
 .../spatial/util/ShapePredicateValueSource.java | 113 -------------------
 .../spatial/util/ShapeValuesPredicate.java      |  99 ++++++++++++++++
 .../spatial/vector/DistanceValueSource.java     |  72 +++++-------
 .../spatial/vector/PointVectorStrategy.java     |  95 ++++++++++++++--
 .../lucene/spatial/DistanceStrategyTest.java    |   6 -
 .../apache/lucene/spatial/SpatialExample.java   |  12 +-
 .../apache/lucene/spatial/StrategyTestCase.java |  39 +++----
 .../lucene/spatial/spatial4j/Geo3dRptTest.java  |   2 +-
 .../Geo3dShapeRectRelationTestCase.java         |   2 +-
 .../org/apache/solr/legacy/BBoxStrategy.java    |  13 +--
 .../org/apache/solr/legacy/BBoxValueSource.java |  82 ++++----------
 .../apache/solr/legacy/DistanceValueSource.java |  81 +++++--------
 .../apache/solr/legacy/PointVectorStrategy.java |  17 ++-
 .../transform/GeoTransformerFactory.java        |  33 +++---
 .../solr/schema/AbstractSpatialFieldType.java   |  10 +-
 .../java/org/apache/solr/schema/BBoxField.java  |   8 +-
 .../solr/schema/LatLonPointSpatialField.java    |  46 ++++----
 .../schema/RptWithGeometrySpatialField.java     |  60 ++++------
 .../distance/GeoDistValueSourceParser.java      |   8 +-
 .../apache/solr/search/TestSolr4Spatial.java    |  10 +-
 45 files changed, 886 insertions(+), 849 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/dev-tools/idea/lucene/spatial-extras/spatial-extras.iml
----------------------------------------------------------------------
diff --git a/dev-tools/idea/lucene/spatial-extras/spatial-extras.iml b/dev-tools/idea/lucene/spatial-extras/spatial-extras.iml
index 6285d26..8e9d887 100644
--- a/dev-tools/idea/lucene/spatial-extras/spatial-extras.iml
+++ b/dev-tools/idea/lucene/spatial-extras/spatial-extras.iml
@@ -24,10 +24,7 @@
     <orderEntry type="library" scope="TEST" name="JUnit" level="project" />
     <orderEntry type="module" scope="TEST" module-name="lucene-test-framework" />
     <orderEntry type="module" module-name="lucene-core" />
-    <orderEntry type="module" module-name="queries" />
-    <orderEntry type="module" module-name="misc" />
     <orderEntry type="module" module-name="spatial3d" />
-    <orderEntry type="module" module-name="backward-codecs" />
     <orderEntry type="module" module-name="analysis-common" scope="TEST"/>
   </component>
-</module>
\ No newline at end of file
+</module>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 517d937..69ba53f 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -92,6 +92,11 @@ API Changes
 * LUCENE-7723: DoubleValuesSource enforces implementation of equals() and
   hashCode() (Alan Woodward)
 
+* LUCENE-7737: The spatial-extras module no longer has a dependency on the
+  queries module.  All uses of ValueSource are either replaced with core
+  DoubleValuesSource extensions, or with the new ShapeValuesSource and
+  ShapeValuesPredicate classes (Alan Woodward, David Smiley)
+
 Bug Fixes
 
 * LUCENE-7626: IndexWriter will no longer accept broken token offsets

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialFileQueryMaker.java
----------------------------------------------------------------------
diff --git a/lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialFileQueryMaker.java b/lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialFileQueryMaker.java
index b6b8f50..28bd821 100644
--- a/lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialFileQueryMaker.java
+++ b/lucene/benchmark/src/java/org/apache/lucene/benchmark/byTask/feeds/SpatialFileQueryMaker.java
@@ -21,16 +21,14 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
 
-import org.locationtech.spatial4j.shape.Shape;
 import org.apache.lucene.benchmark.byTask.utils.Config;
-import org.apache.lucene.queries.function.FunctionQuery;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.queries.function.FunctionScoreQuery;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.spatial.SpatialStrategy;
 import org.apache.lucene.spatial.query.SpatialArgs;
 import org.apache.lucene.spatial.query.SpatialOperation;
+import org.locationtech.spatial4j.shape.Shape;
 
 /**
  * Reads spatial data from the body field docs from an internally created {@link LineDocSource}.
@@ -102,11 +100,8 @@ public class SpatialFileQueryMaker extends AbstractQueryMaker {
     Query filterQuery = strategy.makeQuery(args);
     if (score) {
       //wrap with distance computing query
-      ValueSource valueSource = strategy.makeDistanceValueSource(shape.getCenter());
-      return new BooleanQuery.Builder()
-          .add(new FunctionQuery(valueSource), BooleanClause.Occur.MUST)//matches everything and provides score
-          .add(filterQuery, BooleanClause.Occur.FILTER)//filters (score isn't used)
-          .build();
+      DoubleValuesSource valueSource = strategy.makeDistanceValueSource(shape.getCenter());
+      return new FunctionScoreQuery(filterQuery, valueSource);
     } else {
       return filterQuery; // assume constant scoring
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java b/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java
index 4f12390..84167bc 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DoubleValues.java
@@ -35,4 +35,25 @@ public abstract class DoubleValues {
    */
   public abstract boolean advanceExact(int doc) throws IOException;
 
+  /**
+   * Wrap a DoubleValues instance, returning a default if the wrapped instance has no value
+   */
+  public static DoubleValues withDefault(DoubleValues in, double missingValue) {
+    return new DoubleValues() {
+
+      boolean hasValue = false;
+
+      @Override
+      public double doubleValue() throws IOException {
+        return hasValue ? in.doubleValue() : missingValue;
+      }
+
+      @Override
+      public boolean advanceExact(int doc) throws IOException {
+        hasValue = in.advanceExact(doc);
+        return true;
+      }
+    };
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
index 342c08b..84e5eb6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java
@@ -64,7 +64,12 @@ public abstract class DoubleValuesSource {
    * @return an Explanation for the value
    * @throws IOException if an {@link IOException} occurs
    */
-  public abstract Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException;
+  public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
+    DoubleValues dv = getValues(ctx, DoubleValuesSource.constant(scoreExplanation.getValue()).getValues(ctx, null));
+    if (dv.advanceExact(docId))
+      return Explanation.match((float) dv.doubleValue(), this.toString());
+    return Explanation.noMatch(this.toString());
+  }
 
   /**
    * Create a sort field based on the value of this producer

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java
index cb3faa3..5f38226 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java
@@ -258,6 +258,69 @@ public abstract class ValueSource {
 
   }
 
+  public static ValueSource fromDoubleValuesSource(DoubleValuesSource in) {
+    return new FromDoubleValuesSource(in);
+  }
+
+  private static class FromDoubleValuesSource extends ValueSource {
+
+    final DoubleValuesSource in;
+
+    private FromDoubleValuesSource(DoubleValuesSource in) {
+      this.in = in;
+    }
+
+    @Override
+    public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+      Scorer scorer = (Scorer) context.get("scorer");
+      DoubleValues scores = scorer == null ? null : DoubleValuesSource.fromScorer(scorer);
+      DoubleValues inner = in.getValues(readerContext, scores);
+      return new FunctionValues() {
+        @Override
+        public String toString(int doc) throws IOException {
+          return in.toString();
+        }
+
+        @Override
+        public float floatVal(int doc) throws IOException {
+          if (inner.advanceExact(doc) == false)
+            return 0;
+          return (float) inner.doubleValue();
+        }
+
+        @Override
+        public double doubleVal(int doc) throws IOException {
+          if (inner.advanceExact(doc) == false)
+            return 0;
+          return inner.doubleValue();
+        }
+
+        @Override
+        public boolean exists(int doc) throws IOException {
+          return inner.advanceExact(doc);
+        }
+      };
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (o == null || getClass() != o.getClass()) return false;
+      FromDoubleValuesSource that = (FromDoubleValuesSource) o;
+      return Objects.equals(in, that.in);
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(in);
+    }
+
+    @Override
+    public String description() {
+      return in.toString();
+    }
+  }
+
   //
   // Sorting by function
   //

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/build.xml
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/build.xml b/lucene/spatial-extras/build.xml
index e9cc29c..4cfa4e5 100644
--- a/lucene/spatial-extras/build.xml
+++ b/lucene/spatial-extras/build.xml
@@ -31,7 +31,6 @@
   <path id="classpath">
     <path refid="base.classpath"/>
     <path refid="spatialjar"/>
-    <pathelement path="${queries.jar}" />
     <pathelement path="${spatial3d.jar}" />
   </path>
 
@@ -41,15 +40,12 @@
     <pathelement path="src/test-files" />
   </path>
 
-  <target name="compile-core" depends="jar-backward-codecs,jar-queries,jar-misc,jar-spatial3d,common.compile-core" />
+  <target name="compile-core" depends="jar-spatial3d,common.compile-core" />
 
-  <target name="javadocs" depends="javadocs-backward-codecs,javadocs-queries,javadocs-misc,javadocs-spatial3d,compile-core,check-javadocs-uptodate"
+  <target name="javadocs" depends="javadocs-spatial3d,compile-core,check-javadocs-uptodate"
           unless="javadocs-uptodate-${name}">
     <invoke-module-javadoc>
       <links>
-        <link href="../backward-codecs"/>
-        <link href="../queries"/>
-        <link href="../misc"/>
         <link href="../spatial3d"/>
       </links>
     </invoke-module-javadoc>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValues.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValues.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValues.java
new file mode 100644
index 0000000..51323c9
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValues.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial;
+
+import java.io.IOException;
+
+import org.locationtech.spatial4j.shape.Shape;
+
+/**
+ * Iterator over {@link Shape} objects for an index segment
+ */
+public abstract class ShapeValues {
+
+  /**
+   * Advance the iterator to the given document
+   * @param doc the document to advance to
+   * @return {@code true} if there is a value for this document
+   */
+  public abstract boolean advanceExact(int doc) throws IOException;
+
+  /**
+   * Returns a {@link Shape} for the current document
+   */
+  public abstract Shape value() throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValuesSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValuesSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValuesSource.java
new file mode 100644
index 0000000..b96edd8
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/ShapeValuesSource.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.LeafReaderContext;
+
+/**
+ * Produces {@link ShapeValues} per-segment
+ */
+public abstract class ShapeValuesSource {
+
+  /**
+   * Get a {@link ShapeValues} instance for the given leaf reader context
+   */
+  public abstract ShapeValues getValues(LeafReaderContext ctx) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/SpatialStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/SpatialStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/SpatialStrategy.java
index d980ba9..215fa1d 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/SpatialStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/SpatialStrategy.java
@@ -16,15 +16,15 @@
  */
 package org.apache.lucene.spatial;
 
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.DoubleValuesSource;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.query.SpatialArgs;
+import org.apache.lucene.spatial.util.ReciprocalDoubleValuesSource;
 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.Field;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.valuesource.ReciprocalFloatFunction;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.query.SpatialArgs;
 
 /**
  * The SpatialStrategy encapsulates an approach to indexing and searching based
@@ -103,7 +103,7 @@ public abstract class SpatialStrategy {
    * See {@link #makeDistanceValueSource(org.locationtech.spatial4j.shape.Point, double)} called with
    * a multiplier of 1.0 (i.e. units of degrees).
    */
-  public ValueSource makeDistanceValueSource(Point queryPoint) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint) {
     return makeDistanceValueSource(queryPoint, 1.0);
   }
 
@@ -113,7 +113,7 @@ public abstract class SpatialStrategy {
    * then the closest one is chosen. The result is multiplied by {@code multiplier}, which
    * conveniently is used to get the desired units.
    */
-  public abstract ValueSource makeDistanceValueSource(Point queryPoint, double multiplier);
+  public abstract DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier);
 
   /**
    * Make a Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation}
@@ -133,13 +133,14 @@ public abstract class SpatialStrategy {
    * scores will be 1 for indexed points at the center of the query shape and as
    * low as ~0.1 at its furthest edges.
    */
-  public final ValueSource makeRecipDistanceValueSource(Shape queryShape) {
+  public final DoubleValuesSource makeRecipDistanceValueSource(Shape queryShape) {
     Rectangle bbox = queryShape.getBoundingBox();
     double diagonalDist = ctx.getDistCalc().distance(
         ctx.makePoint(bbox.getMinX(), bbox.getMinY()), bbox.getMaxX(), bbox.getMaxY());
     double distToEdge = diagonalDist * 0.5;
     float c = (float)distToEdge * 0.1f;//one tenth
-    return new ReciprocalFloatFunction(makeDistanceValueSource(queryShape.getCenter(), 1.0), 1f, c, c);
+    DoubleValuesSource distance = makeDistanceValueSource(queryShape.getCenter(), 1.0);
+    return new ReciprocalDoubleValuesSource(c, distance);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
index 101f373..e83279e 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxOverlapRatioValueSource.java
@@ -18,9 +18,8 @@ package org.apache.lucene.spatial.bbox;
 
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.Explanation;
-
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.locationtech.spatial4j.shape.Rectangle;
 
 /**
@@ -79,7 +78,7 @@ public class BBoxOverlapRatioValueSource extends BBoxSimilarityValueSource {
    * @param queryTargetProportion see class javadocs. Between 0 and 1.
    * @param minSideLength see class javadocs. 0.0 will effectively disable.
    */
-  public BBoxOverlapRatioValueSource(ValueSource rectValueSource, boolean isGeo, Rectangle queryExtent,
+  public BBoxOverlapRatioValueSource(ShapeValuesSource rectValueSource, boolean isGeo, Rectangle queryExtent,
                                      double queryTargetProportion, double minSideLength) {
     super(rectValueSource);
     this.isGeo = isGeo;
@@ -94,7 +93,7 @@ public class BBoxOverlapRatioValueSource extends BBoxSimilarityValueSource {
 
   /** Construct with 75% weighting towards target (roughly GeoPortal's default), geo degrees assumed, no
    * minimum side length. */
-  public BBoxOverlapRatioValueSource(ValueSource rectValueSource, Rectangle queryExtent) {
+  public BBoxOverlapRatioValueSource(ShapeValuesSource rectValueSource, Rectangle queryExtent) {
     this(rectValueSource, true, queryExtent, 0.25, 0.0);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
index b1424e0..aed9f6c 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
@@ -17,78 +17,58 @@
 package org.apache.lucene.spatial.bbox;
 
 import java.io.IOException;
-import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.IndexSearcher;
-
+import org.apache.lucene.spatial.ShapeValues;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.locationtech.spatial4j.shape.Rectangle;
 
 /**
  * A base class for calculating a spatial relevance rank per document from a provided
- * {@link ValueSource} in which {@link FunctionValues#objectVal(int)} returns a {@link
- * org.locationtech.spatial4j.shape.Rectangle}.
+ * {@link ShapeValuesSource} returning a {@link
+ * org.locationtech.spatial4j.shape.Rectangle} per-document.
  * <p>
  * Implementers: remember to implement equals and hashCode if you have
  * fields!
  *
  * @lucene.experimental
  */
-public abstract class BBoxSimilarityValueSource extends ValueSource {
+public abstract class BBoxSimilarityValueSource extends DoubleValuesSource {
 
-  private final ValueSource bboxValueSource;
+  private final ShapeValuesSource bboxValueSource;
 
-  public BBoxSimilarityValueSource(ValueSource bboxValueSource) {
+  public BBoxSimilarityValueSource(ShapeValuesSource bboxValueSource) {
     this.bboxValueSource = bboxValueSource;
   }
 
   @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    bboxValueSource.createWeight(context, searcher);
-  }
-
-  @Override
-  public String description() {
-    return getClass().getSimpleName()+"(" + bboxValueSource.description() + "," + similarityDescription() + ")";
+  public String toString() {
+    return getClass().getSimpleName()+"(" + bboxValueSource.toString() + "," + similarityDescription() + ")";
   }
 
-  /** A comma-separated list of configurable items of the subclass to put into {@link #description()}. */
+  /** A comma-separated list of configurable items of the subclass to put into {@link #toString()}. */
   protected abstract String similarityDescription();
 
   @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+  public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
 
-    final FunctionValues shapeValues = bboxValueSource.getValues(context, readerContext);
-
-    return new DoubleDocValues(this) {
+    final ShapeValues shapeValues = bboxValueSource.getValues(readerContext);
+    return DoubleValues.withDefault(new DoubleValues() {
       @Override
-      public double doubleVal(int doc) throws IOException {
-        //? limit to Rect or call getBoundingBox()? latter would encourage bad practice
-        final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
-        return rect==null ? 0 : score(rect, null);
+      public double doubleValue() throws IOException {
+        return score((Rectangle) shapeValues.value(), null);
       }
 
       @Override
-      public boolean exists(int doc) throws IOException {
-        return shapeValues.exists(doc);
+      public boolean advanceExact(int doc) throws IOException {
+        return shapeValues.advanceExact(doc);
       }
+    }, 0);
 
-      @Override
-      public Explanation explain(int doc) throws IOException {
-        final Rectangle rect = (Rectangle) shapeValues.objectVal(doc);
-        if (rect == null) {
-          return Explanation.noMatch("no rect");
-        }
-        AtomicReference<Explanation> explanation = new AtomicReference<>();
-        score(rect, explanation);
-        return explanation.get();
-      }
-    };
   }
 
   /**
@@ -115,4 +95,23 @@ public abstract class BBoxSimilarityValueSource extends ValueSource {
   public int hashCode() {
     return bboxValueSource.hashCode();
   }
+
+  @Override
+  public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
+    DoubleValues dv = getValues(ctx, DoubleValuesSource.constant(scoreExplanation.getValue()).getValues(ctx, null));
+    if (dv.advanceExact(docId)) {
+      AtomicReference<Explanation> explanation = new AtomicReference<>();
+      final ShapeValues shapeValues = bboxValueSource.getValues(ctx);
+      if (shapeValues.advanceExact(docId)) {
+        score((Rectangle) shapeValues.value(), explanation);
+        return explanation.get();
+      }
+    }
+    return Explanation.noMatch(this.toString());
+  }
+
+  @Override
+  public boolean needsScores() {
+    return false;
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/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 7536b60..5029d0e 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
@@ -25,12 +25,13 @@ 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;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.ConstantScoreQuery;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.apache.lucene.spatial.SpatialStrategy;
 import org.apache.lucene.spatial.query.SpatialArgs;
 import org.apache.lucene.spatial.query.SpatialOperation;
@@ -211,23 +212,21 @@ public class BBoxStrategy extends SpatialStrategy {
   //---------------------------------
 
   /**
-   * Provides access to each rectangle per document as a ValueSource in which
-   * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)} returns a {@link
-   * Shape}.
+   * Provides access to each rectangle per document as a {@link ShapeValuesSource}
    */ //TODO raise to SpatialStrategy
-  public ValueSource makeShapeValueSource() {
+  public ShapeValuesSource makeShapeValueSource() {
     return new BBoxValueSource(this);
   }
 
   @Override
-  public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
     //TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
     return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
   }
 
   /** Returns a similarity based on {@link BBoxOverlapRatioValueSource}. This is just a
    * convenience method. */
-  public ValueSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
+  public DoubleValuesSource makeOverlapRatioValueSource(Rectangle queryBox, double queryTargetProportion) {
     return new BBoxOverlapRatioValueSource(
         makeShapeValueSource(), ctx.isGeo(), queryBox, queryTargetProportion, 0.0);
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java
index ef9f54e..6f7fc9f 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/bbox/BBoxValueSource.java
@@ -17,24 +17,22 @@
 package org.apache.lucene.spatial.bbox;
 
 import java.io.IOException;
-import java.util.Map;
 
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.search.Explanation;
+import org.apache.lucene.spatial.ShapeValues;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
 
 /**
- * A ValueSource in which the indexed Rectangle is returned from
- * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}.
+ * A ShapeValuesSource returning a Rectangle from each document derived from four numeric fields
  *
  * @lucene.internal
  */
-class BBoxValueSource extends ValueSource {
+class BBoxValueSource extends ShapeValuesSource {
 
   private final BBoxStrategy strategy;
 
@@ -43,12 +41,12 @@ class BBoxValueSource extends ValueSource {
   }
 
   @Override
-  public String description() {
+  public String toString() {
     return "bboxShape(" + strategy.getFieldName() + ")";
   }
 
   @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+  public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
     LeafReader reader = readerContext.reader();
     final NumericDocValues minX = DocValues.getNumeric(reader, strategy.field_minX);
     final NumericDocValues minY = DocValues.getNumeric(reader, strategy.field_minY);
@@ -58,61 +56,23 @@ class BBoxValueSource extends ValueSource {
     //reused
     final Rectangle rect = strategy.getSpatialContext().makeRectangle(0,0,0,0);
 
-    return new FunctionValues() {
-      private int lastDocID = -1;
-
-      private double getDocValue(NumericDocValues values, int doc) throws IOException {
-        int curDocID = values.docID();
-        if (doc > curDocID) {
-          curDocID = values.advance(doc);
-        }
-        if (doc == curDocID) {
-          return Double.longBitsToDouble(values.longValue());
-        } else {
-          return 0.0;
-        }
-      }
+    return new ShapeValues() {
 
       @Override
-      public Object objectVal(int doc) throws IOException {
-        if (doc < lastDocID) {
-          throw new AssertionError("docs were sent out-of-order: lastDocID=" + lastDocID + " vs doc=" + doc);
-        }
-        lastDocID = doc;
-
-        double minXValue = getDocValue(minX, doc);
-        if (minX.docID() != doc) {
-          return null;
-        } else {
-          double minYValue = getDocValue(minY, doc);
-          double maxXValue = getDocValue(maxX, doc);
-          double maxYValue = getDocValue(maxY, doc);
-          rect.reset(minXValue, maxXValue, minYValue, maxYValue);
-          return rect;
-        }
+      public boolean advanceExact(int doc) throws IOException {
+        return minX.advanceExact(doc) && minY.advanceExact(doc) && maxX.advanceExact(doc) && maxY.advanceExact(doc);
       }
 
       @Override
-      public String strVal(int doc) throws IOException {//TODO support WKT output once Spatial4j does
-        Object v = objectVal(doc);
-        return v == null ? null : v.toString();
+      public Shape value() throws IOException {
+        double minXValue = Double.longBitsToDouble(minX.longValue());
+        double minYValue = Double.longBitsToDouble(minY.longValue());
+        double maxXValue = Double.longBitsToDouble(maxX.longValue());
+        double maxYValue = Double.longBitsToDouble(maxY.longValue());
+        rect.reset(minXValue, maxXValue, minYValue, maxYValue);
+        return rect;
       }
 
-      @Override
-      public boolean exists(int doc) throws IOException {
-        getDocValue(minX, doc);
-        return minX.docID() == doc;
-      }
-
-      @Override
-      public Explanation explain(int doc) throws IOException {
-        return Explanation.match(Float.NaN, toString(doc));
-      }
-
-      @Override
-      public String toString(int doc) throws IOException {
-        return description() + '=' + strVal(doc);
-      }
     };
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
index de5bb61..348b7d6 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
@@ -20,10 +20,8 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import org.locationtech.spatial4j.shape.Point;
-import org.locationtech.spatial4j.shape.Shape;
 import org.apache.lucene.document.Field;
-import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.spatial.SpatialStrategy;
 import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
@@ -32,7 +30,9 @@ import org.apache.lucene.spatial.query.SpatialArgs;
 import org.apache.lucene.spatial.query.SpatialOperation;
 import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
 import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
-import org.apache.lucene.spatial.util.ShapePredicateValueSource;
+import org.apache.lucene.spatial.util.ShapeValuesPredicate;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Shape;
 
 /**
  * A composite {@link SpatialStrategy} based on {@link RecursivePrefixTreeStrategy} (RPT) and
@@ -86,7 +86,7 @@ public class CompositeSpatialStrategy extends SpatialStrategy {
   }
 
   @Override
-  public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
     //TODO consider indexing center-point in DV?  Guarantee contained by the shape, which could then be used for
     // other purposes like faster WITHIN predicate?
     throw new UnsupportedOperationException();
@@ -108,8 +108,8 @@ public class CompositeSpatialStrategy extends SpatialStrategy {
       throw new UnsupportedSpatialOperation(pred);
     }
 
-    final ShapePredicateValueSource predicateValueSource =
-        new ShapePredicateValueSource(geometryStrategy.makeShapeValueSource(), pred, args.getShape());
+    final ShapeValuesPredicate predicateValueSource =
+        new ShapeValuesPredicate(geometryStrategy.makeShapeValueSource(), pred, args.getShape());
     //System.out.println("PredOpt: " + optimizePredicates);
     if (pred == SpatialOperation.Intersects && optimizePredicates) {
       // We have a smart Intersects impl

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java
index d556efa..36a9eff 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java
@@ -17,12 +17,9 @@
 package org.apache.lucene.spatial.composite;
 
 import java.io.IOException;
-import java.util.Map;
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.ConstantScoreScorer;
 import org.apache.lucene.search.ConstantScoreWeight;
 import org.apache.lucene.search.IndexSearcher;
@@ -30,19 +27,20 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TwoPhaseIterator;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.spatial.util.ShapeValuesPredicate;
 
 /**
  * A Query that considers an "indexQuery" to have approximate results, and a follow-on
- * {@link ValueSource}/{@link FunctionValues#boolVal(int)} is called to verify each hit
- * from {@link TwoPhaseIterator#matches()}.
+ * ShapeValuesSource is called to verify each hit from {@link TwoPhaseIterator#matches()}.
  *
  * @lucene.experimental
  */
 public class CompositeVerifyQuery extends Query {
-  final Query indexQuery;//approximation (matches more than needed)
-  final ValueSource predicateValueSource;//we call boolVal(doc)
 
-  public CompositeVerifyQuery(Query indexQuery, ValueSource predicateValueSource) {
+  private final Query indexQuery;//approximation (matches more than needed)
+  private final ShapeValuesPredicate predicateValueSource;
+
+  public CompositeVerifyQuery(Query indexQuery, ShapeValuesPredicate predicateValueSource) {
     this.indexQuery = indexQuery;
     this.predicateValueSource = predicateValueSource;
   }
@@ -84,7 +82,6 @@ public class CompositeVerifyQuery extends Query {
   @Override
   public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
     final Weight indexQueryWeight = indexQuery.createWeight(searcher, false, boost);//scores aren't unsupported
-    final Map valueSourceContext = ValueSource.newContext(searcher);
 
     return new ConstantScoreWeight(this, boost) {
 
@@ -96,21 +93,8 @@ public class CompositeVerifyQuery extends Query {
           return null;
         }
 
-        final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
-
-        final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(indexQueryScorer.iterator()) {
-          @Override
-          public boolean matches() throws IOException {
-            return predFuncValues.boolVal(indexQueryScorer.docID());
-          }
-
-          @Override
-          public float matchCost() {
-            return 100; // TODO: use cost of predFuncValues.boolVal()
-          }
-        };
-
-        return new ConstantScoreScorer(this, score(), twoPhaseIterator);
+        final TwoPhaseIterator predFuncValues = predicateValueSource.iterator(context, indexQueryScorer.iterator());
+        return new ConstantScoreScorer(this, score(), predFuncValues);
       }
     };
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java
index ce8c207..a6ea3a3 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java
@@ -17,11 +17,8 @@
 package org.apache.lucene.spatial.composite;
 
 import java.io.IOException;
-import java.util.Map;
 
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.ConstantScoreScorer;
 import org.apache.lucene.search.ConstantScoreWeight;
 import org.apache.lucene.search.DocIdSet;
@@ -34,6 +31,7 @@ import org.apache.lucene.search.Weight;
 import org.apache.lucene.spatial.prefix.AbstractVisitingPrefixTreeQuery;
 import org.apache.lucene.spatial.prefix.tree.Cell;
 import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
+import org.apache.lucene.spatial.util.ShapeValuesPredicate;
 import org.apache.lucene.util.DocIdSetBuilder;
 import org.locationtech.spatial4j.shape.Shape;
 import org.locationtech.spatial4j.shape.SpatialRelation;
@@ -41,17 +39,17 @@ import org.locationtech.spatial4j.shape.SpatialRelation;
 /**
  * A spatial Intersects predicate that distinguishes an approximated match from an exact match based on which cells
  * are within the query shape. It exposes a {@link TwoPhaseIterator} that will verify a match with a provided
- * predicate in the form of a {@link ValueSource} by calling {@link FunctionValues#boolVal(int)}.
+ * predicate in the form of an ShapeValuesPredicate.
  *
  * @lucene.internal
  */
 public class IntersectsRPTVerifyQuery extends Query {
 
   private final IntersectsDifferentiatingQuery intersectsDiffQuery;
-  private final ValueSource predicateValueSource; // we call FunctionValues.boolVal(doc)
+  private final ShapeValuesPredicate predicateValueSource;
 
   public IntersectsRPTVerifyQuery(Shape queryShape, String fieldName, SpatialPrefixTree grid, int detailLevel,
-                                  int prefixGridScanLevel, ValueSource predicateValueSource) {
+                                  int prefixGridScanLevel, ShapeValuesPredicate predicateValueSource) {
     this.predicateValueSource = predicateValueSource;
     this.intersectsDiffQuery = new IntersectsDifferentiatingQuery(queryShape, fieldName, grid, detailLevel,
         prefixGridScanLevel);
@@ -83,7 +81,6 @@ public class IntersectsRPTVerifyQuery extends Query {
 
   @Override
   public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    final Map valueSourceContext = ValueSource.newContext(searcher);
 
     return new ConstantScoreWeight(this, boost) {
       @Override
@@ -110,9 +107,10 @@ public class IntersectsRPTVerifyQuery extends Query {
           exactIterator = null;
         }
 
-        final FunctionValues predFuncValues = predicateValueSource.getValues(valueSourceContext, context);
-
         final TwoPhaseIterator twoPhaseIterator = new TwoPhaseIterator(approxDISI) {
+
+          final TwoPhaseIterator predFuncValues = predicateValueSource.iterator(context, approxDISI);
+
           @Override
           public boolean matches() throws IOException {
             final int doc = approxDISI.docID();
@@ -124,13 +122,12 @@ public class IntersectsRPTVerifyQuery extends Query {
                 return true;
               }
             }
-
-            return predFuncValues.boolVal(doc);
+            return predFuncValues.matches();
           }
 
           @Override
           public float matchCost() {
-            return 100; // TODO: use cost of exactIterator.advance() and predFuncValues.boolVal()
+            return 100; // TODO: use cost of exactIterator.advance() and predFuncValues.cost()
           }
         };
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/NumberRangePrefixTreeStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/NumberRangePrefixTreeStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/NumberRangePrefixTreeStrategy.java
index 8367644..d720215 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/NumberRangePrefixTreeStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/NumberRangePrefixTreeStrategy.java
@@ -23,7 +23,7 @@ import java.util.SortedMap;
 import java.util.TreeMap;
 
 import org.apache.lucene.index.IndexReaderContext;
-import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.spatial.prefix.tree.Cell;
 import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree;
 import org.apache.lucene.util.Bits;
@@ -76,7 +76,7 @@ public class NumberRangePrefixTreeStrategy extends RecursivePrefixTreeStrategy {
 
   /** Unsupported. */
   @Override
-  public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
     throw new UnsupportedOperationException();
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java
index 0716e78..d65e9f5 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java
@@ -25,7 +25,7 @@ import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.index.IndexOptions;
 import org.apache.lucene.index.IndexReaderContext;
-import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.spatial.SpatialStrategy;
 import org.apache.lucene.spatial.prefix.tree.Cell;
 import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
@@ -180,7 +180,7 @@ public abstract class PrefixTreeStrategy extends SpatialStrategy {
   }
 
   @Override
-  public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
     PointPrefixTreeFieldCacheProvider p = provider.get( getFieldName() );
     if( p == null ) {
       synchronized (this) {//double checked locking idiom is okay since provider is threadsafe

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java
index 47ac90e..3a33380 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java
@@ -22,29 +22,27 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FilterOutputStream;
 import java.io.IOException;
-import java.util.Map;
 
 import org.apache.lucene.document.BinaryDocValuesField;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.ConstantScoreScorer;
 import org.apache.lucene.search.ConstantScoreWeight;
 import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TwoPhaseIterator;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.spatial.ShapeValues;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.apache.lucene.spatial.SpatialStrategy;
 import org.apache.lucene.spatial.query.SpatialArgs;
 import org.apache.lucene.spatial.util.DistanceToShapeValueSource;
-import org.apache.lucene.spatial.util.ShapePredicateValueSource;
+import org.apache.lucene.spatial.util.ShapeValuesPredicate;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.BytesRefBuilder;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.io.BinaryCodec;
 import org.locationtech.spatial4j.shape.Point;
@@ -100,7 +98,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
   }
 
   @Override
-  public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+  public DoubleValuesSource makeDistanceValueSource(Point queryPoint, double multiplier) {
     //TODO if makeShapeValueSource gets lifted to the top; this could become a generic impl.
     return new DistanceToShapeValueSource(makeShapeValueSource(), queryPoint, multiplier, ctx);
   }
@@ -111,18 +109,15 @@ public class SerializedDVStrategy extends SpatialStrategy {
    */
   @Override
   public Query makeQuery(SpatialArgs args) {
-    ValueSource shapeValueSource = makeShapeValueSource();
-    ShapePredicateValueSource predicateValueSource = new ShapePredicateValueSource(
-        shapeValueSource, args.getOperation(), args.getShape());
+    ShapeValuesSource shapeValueSource = makeShapeValueSource();
+    ShapeValuesPredicate predicateValueSource = new ShapeValuesPredicate(shapeValueSource, args.getOperation(), args.getShape());
     return new PredicateValueSourceQuery(predicateValueSource);
   }
 
   /**
-   * Provides access to each shape per document as a ValueSource in which
-   * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)} returns a {@link
-   * Shape}.
+   * Provides access to each shape per document
    */ //TODO raise to SpatialStrategy
-  public ValueSource makeShapeValueSource() {
+  public ShapeValuesSource makeShapeValueSource() {
     return new ShapeDocValueSource(getFieldName(), ctx.getBinaryCodec());
   }
 
@@ -130,9 +125,9 @@ public class SerializedDVStrategy extends SpatialStrategy {
    * by {@link TwoPhaseIterator}.
    */
   static class PredicateValueSourceQuery extends Query {
-    private final ValueSource predicateValueSource;//we call boolVal(doc)
+    private final ShapeValuesPredicate predicateValueSource;
 
-    public PredicateValueSourceQuery(ValueSource predicateValueSource) {
+    public PredicateValueSourceQuery(ShapeValuesPredicate predicateValueSource) {
       this.predicateValueSource = predicateValueSource;
     }
 
@@ -142,21 +137,8 @@ public class SerializedDVStrategy extends SpatialStrategy {
         @Override
         public Scorer scorer(LeafReaderContext context) throws IOException {
           DocIdSetIterator approximation = DocIdSetIterator.all(context.reader().maxDoc());
-          final FunctionValues predFuncValues = predicateValueSource.getValues(null, context);
-          return new ConstantScoreScorer(this, score(), new TwoPhaseIterator(approximation) {
-
-            @Override
-            public boolean matches() throws IOException {
-              final int docID = approximation.docID();
-              return predFuncValues.boolVal(docID);
-            }
-
-            @Override
-            public float matchCost() {
-              // TODO: what is the cost of the predicateValueSource
-              return 100f;
-            }
-          });
+          TwoPhaseIterator it = predicateValueSource.iterator(context, approximation);
+          return new ConstantScoreScorer(this, score(), it);
         }
       };
     }
@@ -184,7 +166,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
    * Implements a ValueSource by deserializing a Shape in from BinaryDocValues using BinaryCodec.
    * @see #makeShapeValueSource()
    */
-  static class ShapeDocValueSource extends ValueSource {
+  static class ShapeDocValueSource extends ShapeValuesSource {
 
     private final String fieldName;
     private final BinaryCodec binaryCodec;//spatial4j
@@ -195,65 +177,21 @@ public class SerializedDVStrategy extends SpatialStrategy {
     }
 
     @Override
-    public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+    public ShapeValues getValues(LeafReaderContext readerContext) throws IOException {
       final BinaryDocValues docValues = readerContext.reader().getBinaryDocValues(fieldName);
 
-      return new FunctionValues() {
-        int bytesRefDoc = -1;
-        BytesRefBuilder bytesRef = new BytesRefBuilder();
-
-        boolean fillBytes(int doc) throws IOException {
-          if (bytesRefDoc != doc) {
-            if (docValues.docID() < doc) {
-              docValues.advance(doc);
-            }
-            if (docValues.docID() == doc) {
-              bytesRef.copyBytes(docValues.binaryValue());
-            } else {
-              bytesRef.clear();
-            }
-            bytesRefDoc = doc;
-          }
-          return bytesRef.length() != 0;
-        }
-
-        @Override
-        public boolean exists(int doc) throws IOException {
-          return fillBytes(doc);
-        }
-
-        @Override
-        public boolean bytesVal(int doc, BytesRefBuilder target) throws IOException {
-          target.clear();
-          if (fillBytes(doc)) {
-            target.copyBytes(bytesRef);
-            return true;
-          } else {
-            return false;
-          }
-        }
-
-        @Override
-        public Object objectVal(int docId) throws IOException {
-          if (!fillBytes(docId))
-            return null;
-          DataInputStream dataInput = new DataInputStream(
-              new ByteArrayInputStream(bytesRef.bytes(), 0, bytesRef.length()));
-          try {
-            return binaryCodec.readShape(dataInput);
-          } catch (IOException e) {
-            throw new RuntimeException(e);
-          }
-        }
-
+      return new ShapeValues() {
         @Override
-        public Explanation explain(int doc) throws IOException {
-          return Explanation.match(Float.NaN, toString(doc));
+        public boolean advanceExact(int doc) throws IOException {
+          return docValues.advanceExact(doc);
         }
 
         @Override
-        public String toString(int doc) throws IOException {
-          return description() + "=" + objectVal(doc);//TODO truncate?
+        public Shape value() throws IOException {
+          BytesRef bytesRef = docValues.binaryValue();
+          DataInputStream dataInput
+              = new DataInputStream(new ByteArrayInputStream(bytesRef.bytes, bytesRef.offset, bytesRef.length));
+          return binaryCodec.readShape(dataInput);
         }
 
       };
@@ -278,7 +216,7 @@ public class SerializedDVStrategy extends SpatialStrategy {
     }
 
     @Override
-    public String description() {
+    public String toString() {
       return "shapeDocVal(" + fieldName + ")";
     }
   }//ShapeDocValueSource

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/CachingDoubleValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/CachingDoubleValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/CachingDoubleValueSource.java
index d43d4e8..8d2f6f9 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/CachingDoubleValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/CachingDoubleValueSource.java
@@ -16,65 +16,74 @@
  */
 package org.apache.lucene.spatial.util;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
+import org.apache.lucene.search.Explanation;
+
 /**
  * Caches the doubleVal of another value source in a HashMap
  * so that it is computed only once.
  * @lucene.internal
  */
-public class CachingDoubleValueSource extends ValueSource {
+public class CachingDoubleValueSource extends DoubleValuesSource {
 
-  final ValueSource source;
+  final DoubleValuesSource source;
   final Map<Integer, Double> cache;
 
-  public CachingDoubleValueSource( ValueSource source )
-  {
+  public CachingDoubleValueSource(DoubleValuesSource source) {
     this.source = source;
     cache = new HashMap<>();
   }
 
   @Override
-  public String description() {
-    return "Cached["+source.description()+"]";
+  public String toString() {
+    return "Cached["+source.toString()+"]";
   }
 
   @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+  public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
     final int base = readerContext.docBase;
-    final FunctionValues vals = source.getValues(context,readerContext);
-    return new FunctionValues() {
+    final DoubleValues vals = source.getValues(readerContext, scores);
+    return new DoubleValues() {
 
       @Override
-      public double doubleVal(int doc) throws IOException {
-        Integer key = Integer.valueOf( base+doc );
-        Double v = cache.get( key );
-        if( v == null ) {
-          v = Double.valueOf( vals.doubleVal(doc) );
-          cache.put( key, v );
+      public double doubleValue() throws IOException {
+        int key = base + doc;
+        Double v = cache.get(key);
+        if (v == null) {
+          v = vals.doubleValue();
+          cache.put(key, v);
         }
-        return v.doubleValue();
+        return v;
       }
 
       @Override
-      public float floatVal(int doc) throws IOException {
-        return (float)doubleVal(doc);
+      public boolean advanceExact(int doc) throws IOException {
+        this.doc = doc;
+        return vals.advanceExact(doc);
       }
 
-      @Override
-      public String toString(int doc) throws IOException {
-        return doubleVal(doc)+"";
-      }
+      int doc = -1;
+
     };
   }
 
   @Override
+  public boolean needsScores() {
+    return false;
+  }
+
+  @Override
+  public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
+    return source.explain(ctx, docId, scoreExplanation);
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java
index 6993adb..a926d7e 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/DistanceToShapeValueSource.java
@@ -17,81 +17,67 @@
 package org.apache.lucene.spatial.util;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
 
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.IndexSearcher;
-
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
+import org.apache.lucene.spatial.ShapeValues;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.distance.DistanceCalculator;
 import org.locationtech.spatial4j.shape.Point;
-import org.locationtech.spatial4j.shape.Shape;
 
 /**
- * The distance from a provided Point to a Point retrieved from a ValueSource via
- * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}. The distance
+ * The distance from a provided Point to a Point retrieved from an ShapeValuesSource. The distance
  * is calculated via a {@link org.locationtech.spatial4j.distance.DistanceCalculator}.
  *
  * @lucene.experimental
  */
-public class DistanceToShapeValueSource extends ValueSource {
-  private final ValueSource shapeValueSource;
+public class DistanceToShapeValueSource extends DoubleValuesSource {
+
+  private final ShapeValuesSource shapeValueSource;
   private final Point queryPoint;
   private final double multiplier;
   private final DistanceCalculator distCalc;
 
-  //TODO if FunctionValues returns NaN; will things be ok?
-  private final double nullValue;//computed
+  //TODO if DoubleValues returns NaN; will things be ok?
+  private final double nullValue;
 
-  public DistanceToShapeValueSource(ValueSource shapeValueSource, Point queryPoint,
+  public DistanceToShapeValueSource(ShapeValuesSource shapeValueSource, Point queryPoint,
                                     double multiplier, SpatialContext ctx) {
     this.shapeValueSource = shapeValueSource;
     this.queryPoint = queryPoint;
     this.multiplier = multiplier;
     this.distCalc = ctx.getDistCalc();
-    this.nullValue =
-        (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
+    this.nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
   }
 
   @Override
-  public String description() {
-    return "distance(" + queryPoint + " to " + shapeValueSource.description() + ")*" + multiplier + ")";
+  public String toString() {
+    return "distance(" + queryPoint + " to " + shapeValueSource.toString() + ")*" + multiplier + ")";
   }
 
   @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    shapeValueSource.createWeight(context, searcher);
-  }
+  public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
 
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues shapeValues = shapeValueSource.getValues(context, readerContext);
+    final ShapeValues shapeValues = shapeValueSource.getValues(readerContext);
 
-    return new DoubleDocValues(this) {
+    return DoubleValues.withDefault(new DoubleValues() {
       @Override
-      public double doubleVal(int doc) throws IOException {
-        Shape shape = (Shape) shapeValues.objectVal(doc);
-        if (shape == null || shape.isEmpty())
-          return nullValue;
-        Point pt = shape.getCenter();
-        return distCalc.distance(queryPoint, pt) * multiplier;
+      public double doubleValue() throws IOException {
+        return distCalc.distance(queryPoint, shapeValues.value().getCenter()) * multiplier;
       }
 
       @Override
-      public Explanation explain(int doc) throws IOException {
-        Explanation exp = super.explain(doc);
-        List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
-        details.add(shapeValues.explain(doc));
-        return Explanation.match(exp.getValue(), exp.getDescription(), details);
+      public boolean advanceExact(int doc) throws IOException {
+        return shapeValues.advanceExact(doc);
       }
-    };
+    }, nullValue);
+  }
+
+  @Override
+  public boolean needsScores() {
+    return false;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ReciprocalDoubleValuesSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ReciprocalDoubleValuesSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ReciprocalDoubleValuesSource.java
new file mode 100644
index 0000000..f0e2c45
--- /dev/null
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ReciprocalDoubleValuesSource.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.util;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
+import org.apache.lucene.search.Explanation;
+
+/**
+ * Transforms a DoubleValuesSource using the formula v = k / (v + k)
+ */
+public class ReciprocalDoubleValuesSource extends DoubleValuesSource {
+
+  private final double distToEdge;
+  private final DoubleValuesSource input;
+
+  /**
+   * Creates a ReciprocalDoubleValuesSource
+   * @param distToEdge  the value k in v = k / (v + k)
+   * @param input       the input DoubleValuesSource to transform
+   */
+  public ReciprocalDoubleValuesSource(double distToEdge, DoubleValuesSource input) {
+    this.distToEdge = distToEdge;
+    this.input = input;
+  }
+
+  @Override
+  public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
+    DoubleValues in = input.getValues(ctx, scores);
+    return new DoubleValues() {
+      @Override
+      public double doubleValue() throws IOException {
+        return recip(in.doubleValue());
+      }
+
+      @Override
+      public boolean advanceExact(int doc) throws IOException {
+        return in.advanceExact(doc);
+      }
+    };
+  }
+
+  private double recip(double in) {
+    return distToEdge / (in + distToEdge);
+  }
+
+  @Override
+  public boolean needsScores() {
+    return input.needsScores();
+  }
+
+  @Override
+  public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
+    Explanation expl = input.explain(ctx, docId, scoreExplanation);
+    return Explanation.match((float)recip(expl.getValue()),
+        distToEdge + " / (v + " + distToEdge + "), computed from:", expl);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    ReciprocalDoubleValuesSource that = (ReciprocalDoubleValuesSource) o;
+    return Double.compare(that.distToEdge, distToEdge) == 0 &&
+        Objects.equals(input, that.input);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(distToEdge, input);
+  }
+
+  @Override
+  public String toString() {
+    return "recip(" + distToEdge + ", " + input.toString() + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java
index 9c2c0e3..6767991 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java
@@ -17,36 +17,29 @@
 package org.apache.lucene.spatial.util;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
 
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
+import org.apache.lucene.spatial.ShapeValues;
+import org.apache.lucene.spatial.ShapeValuesSource;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.shape.Shape;
 
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.IndexSearcher;
-
 /**
- * The area of a Shape retrieved from a ValueSource via
- * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}.
+ * The area of a Shape retrieved from an ShapeValuesSource
  *
  * @see Shape#getArea(org.locationtech.spatial4j.context.SpatialContext)
  *
  * @lucene.experimental
  */
-public class ShapeAreaValueSource extends ValueSource {
-  private final ValueSource shapeValueSource;
+public class ShapeAreaValueSource extends DoubleValuesSource {
+  private final ShapeValuesSource shapeValueSource;
   private final SpatialContext ctx;//not part of identity; should be associated with shapeValueSource indirectly
   private final boolean geoArea;
   private double multiplier;
 
-  public ShapeAreaValueSource(ValueSource shapeValueSource, SpatialContext ctx, boolean geoArea, double multiplier) {
+  public ShapeAreaValueSource(ShapeValuesSource shapeValueSource, SpatialContext ctx, boolean geoArea, double multiplier) {
     this.shapeValueSource = shapeValueSource;
     this.ctx = ctx;
     this.geoArea = geoArea;
@@ -54,43 +47,29 @@ public class ShapeAreaValueSource extends ValueSource {
   }
 
   @Override
-  public String description() {
-    return "area(" + shapeValueSource.description() + ",geo=" + geoArea + ")";
-  }
-
-  @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    shapeValueSource.createWeight(context, searcher);
+  public String toString() {
+    return "area(" + shapeValueSource.toString() + ",geo=" + geoArea + ")";
   }
 
   @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues shapeValues = shapeValueSource.getValues(context, readerContext);
-
-    return new DoubleDocValues(this) {
+  public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
+    final ShapeValues shapeValues = shapeValueSource.getValues(readerContext);
+    return DoubleValues.withDefault(new DoubleValues() {
       @Override
-      public double doubleVal(int doc) throws IOException {
-        Shape shape = (Shape) shapeValues.objectVal(doc);
-        if (shape == null || shape.isEmpty())
-          return 0;//or NaN?
-        //This part of Spatial4j API is kinda weird. Passing null means 2D area, otherwise geo
-        //   assuming ctx.isGeo()
-        return shape.getArea( geoArea ? ctx : null ) * multiplier;
+      public double doubleValue() throws IOException {
+        return shapeValues.value().getArea(geoArea ? ctx : null) * multiplier;
       }
 
       @Override
-      public boolean exists(int doc) throws IOException {
-        return shapeValues.exists(doc);
+      public boolean advanceExact(int doc) throws IOException {
+        return shapeValues.advanceExact(doc);
       }
+    }, 0);
+  }
 
-      @Override
-      public Explanation explain(int doc) throws IOException {
-        Explanation exp = super.explain(doc);
-        List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
-        details.add(shapeValues.explain(doc));
-        return Explanation.match(exp.getValue(), exp.getDescription(), details);
-      }
-    };
+  @Override
+  public boolean needsScores() {
+    return false;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java
index 1ac84e8..2d7ff4a 100644
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java
+++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java
@@ -16,26 +16,25 @@
  */
 package org.apache.lucene.spatial.util;
 
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DoubleValues;
+import org.apache.lucene.search.DoubleValuesSource;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.distance.DistanceCalculator;
 import org.locationtech.spatial4j.shape.Point;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
 
 /**
- * An implementation of the Lucene ValueSource that returns the spatial distance
+ * A DoubleValuesSource that returns the spatial distance
  * between an input point and a document's points in
  * {@link ShapeFieldCacheProvider}. The shortest distance is returned if a
  * document has more than one point.
  *
  * @lucene.internal
  */
-public class ShapeFieldCacheDistanceValueSource extends ValueSource {
+public class ShapeFieldCacheDistanceValueSource extends DoubleValuesSource {
 
   private final SpatialContext ctx;
   private final Point from;
@@ -51,43 +50,43 @@ public class ShapeFieldCacheDistanceValueSource extends ValueSource {
   }
 
   @Override
-  public String description() {
+  public String toString() {
     return getClass().getSimpleName()+"("+provider+", "+from+")";
   }
 
   @Override
-  public FunctionValues getValues(Map context, final LeafReaderContext readerContext) throws IOException {
-    return new FunctionValues() {
+  public DoubleValues getValues(LeafReaderContext readerContext, DoubleValues scores) throws IOException {
+
+    final double nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
+
+    return DoubleValues.withDefault(new DoubleValues() {
       private final ShapeFieldCache<Point> cache =
           provider.getCache(readerContext.reader());
       private final Point from = ShapeFieldCacheDistanceValueSource.this.from;
       private final DistanceCalculator calculator = ctx.getDistCalc();
-      private final double nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE);
 
-      @Override
-      public float floatVal(int doc) {
-        return (float) doubleVal(doc);
-      }
+      private List<Point> currentVals;
 
       @Override
-      public double doubleVal(int doc) {
-
-        List<Point> vals = cache.getShapes( doc );
-        if( vals != null ) {
-          double v = calculator.distance(from, vals.get(0));
-          for( int i=1; i<vals.size(); i++ ) {
-            v = Math.min(v, calculator.distance(from, vals.get(i)));
-          }
-          return v * multiplier;
+      public double doubleValue() throws IOException {
+        double v = calculator.distance(from, currentVals.get(0));
+        for (int i = 1; i < currentVals.size(); i++) {
+          v = Math.min(v, calculator.distance(from, currentVals.get(i)));
         }
-        return nullValue;
+        return v * multiplier;
       }
 
       @Override
-      public String toString(int doc) {
-        return description() + "=" + floatVal(doc);
+      public boolean advanceExact(int doc) throws IOException {
+        currentVals = cache.getShapes(doc);
+        return currentVals != null;
       }
-    };
+    }, nullValue);
+  }
+
+  @Override
+  public boolean needsScores() {
+    return false;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f2e00ff/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapePredicateValueSource.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapePredicateValueSource.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapePredicateValueSource.java
deleted file mode 100644
index d0dc51f..0000000
--- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/util/ShapePredicateValueSource.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.spatial.util;
-
-import org.locationtech.spatial4j.shape.Shape;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.BoolDocValues;
-import org.apache.lucene.search.Explanation;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.spatial.query.SpatialOperation;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A boolean ValueSource that compares a shape from a provided ValueSource with a given Shape and sees
- * if it matches a given {@link SpatialOperation} (the predicate).
- *
- * @lucene.experimental
- */
-public class ShapePredicateValueSource extends ValueSource {
-  private final ValueSource shapeValuesource;//the left hand side
-  private final SpatialOperation op;
-  private final Shape queryShape;//the right hand side (constant)
-
-  /**
-   *
-   * @param shapeValuesource Must yield {@link Shape} instances from its objectVal(doc). If null
-   *                         then the result is false. This is the left-hand (indexed) side.
-   * @param op the predicate
-   * @param queryShape The shape on the right-hand (query) side.
-   */
-  public ShapePredicateValueSource(ValueSource shapeValuesource, SpatialOperation op, Shape queryShape) {
-    this.shapeValuesource = shapeValuesource;
-    this.op = op;
-    this.queryShape = queryShape;
-  }
-
-  @Override
-  public String description() {
-    return shapeValuesource + " " + op + " " + queryShape;
-  }
-
-  @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    shapeValuesource.createWeight(context, searcher);
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues shapeValues = shapeValuesource.getValues(context, readerContext);
-
-    return new BoolDocValues(this) {
-      @Override
-      public boolean boolVal(int doc) throws IOException {
-        Shape indexedShape = (Shape) shapeValues.objectVal(doc);
-        if (indexedShape == null)
-          return false;
-        return op.evaluate(indexedShape, queryShape);
-      }
-
-      @Override
-      public Explanation explain(int doc) throws IOException {
-        Explanation exp = super.explain(doc);
-        List<Explanation> details = new ArrayList<>(Arrays.asList(exp.getDetails()));
-        details.add(shapeValues.explain(doc));
-        return Explanation.match(exp.getValue(), exp.getDescription(), details);
-      }
-    };
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-
-    ShapePredicateValueSource that = (ShapePredicateValueSource) o;
-
-    if (!shapeValuesource.equals(that.shapeValuesource)) return false;
-    if (!op.equals(that.op)) return false;
-    if (!queryShape.equals(that.queryShape)) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = shapeValuesource.hashCode();
-    result = 31 * result + op.hashCode();
-    result = 31 * result + queryShape.hashCode();
-    return result;
-  }
-}