Selaa lähdekoodia

Split off the values supplier for ScriptDocValues (#80635)

This change makes all ScriptDocValues purely a wrapper around a supplier. (Similar to what 
FieldValues was.) However, there are some important differences:

* This is meant to be transitory. As more DocValuesFields are completed, more of the simple 
suppliers (ones that aren't DocValuesFields) can be removed.
* ScriptDocValues is the wrapper rather than the supplier. DocValuesFields are eventually the target 
suppliers which makes it really easy to remove the simple suppliers once they are no longer 
necessary.
* ScriptDocValues can be easily deprecated and removed without having to move their code to 
DocValuesFields. Once ScriptDocValues is removed we can remove the supplier code from 
DocValuesFields.
* DelegateDocValuesField ensures that any ScriptDocValues field are not supplied by another 
DocValuesField with an assert statement. This helps us to identify bugs during testing.
* ScriptDocValues no longer have setNextDocId. This helps us identify bugs during compilation.
* Conversions will not share/wrap suppliers since the suppliers are transitory.
Jack Conradson 3 vuotta sitten
vanhempi
commit
1adb59c041
44 muutettua tiedostoa jossa 666 lisäystä ja 357 poistoa
  1. 2 1
      modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java
  2. 5 1
      plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java
  3. 9 6
      server/src/main/java/org/elasticsearch/index/fielddata/IpScriptFieldData.java
  4. 195 94
      server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java
  5. 2 1
      server/src/main/java/org/elasticsearch/index/fielddata/StringScriptFieldData.java
  6. 2 1
      server/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractLeafGeoPointFieldData.java
  7. 1 1
      server/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractLeafOrdinalsFieldData.java
  8. 1 1
      server/src/main/java/org/elasticsearch/index/fielddata/plain/BinaryDVLeafFieldData.java
  9. 1 1
      server/src/main/java/org/elasticsearch/index/fielddata/plain/StringBinaryDVLeafFieldData.java
  10. 7 2
      server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java
  11. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java
  12. 1 1
      server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java
  13. 47 25
      server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java
  14. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java
  15. 9 7
      server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java
  16. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java
  17. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java
  18. 1 1
      server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java
  19. 3 2
      server/src/main/java/org/elasticsearch/script/field/BinaryDocValuesField.java
  20. 22 42
      server/src/main/java/org/elasticsearch/script/field/BooleanDocValuesField.java
  21. 4 1
      server/src/main/java/org/elasticsearch/script/field/DelegateDocValuesField.java
  22. 10 9
      server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesGeoPointsTests.java
  23. 4 3
      server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java
  24. 3 2
      server/src/test/java/org/elasticsearch/index/fielddata/plain/HalfFloatFielddataTests.java
  25. 2 1
      server/src/test/java/org/elasticsearch/index/mapper/DateFieldTypeTests.java
  26. 2 2
      server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java
  27. 13 3
      server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java
  28. 2 1
      server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedSamplerTests.java
  29. 9 3
      server/src/test/java/org/elasticsearch/search/lookup/LeafDocLookupTests.java
  30. 5 1
      x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java
  31. 26 9
      x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongDocValuesField.java
  32. 5 18
      x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java
  33. 3 4
      x-pack/plugin/mapper-unsigned-long/src/main/resources/org/elasticsearch/xpack/unsignedlong/org.elasticsearch.xpack.unsignedlong.txt
  34. 37 20
      x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionScriptDocValues.java
  35. 6 1
      x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java
  36. 45 10
      x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java
  37. 48 22
      x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/BinaryDenseVectorScriptDocValues.java
  38. 16 9
      x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/DenseVectorScriptDocValues.java
  39. 49 24
      x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/KnnDenseVectorScriptDocValues.java
  40. 1 1
      x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/ScoreScriptUtils.java
  41. 26 3
      x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/VectorDVLeafFieldData.java
  42. 15 10
      x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/BinaryDenseVectorScriptDocValuesTests.java
  43. 6 1
      x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/DenseVectorFunctionTests.java
  44. 13 8
      x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/KnnDenseVectorScriptDocValuesTests.java

+ 2 - 1
modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java

@@ -26,6 +26,7 @@ import org.elasticsearch.index.fielddata.IndexNumericFieldData;
 import org.elasticsearch.index.fielddata.LeafNumericFieldData;
 import org.elasticsearch.index.fielddata.LeafNumericFieldData;
 import org.elasticsearch.index.fielddata.NumericDoubleValues;
 import org.elasticsearch.index.fielddata.NumericDoubleValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
@@ -269,7 +270,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
                 return new ScaledFloatIndexFieldData(
                 return new ScaledFloatIndexFieldData(
                     scaledValues,
                     scaledValues,
                     scalingFactor,
                     scalingFactor,
-                    (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
                 );
                 );
             };
             };
         }
         }

+ 5 - 1
plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java

@@ -19,6 +19,7 @@ import org.elasticsearch.common.hash.MurmurHash3;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.mapper.DocumentParserContext;
 import org.elasticsearch.index.mapper.DocumentParserContext;
 import org.elasticsearch.index.mapper.FieldMapper;
 import org.elasticsearch.index.mapper.FieldMapper;
@@ -83,7 +84,10 @@ public class Murmur3FieldMapper extends FieldMapper {
     // this only exists so a check can be done to match the field type to using murmur3 hashing...
     // this only exists so a check can be done to match the field type to using murmur3 hashing...
     public static class Murmur3FieldType extends MappedFieldType {
     public static class Murmur3FieldType extends MappedFieldType {
 
 
-        public static final ToScriptField<SortedNumericDocValues> TO_SCRIPT_FIELD = (dv, n) -> new DelegateDocValuesField(new Longs(dv), n);
+        public static final ToScriptField<SortedNumericDocValues> TO_SCRIPT_FIELD = (dv, n) -> new DelegateDocValuesField(
+            new Longs(new LongsSupplier(dv)),
+            n
+        );
 
 
         private Murmur3FieldType(String name, boolean isStored, Map<String, String> meta) {
         private Murmur3FieldType(String name, boolean isStored, Map<String, String> meta) {
             super(name, false, isStored, true, TextSearchInfo.NONE, meta);
             super(name, false, isStored, true, TextSearchInfo.NONE, meta);

+ 9 - 6
server/src/main/java/org/elasticsearch/index/fielddata/IpScriptFieldData.java

@@ -14,6 +14,8 @@ import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.bytes.BytesReference;
 import org.elasticsearch.common.network.InetAddresses;
 import org.elasticsearch.common.network.InetAddresses;
+import org.elasticsearch.index.fielddata.ScriptDocValues.Strings;
+import org.elasticsearch.index.fielddata.ScriptDocValues.StringsSupplier;
 import org.elasticsearch.index.mapper.IpFieldMapper;
 import org.elasticsearch.index.mapper.IpFieldMapper;
 import org.elasticsearch.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.script.IpFieldScript;
 import org.elasticsearch.script.IpFieldScript;
@@ -53,7 +55,7 @@ public class IpScriptFieldData extends BinaryScriptFieldData {
         return new BinaryScriptLeafFieldData() {
         return new BinaryScriptLeafFieldData() {
             @Override
             @Override
             public DocValuesField<?> getScriptField(String name) {
             public DocValuesField<?> getScriptField(String name) {
-                return new DelegateDocValuesField(new IpScriptDocValues(getBytesValues()), name);
+                return new DelegateDocValuesField(new Strings(new IpSupplier(getBytesValues())), name);
             }
             }
 
 
             @Override
             @Override
@@ -69,18 +71,19 @@ public class IpScriptFieldData extends BinaryScriptFieldData {
     }
     }
 
 
     /**
     /**
-     * Doc values implementation for ips. We can't share
+     * Doc values supplier implementation for ips. We can't share
      * {@link IpFieldMapper.IpFieldType.IpScriptDocValues} because it is based
      * {@link IpFieldMapper.IpFieldType.IpScriptDocValues} because it is based
      * on global ordinals and we don't have those.
      * on global ordinals and we don't have those.
      */
      */
-    public static class IpScriptDocValues extends ScriptDocValues.Strings {
-        public IpScriptDocValues(SortedBinaryDocValues in) {
+    public static class IpSupplier extends StringsSupplier {
+
+        public IpSupplier(SortedBinaryDocValues in) {
             super(in);
             super(in);
         }
         }
 
 
         @Override
         @Override
-        protected String bytesToString(BytesRef bytes) {
-            InetAddress addr = InetAddressPoint.decode(BytesReference.toBytes(new BytesArray(bytes)));
+        protected String bytesToString(BytesRef bytesRef) {
+            InetAddress addr = InetAddressPoint.decode(BytesReference.toBytes(new BytesArray(bytesRef)));
             return InetAddresses.toAddrString(addr);
             return InetAddresses.toAddrString(addr);
         }
         }
     }
     }

+ 195 - 94
server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java

@@ -17,8 +17,7 @@ import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.common.time.DateUtils;
 import org.elasticsearch.common.time.DateUtils;
 import org.elasticsearch.geometry.utils.Geohash;
 import org.elasticsearch.geometry.utils.Geohash;
-import org.elasticsearch.script.field.BinaryDocValuesField;
-import org.elasticsearch.script.field.BooleanDocValuesField;
+import org.elasticsearch.script.field.DocValuesField;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.time.Instant;
 import java.time.Instant;
@@ -39,9 +38,30 @@ import java.util.function.UnaryOperator;
 public abstract class ScriptDocValues<T> extends AbstractList<T> {
 public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
 
     /**
     /**
-     * Set the current doc ID.
+     * Supplies values to different ScriptDocValues as we
+     * convert them to wrappers around {@link DocValuesField}.
+     * This allows for different {@link DocValuesField} to implement
+     * this supplier class in many-to-one relationship since
+     * {@link DocValuesField} are more specific where
+     * ({byte, short, int, long, _version, murmur3, etc.} -> {long})
      */
      */
-    public abstract void setNextDocId(int docId) throws IOException;
+    public interface Supplier<T> {
+        void setNextDocId(int docId) throws IOException;
+
+        T getInternal(int index);
+
+        int size();
+    }
+
+    protected final Supplier<T> supplier;
+
+    public ScriptDocValues(Supplier<T> supplier) {
+        this.supplier = supplier;
+    }
+
+    public Supplier<T> getSupplier() {
+        return supplier;
+    }
 
 
     // Throw meaningful exceptions if someone tries to modify the ScriptDocValues.
     // Throw meaningful exceptions if someone tries to modify the ScriptDocValues.
     @Override
     @Override
@@ -77,15 +97,13 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         }
         }
     }
     }
 
 
-    public static final class Longs extends ScriptDocValues<Long> {
+    public static class LongsSupplier implements Supplier<Long> {
+
         private final SortedNumericDocValues in;
         private final SortedNumericDocValues in;
         private long[] values = new long[0];
         private long[] values = new long[0];
         private int count;
         private int count;
 
 
-        /**
-         * Standard constructor.
-         */
-        public Longs(SortedNumericDocValues in) {
+        public LongsSupplier(SortedNumericDocValues in) {
             this.in = in;
             this.in = in;
         }
         }
 
 
@@ -105,11 +123,28 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * store at least that many entries.
          * store at least that many entries.
          */
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             count = newSize;
             values = ArrayUtil.grow(values, count);
             values = ArrayUtil.grow(values, count);
         }
         }
 
 
+        @Override
+        public Long getInternal(int index) {
+            return values[index];
+        }
+
+        @Override
+        public int size() {
+            return count;
+        }
+    }
+
+    public static class Longs extends ScriptDocValues<Long> {
+
+        public Longs(Supplier<Long> supplier) {
+            super(supplier);
+        }
+
         public long getValue() {
         public long getValue() {
             return get(0);
             return get(0);
         }
         }
@@ -117,16 +152,16 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         @Override
         public Long get(int index) {
         public Long get(int index) {
             throwIfEmpty();
             throwIfEmpty();
-            return values[index];
+            return supplier.getInternal(index);
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return count;
+            return supplier.size();
         }
         }
     }
     }
 
 
-    public static final class Dates extends ScriptDocValues<ZonedDateTime> {
+    public static class DatesSupplier implements Supplier<ZonedDateTime> {
 
 
         private final SortedNumericDocValues in;
         private final SortedNumericDocValues in;
         private final boolean isNanos;
         private final boolean isNanos;
@@ -137,32 +172,13 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         private ZonedDateTime[] dates;
         private ZonedDateTime[] dates;
         private int count;
         private int count;
 
 
-        public Dates(SortedNumericDocValues in, boolean isNanos) {
+        public DatesSupplier(SortedNumericDocValues in, boolean isNanos) {
             this.in = in;
             this.in = in;
             this.isNanos = isNanos;
             this.isNanos = isNanos;
         }
         }
 
 
-        /**
-         * Fetch the first field value or 0 millis after epoch if there are no
-         * in.
-         */
-        public ZonedDateTime getValue() {
-            return get(0);
-        }
-
         @Override
         @Override
-        public ZonedDateTime get(int index) {
-            if (count == 0) {
-                throw new IllegalStateException(
-                    "A document doesn't have a value for a field! "
-                        + "Use doc[<field>].size()==0 to check if a document is missing a field!"
-                );
-            }
-            if (index >= count) {
-                throw new IndexOutOfBoundsException(
-                    "attempted to fetch the [" + index + "] date when there are only [" + count + "] dates."
-                );
-            }
+        public ZonedDateTime getInternal(int index) {
             return dates[index];
             return dates[index];
         }
         }
 
 
@@ -184,7 +200,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         /**
         /**
          * Refresh the backing array. Package private so it can be called when {@link Longs} loads dates.
          * Refresh the backing array. Package private so it can be called when {@link Longs} loads dates.
          */
          */
-        void refreshArray() throws IOException {
+        private void refreshArray() throws IOException {
             if (count == 0) {
             if (count == 0) {
                 return;
                 return;
             }
             }
@@ -202,13 +218,49 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         }
         }
     }
     }
 
 
-    public static final class Doubles extends ScriptDocValues<Double> {
+    public static class Dates extends ScriptDocValues<ZonedDateTime> {
+
+        public Dates(Supplier<ZonedDateTime> supplier) {
+            super(supplier);
+        }
+
+        /**
+         * Fetch the first field value or 0 millis after epoch if there are no
+         * in.
+         */
+        public ZonedDateTime getValue() {
+            return get(0);
+        }
+
+        @Override
+        public ZonedDateTime get(int index) {
+            if (supplier.size() == 0) {
+                throw new IllegalStateException(
+                    "A document doesn't have a value for a field! "
+                        + "Use doc[<field>].size()==0 to check if a document is missing a field!"
+                );
+            }
+            if (index >= supplier.size()) {
+                throw new IndexOutOfBoundsException(
+                    "attempted to fetch the [" + index + "] date when there are only [" + supplier.size() + "] dates."
+                );
+            }
+            return supplier.getInternal(index);
+        }
+
+        @Override
+        public int size() {
+            return supplier.size();
+        }
+    }
+
+    public static class DoublesSupplier implements Supplier<Double> {
 
 
         private final SortedNumericDoubleValues in;
         private final SortedNumericDoubleValues in;
         private double[] values = new double[0];
         private double[] values = new double[0];
         private int count;
         private int count;
 
 
-        public Doubles(SortedNumericDoubleValues in) {
+        public DoublesSupplier(SortedNumericDoubleValues in) {
             this.in = in;
             this.in = in;
         }
         }
 
 
@@ -228,13 +280,26 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * store at least that many entries.
          * store at least that many entries.
          */
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             count = newSize;
             values = ArrayUtil.grow(values, count);
             values = ArrayUtil.grow(values, count);
         }
         }
 
 
-        public SortedNumericDoubleValues getInternalValues() {
-            return this.in;
+        @Override
+        public Double getInternal(int index) {
+            return values[index];
+        }
+
+        @Override
+        public int size() {
+            return count;
+        }
+    }
+
+    public static class Doubles extends ScriptDocValues<Double> {
+
+        public Doubles(Supplier<Double> supplier) {
+            super(supplier);
         }
         }
 
 
         public double getValue() {
         public double getValue() {
@@ -243,22 +308,27 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
 
         @Override
         @Override
         public Double get(int index) {
         public Double get(int index) {
-            if (count == 0) {
+            if (supplier.size() == 0) {
                 throw new IllegalStateException(
                 throw new IllegalStateException(
                     "A document doesn't have a value for a field! "
                     "A document doesn't have a value for a field! "
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                 );
                 );
             }
             }
-            return values[index];
+            return supplier.getInternal(index);
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return count;
+            return supplier.size();
         }
         }
     }
     }
 
 
     public abstract static class Geometry<T> extends ScriptDocValues<T> {
     public abstract static class Geometry<T> extends ScriptDocValues<T> {
+
+        public Geometry(Supplier<T> supplier) {
+            super(supplier);
+        }
+
         /** Returns the dimensional type of this geometry */
         /** Returns the dimensional type of this geometry */
         public abstract int getDimensionalType();
         public abstract int getDimensionalType();
 
 
@@ -275,7 +345,14 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         public abstract double getMercatorHeight();
         public abstract double getMercatorHeight();
     }
     }
 
 
-    public static final class GeoPoints extends Geometry<GeoPoint> {
+    public interface GeometrySupplier<T> extends Supplier<T> {
+
+        GeoPoint getCentroid();
+
+        GeoBoundingBox getBoundingBox();
+    }
+
+    public static class GeoPointsSupplier implements GeometrySupplier<GeoPoint> {
 
 
         private final MultiGeoPointValues in;
         private final MultiGeoPointValues in;
         private GeoPoint[] values = new GeoPoint[0];
         private GeoPoint[] values = new GeoPoint[0];
@@ -283,7 +360,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         private final GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
         private final GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
         private int count;
         private int count;
 
 
-        public GeoPoints(MultiGeoPointValues in) {
+        public GeoPointsSupplier(MultiGeoPointValues in) {
             this.in = in;
             this.in = in;
         }
         }
 
 
@@ -335,7 +412,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * store at least that many entries.
          * store at least that many entries.
          */
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             count = newSize;
             if (newSize > values.length) {
             if (newSize > values.length) {
                 int oldLength = values.length;
                 int oldLength = values.length;
@@ -346,6 +423,36 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
             }
             }
         }
         }
 
 
+        @Override
+        public GeoPoint getInternal(int index) {
+            return values[index];
+        }
+
+        @Override
+        public GeoPoint getCentroid() {
+            return centroid;
+        }
+
+        @Override
+        public GeoBoundingBox getBoundingBox() {
+            return boundingBox;
+        }
+
+        @Override
+        public int size() {
+            return count;
+        }
+    }
+
+    public static class GeoPoints extends Geometry<GeoPoint> {
+
+        private final GeometrySupplier<GeoPoint> geometrySupplier;
+
+        public GeoPoints(GeometrySupplier<GeoPoint> supplier) {
+            super(supplier);
+            geometrySupplier = supplier;
+        }
+
         public GeoPoint getValue() {
         public GeoPoint getValue() {
             return get(0);
             return get(0);
         }
         }
@@ -376,19 +483,19 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
 
         @Override
         @Override
         public GeoPoint get(int index) {
         public GeoPoint get(int index) {
-            if (count == 0) {
+            if (supplier.size() == 0) {
                 throw new IllegalStateException(
                 throw new IllegalStateException(
                     "A document doesn't have a value for a field! "
                     "A document doesn't have a value for a field! "
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                 );
                 );
             }
             }
-            final GeoPoint point = values[index];
+            final GeoPoint point = supplier.getInternal(index);
             return new GeoPoint(point.lat(), point.lon());
             return new GeoPoint(point.lat(), point.lon());
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return count;
+            return supplier.size();
         }
         }
 
 
         public double arcDistance(double lat, double lon) {
         public double arcDistance(double lat, double lon) {
@@ -434,7 +541,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
 
         @Override
         @Override
         public GeoPoint getCentroid() {
         public GeoPoint getCentroid() {
-            return size() == 0 ? null : centroid;
+            return size() == 0 ? null : geometrySupplier.getCentroid();
         }
         }
 
 
         @Override
         @Override
@@ -449,21 +556,14 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
 
         @Override
         @Override
         public GeoBoundingBox getBoundingBox() {
         public GeoBoundingBox getBoundingBox() {
-            return size() == 0 ? null : boundingBox;
+            return size() == 0 ? null : geometrySupplier.getBoundingBox();
         }
         }
     }
     }
 
 
-    public static final class Booleans extends ScriptDocValues<Boolean> {
-
-        private final BooleanDocValuesField booleanDocValuesField;
+    public static class Booleans extends ScriptDocValues<Boolean> {
 
 
-        public Booleans(BooleanDocValuesField booleanDocValuesField) {
-            this.booleanDocValuesField = booleanDocValuesField;
-        }
-
-        @Override
-        public void setNextDocId(int docId) throws IOException {
-            throw new UnsupportedOperationException();
+        public Booleans(Supplier<Boolean> supplier) {
+            super(supplier);
         }
         }
 
 
         public boolean getValue() {
         public boolean getValue() {
@@ -474,22 +574,22 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         @Override
         public Boolean get(int index) {
         public Boolean get(int index) {
             throwIfEmpty();
             throwIfEmpty();
-            return booleanDocValuesField.getInternal(index);
+            return supplier.getInternal(index);
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return booleanDocValuesField.size();
+            return supplier.size();
         }
         }
     }
     }
 
 
-    abstract static class BinaryScriptDocValues<T> extends ScriptDocValues<T> {
+    public static class StringsSupplier implements Supplier<String> {
 
 
         private final SortedBinaryDocValues in;
         private final SortedBinaryDocValues in;
-        protected BytesRefBuilder[] values = new BytesRefBuilder[0];
-        protected int count;
+        private BytesRefBuilder[] values = new BytesRefBuilder[0];
+        private int count;
 
 
-        BinaryScriptDocValues(SortedBinaryDocValues in) {
+        public StringsSupplier(SortedBinaryDocValues in) {
             this.in = in;
             this.in = in;
         }
         }
 
 
@@ -512,7 +612,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * store at least that many entries.
          * store at least that many entries.
          */
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             count = newSize;
             if (newSize > values.length) {
             if (newSize > values.length) {
                 final int oldLength = values.length;
                 final int oldLength = values.length;
@@ -523,51 +623,52 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
             }
             }
         }
         }
 
 
+        protected String bytesToString(BytesRef bytesRef) {
+            return bytesRef.utf8ToString();
+        }
+
+        @Override
+        public String getInternal(int index) {
+            return bytesToString(values[index].toBytesRef());
+        }
+
         @Override
         @Override
         public int size() {
         public int size() {
             return count;
             return count;
         }
         }
     }
     }
 
 
-    public static class Strings extends BinaryScriptDocValues<String> {
-        public Strings(SortedBinaryDocValues in) {
-            super(in);
+    public static class Strings extends ScriptDocValues<String> {
+
+        public Strings(Supplier<String> supplier) {
+            super(supplier);
+        }
+
+        public String getValue() {
+            return get(0);
         }
         }
 
 
         @Override
         @Override
-        public final String get(int index) {
-            if (count == 0) {
+        public String get(int index) {
+            if (supplier.size() == 0) {
                 throw new IllegalStateException(
                 throw new IllegalStateException(
                     "A document doesn't have a value for a field! "
                     "A document doesn't have a value for a field! "
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                         + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                 );
                 );
             }
             }
-            return bytesToString(values[index].get());
-        }
-
-        /**
-         * Convert the stored bytes to a String.
-         */
-        protected String bytesToString(BytesRef bytes) {
-            return bytes.utf8ToString();
+            return supplier.getInternal(index);
         }
         }
 
 
-        public final String getValue() {
-            return get(0);
+        @Override
+        public int size() {
+            return supplier.size();
         }
         }
     }
     }
 
 
     public static final class BytesRefs extends ScriptDocValues<BytesRef> {
     public static final class BytesRefs extends ScriptDocValues<BytesRef> {
 
 
-        private final BinaryDocValuesField binaryDocValuesField;
-
-        public BytesRefs(BinaryDocValuesField binaryDocValuesField) {
-            this.binaryDocValuesField = binaryDocValuesField;
-        }
-
-        @Override
-        public void setNextDocId(int docId) throws IOException {
-            throw new UnsupportedOperationException();
+        public BytesRefs(Supplier<BytesRef> supplier) {
+            super(supplier);
         }
         }
 
 
         public BytesRef getValue() {
         public BytesRef getValue() {
@@ -578,12 +679,12 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         @Override
         public BytesRef get(int index) {
         public BytesRef get(int index) {
             throwIfEmpty();
             throwIfEmpty();
-            return binaryDocValuesField.getInternal(index);
+            return supplier.getInternal(index);
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return binaryDocValuesField.size();
+            return supplier.size();
         }
         }
     }
     }
 }
 }

+ 2 - 1
server/src/main/java/org/elasticsearch/index/fielddata/StringScriptFieldData.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.index.fielddata;
 package org.elasticsearch.index.fielddata;
 
 
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
+import org.elasticsearch.index.fielddata.ScriptDocValues.StringsSupplier;
 import org.elasticsearch.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.script.StringFieldScript;
 import org.elasticsearch.script.StringFieldScript;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DelegateDocValuesField;
@@ -45,7 +46,7 @@ public class StringScriptFieldData extends BinaryScriptFieldData {
         return new BinaryScriptLeafFieldData() {
         return new BinaryScriptLeafFieldData() {
             @Override
             @Override
             public DocValuesField<?> getScriptField(String name) {
             public DocValuesField<?> getScriptField(String name) {
-                return new DelegateDocValuesField(new ScriptDocValues.Strings(getBytesValues()), name);
+                return new DelegateDocValuesField(new ScriptDocValues.Strings(new StringsSupplier(getBytesValues())), name);
             }
             }
 
 
             @Override
             @Override

+ 2 - 1
server/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractLeafGeoPointFieldData.java

@@ -12,6 +12,7 @@ import org.elasticsearch.index.fielddata.FieldData;
 import org.elasticsearch.index.fielddata.LeafGeoPointFieldData;
 import org.elasticsearch.index.fielddata.LeafGeoPointFieldData;
 import org.elasticsearch.index.fielddata.MultiGeoPointValues;
 import org.elasticsearch.index.fielddata.MultiGeoPointValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
+import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPointsSupplier;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
@@ -28,7 +29,7 @@ public abstract class AbstractLeafGeoPointFieldData implements LeafGeoPointField
 
 
     @Override
     @Override
     public final DocValuesField<?> getScriptField(String name) {
     public final DocValuesField<?> getScriptField(String name) {
-        return new DelegateDocValuesField(new ScriptDocValues.GeoPoints(getGeoPointValues()), name);
+        return new DelegateDocValuesField(new ScriptDocValues.GeoPoints(new GeoPointsSupplier(getGeoPointValues())), name);
     }
     }
 
 
     public static LeafGeoPointFieldData empty(final int maxDoc) {
     public static LeafGeoPointFieldData empty(final int maxDoc) {

+ 1 - 1
server/src/main/java/org/elasticsearch/index/fielddata/plain/AbstractLeafOrdinalsFieldData.java

@@ -26,7 +26,7 @@ public abstract class AbstractLeafOrdinalsFieldData implements LeafOrdinalsField
 
 
     public static final Function<SortedSetDocValues, ScriptDocValues<?>> DEFAULT_SCRIPT_FUNCTION = ((Function<
     public static final Function<SortedSetDocValues, ScriptDocValues<?>> DEFAULT_SCRIPT_FUNCTION = ((Function<
         SortedSetDocValues,
         SortedSetDocValues,
-        SortedBinaryDocValues>) FieldData::toString).andThen(ScriptDocValues.Strings::new);
+        SortedBinaryDocValues>) FieldData::toString).andThen(ScriptDocValues.StringsSupplier::new).andThen(ScriptDocValues.Strings::new);
 
 
     private final Function<SortedSetDocValues, ScriptDocValues<?>> scriptFunction;
     private final Function<SortedSetDocValues, ScriptDocValues<?>> scriptFunction;
 
 

+ 1 - 1
server/src/main/java/org/elasticsearch/index/fielddata/plain/BinaryDVLeafFieldData.java

@@ -46,7 +46,7 @@ public class BinaryDVLeafFieldData implements LeafFieldData {
 
 
     @Override
     @Override
     public DocValuesField<?> getScriptField(String name) {
     public DocValuesField<?> getScriptField(String name) {
-        return new DelegateDocValuesField(new ScriptDocValues.Strings(getBytesValues()), name);
+        return new DelegateDocValuesField(new ScriptDocValues.Strings(new ScriptDocValues.StringsSupplier(getBytesValues())), name);
     }
     }
 
 
     @Override
     @Override

+ 1 - 1
server/src/main/java/org/elasticsearch/index/fielddata/plain/StringBinaryDVLeafFieldData.java

@@ -20,6 +20,6 @@ final class StringBinaryDVLeafFieldData extends AbstractBinaryDVLeafFieldData {
 
 
     @Override
     @Override
     public DocValuesField<?> getScriptField(String name) {
     public DocValuesField<?> getScriptField(String name) {
-        return new DelegateDocValuesField(new ScriptDocValues.Strings(getBytesValues()), name);
+        return new DelegateDocValuesField(new ScriptDocValues.Strings(new ScriptDocValues.StringsSupplier(getBytesValues())), name);
     }
     }
 }
 }

+ 7 - 2
server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

@@ -34,6 +34,7 @@ import org.elasticsearch.core.TimeValue;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DatesSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.query.DateRangeIncludingNowQuery;
 import org.elasticsearch.index.query.DateRangeIncludingNowQuery;
 import org.elasticsearch.index.query.QueryRewriteContext;
 import org.elasticsearch.index.query.QueryRewriteContext;
@@ -80,7 +81,7 @@ public final class DateFieldMapper extends FieldMapper {
     private static final DateMathParser EPOCH_MILLIS_PARSER = DateFormatter.forPattern("epoch_millis").toDateMathParser();
     private static final DateMathParser EPOCH_MILLIS_PARSER = DateFormatter.forPattern("epoch_millis").toDateMathParser();
 
 
     public enum Resolution {
     public enum Resolution {
-        MILLISECONDS(CONTENT_TYPE, NumericType.DATE, (dv, n) -> new DelegateDocValuesField(new Dates(dv, false), n)) {
+        MILLISECONDS(CONTENT_TYPE, NumericType.DATE, (dv, n) -> new DelegateDocValuesField(new Dates(new DatesSupplier(dv, false)), n)) {
             @Override
             @Override
             public long convert(Instant instant) {
             public long convert(Instant instant) {
                 return instant.toEpochMilli();
                 return instant.toEpochMilli();
@@ -111,7 +112,11 @@ public final class DateFieldMapper extends FieldMapper {
                 return LongPoint.newDistanceFeatureQuery(field, boost, origin, pivot.getMillis());
                 return LongPoint.newDistanceFeatureQuery(field, boost, origin, pivot.getMillis());
             }
             }
         },
         },
-        NANOSECONDS(DATE_NANOS_CONTENT_TYPE, NumericType.DATE_NANOSECONDS, (dv, n) -> new DelegateDocValuesField(new Dates(dv, true), n)) {
+        NANOSECONDS(
+            DATE_NANOS_CONTENT_TYPE,
+            NumericType.DATE_NANOSECONDS,
+            (dv, n) -> new DelegateDocValuesField(new Dates(new DatesSupplier(dv, true)), n)
+        ) {
             @Override
             @Override
             public long convert(Instant instant) {
             public long convert(Instant instant) {
                 return toLong(instant);
                 return toLong(instant);

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/DoubleScriptFieldType.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.index.fielddata.DoubleScriptFieldData;
 import org.elasticsearch.index.fielddata.DoubleScriptFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
 import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.script.CompositeFieldScript;
 import org.elasticsearch.script.CompositeFieldScript;
@@ -103,7 +104,7 @@ public final class DoubleScriptFieldType extends AbstractScriptFieldType<DoubleF
         return new DoubleScriptFieldData.Builder(
         return new DoubleScriptFieldData.Builder(
             name(),
             name(),
             leafFactory(searchLookup.get()),
             leafFactory(searchLookup.get()),
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         );
         );
     }
     }
 
 

+ 1 - 1
server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java

@@ -220,7 +220,7 @@ public class IdFieldMapper extends MetadataFieldMapper {
 
 
             @Override
             @Override
             public DocValuesField<?> getScriptField(String name) {
             public DocValuesField<?> getScriptField(String name) {
-                return new DelegateDocValuesField(new ScriptDocValues.Strings(getBytesValues()), name);
+                return new DelegateDocValuesField(new ScriptDocValues.Strings(new ScriptDocValues.StringsSupplier(getBytesValues())), name);
             }
             }
 
 
             @Override
             @Override

+ 47 - 25
server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java

@@ -29,6 +29,7 @@ import org.elasticsearch.core.Tuple;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData;
+import org.elasticsearch.index.mapper.IpFieldMapper.IpFieldType.IpScriptDocValues.IpSupplier;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.script.IpFieldScript;
 import org.elasticsearch.script.IpFieldScript;
 import org.elasticsearch.script.Script;
 import org.elasticsearch.script.Script;
@@ -351,27 +352,52 @@ public class IpFieldMapper extends FieldMapper {
 
 
         public static final class IpScriptDocValues extends ScriptDocValues<String> {
         public static final class IpScriptDocValues extends ScriptDocValues<String> {
 
 
-            private final SortedSetDocValues in;
-            private long[] ords = new long[0];
-            private int count;
+            public static final class IpSupplier implements ScriptDocValues.Supplier<String> {
 
 
-            public IpScriptDocValues(SortedSetDocValues in) {
-                this.in = in;
-            }
+                private final SortedSetDocValues in;
+                private long[] ords = new long[0];
+                private int count;
 
 
-            @Override
-            public void setNextDocId(int docId) throws IOException {
-                count = 0;
-                if (in.advanceExact(docId)) {
-                    for (long ord = in.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = in.nextOrd()) {
-                        ords = ArrayUtil.grow(ords, count + 1);
-                        ords[count++] = ord;
+                public IpSupplier(SortedSetDocValues in) {
+                    this.in = in;
+                }
+
+                @Override
+                public void setNextDocId(int docId) throws IOException {
+                    count = 0;
+                    if (in.advanceExact(docId)) {
+                        for (long ord = in.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = in.nextOrd()) {
+                            ords = ArrayUtil.grow(ords, count + 1);
+                            ords[count++] = ord;
+                        }
+                    }
+                }
+
+                @Override
+                public String getInternal(int index) {
+                    try {
+                        BytesRef encoded = in.lookupOrd(ords[index]);
+                        InetAddress address = InetAddressPoint.decode(
+                            Arrays.copyOfRange(encoded.bytes, encoded.offset, encoded.offset + encoded.length)
+                        );
+                        return InetAddresses.toAddrString(address);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
                     }
                     }
                 }
                 }
+
+                @Override
+                public int size() {
+                    return count;
+                }
+            }
+
+            public IpScriptDocValues(IpSupplier supplier) {
+                super(supplier);
             }
             }
 
 
             public String getValue() {
             public String getValue() {
-                if (count == 0) {
+                if (supplier.size() == 0) {
                     return null;
                     return null;
                 } else {
                 } else {
                     return get(0);
                     return get(0);
@@ -380,27 +406,23 @@ public class IpFieldMapper extends FieldMapper {
 
 
             @Override
             @Override
             public String get(int index) {
             public String get(int index) {
-                try {
-                    BytesRef encoded = in.lookupOrd(ords[index]);
-                    InetAddress address = InetAddressPoint.decode(
-                        Arrays.copyOfRange(encoded.bytes, encoded.offset, encoded.offset + encoded.length)
-                    );
-                    return InetAddresses.toAddrString(address);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
+                return supplier.getInternal(index);
             }
             }
 
 
             @Override
             @Override
             public int size() {
             public int size() {
-                return count;
+                return supplier.size();
             }
             }
         }
         }
 
 
         @Override
         @Override
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
             failIfNoDocValues();
             failIfNoDocValues();
-            return new SortedSetOrdinalsIndexFieldData.Builder(name(), IpScriptDocValues::new, CoreValuesSourceType.IP);
+            return new SortedSetOrdinalsIndexFieldData.Builder(
+                name(),
+                s -> new IpScriptDocValues(new IpSupplier(s)),
+                CoreValuesSourceType.IP
+            );
         }
         }
 
 
         @Override
         @Override

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/LongScriptFieldType.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.index.fielddata.LongScriptFieldData;
 import org.elasticsearch.index.fielddata.LongScriptFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
 import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.script.CompositeFieldScript;
 import org.elasticsearch.script.CompositeFieldScript;
@@ -98,7 +99,7 @@ public final class LongScriptFieldType extends AbstractScriptFieldType<LongField
         return new LongScriptFieldData.Builder(
         return new LongScriptFieldData.Builder(
             name(),
             name(),
             leafFactory(searchLookup.get()),
             leafFactory(searchLookup.get()),
-            (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
         );
         );
     }
     }
 
 

+ 9 - 7
server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java

@@ -36,7 +36,9 @@ import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.mapper.TimeSeriesParams.MetricType;
 import org.elasticsearch.index.mapper.TimeSeriesParams.MetricType;
@@ -335,7 +337,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedDoublesIndexFieldData.Builder(
                 return new SortedDoublesIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
                 );
                 );
             }
             }
 
 
@@ -446,7 +448,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedDoublesIndexFieldData.Builder(
                 return new SortedDoublesIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
                 );
                 );
             }
             }
 
 
@@ -540,7 +542,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedDoublesIndexFieldData.Builder(
                 return new SortedDoublesIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
                 );
                 );
             }
             }
 
 
@@ -621,7 +623,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedNumericIndexFieldData.Builder(
                 return new SortedNumericIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
                 );
                 );
             }
             }
         },
         },
@@ -692,7 +694,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedNumericIndexFieldData.Builder(
                 return new SortedNumericIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
                 );
                 );
             }
             }
         },
         },
@@ -822,7 +824,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedNumericIndexFieldData.Builder(
                 return new SortedNumericIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
                 );
                 );
             }
             }
         },
         },
@@ -922,7 +924,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedNumericIndexFieldData.Builder(
                 return new SortedNumericIndexFieldData.Builder(
                     name,
                     name,
                     numericType(),
                     numericType(),
-                    (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                    (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
                 );
                 );
             }
             }
         };
         };

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java

@@ -18,6 +18,7 @@ import org.elasticsearch.core.Nullable;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.seqno.SequenceNumbers;
 import org.elasticsearch.index.seqno.SequenceNumbers;
@@ -188,7 +189,7 @@ public class SeqNoFieldMapper extends MetadataFieldMapper {
             return new SortedNumericIndexFieldData.Builder(
             return new SortedNumericIndexFieldData.Builder(
                 name(),
                 name(),
                 NumericType.LONG,
                 NumericType.LONG,
-                (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
             );
             );
         }
         }
     }
     }

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java

@@ -14,6 +14,7 @@ import org.apache.lucene.search.Query;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.query.QueryShardException;
 import org.elasticsearch.index.query.QueryShardException;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
@@ -62,7 +63,7 @@ public class VersionFieldMapper extends MetadataFieldMapper {
             return new SortedNumericIndexFieldData.Builder(
             return new SortedNumericIndexFieldData.Builder(
                 name(),
                 name(),
                 NumericType.LONG,
                 NumericType.LONG,
-                (dv, n) -> new DelegateDocValuesField(new Longs(dv), n)
+                (dv, n) -> new DelegateDocValuesField(new Longs(new LongsSupplier(dv)), n)
             );
             );
         }
         }
     }
     }

+ 1 - 1
server/src/main/java/org/elasticsearch/script/ScoreScriptUtils.java

@@ -57,7 +57,7 @@ public final class ScoreScriptUtils {
 
 
         public double randomScore() {
         public double randomScore() {
             try {
             try {
-                docValues.setNextDocId(scoreScript._getDocId());
+                docValues.getSupplier().setNextDocId(scoreScript._getDocId());
                 String seedValue = String.valueOf(docValues.get(0));
                 String seedValue = String.valueOf(docValues.get(0));
                 int hash = StringHelper.murmurhash3_x86_32(new BytesRef(seedValue), saltedSeed);
                 int hash = StringHelper.murmurhash3_x86_32(new BytesRef(seedValue), saltedSeed);
                 return (hash & 0x00FFFFFF) / (float) (1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0
                 return (hash & 0x00FFFFFF) / (float) (1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0

+ 3 - 2
server/src/main/java/org/elasticsearch/script/field/BinaryDocValuesField.java

@@ -20,7 +20,7 @@ import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.NoSuchElementException;
 
 
-public class BinaryDocValuesField implements DocValuesField<ByteBuffer> {
+public class BinaryDocValuesField implements DocValuesField<ByteBuffer>, ScriptDocValues.Supplier<BytesRef> {
 
 
     private final SortedBinaryDocValues input;
     private final SortedBinaryDocValues input;
     private final String name;
     private final String name;
@@ -74,8 +74,9 @@ public class BinaryDocValuesField implements DocValuesField<ByteBuffer> {
         return bytesRefs;
         return bytesRefs;
     }
     }
 
 
-    // this method is required to support the ByteRef return values
+    // this method is required to support the Boolean return values
     // for the old-style "doc" access in ScriptDocValues
     // for the old-style "doc" access in ScriptDocValues
+    @Override
     public BytesRef getInternal(int index) {
     public BytesRef getInternal(int index) {
         return values[index].toBytesRef();
         return values[index].toBytesRef();
     }
     }

+ 22 - 42
server/src/main/java/org/elasticsearch/script/field/BooleanDocValuesField.java

@@ -17,7 +17,7 @@ import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import java.util.NoSuchElementException;
 
 
-public class BooleanDocValuesField implements DocValuesField<Boolean> {
+public class BooleanDocValuesField implements DocValuesField<Boolean>, ScriptDocValues.Supplier<Boolean> {
 
 
     private final SortedNumericDocValues input;
     private final SortedNumericDocValues input;
     private final String name;
     private final String name;
@@ -25,6 +25,8 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
     private boolean[] values = new boolean[0];
     private boolean[] values = new boolean[0];
     private int count;
     private int count;
 
 
+    // used for backwards compatibility for old-style "doc" access
+    // as a delegate to this field class
     private ScriptDocValues.Booleans booleans = null;
     private ScriptDocValues.Booleans booleans = null;
 
 
     public BooleanDocValuesField(SortedNumericDocValues input, String name) {
     public BooleanDocValuesField(SortedNumericDocValues input, String name) {
@@ -32,11 +34,6 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
         this.name = name;
         this.name = name;
     }
     }
 
 
-    /**
-     * Set the current document ID.
-     *
-     * @param docId
-     */
     @Override
     @Override
     public void setNextDocId(int docId) throws IOException {
     public void setNextDocId(int docId) throws IOException {
         if (input.advanceExact(docId)) {
         if (input.advanceExact(docId)) {
@@ -58,11 +55,6 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
         }
         }
     }
     }
 
 
-    /**
-     * Returns a {@code ScriptDocValues} of the appropriate type for this field.
-     * This is used to support backwards compatibility for accessing field values
-     * through the {@code doc} variable.
-     */
     @Override
     @Override
     public ScriptDocValues<?> getScriptDocValues() {
     public ScriptDocValues<?> getScriptDocValues() {
         if (booleans == null) {
         if (booleans == null) {
@@ -72,35 +64,40 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
         return booleans;
         return booleans;
     }
     }
 
 
-    /**
-     * Returns the name of this field.
-     */
+    // this method is required to support the Boolean return values
+    // for the old-style "doc" access in ScriptDocValues
+    @Override
+    public Boolean getInternal(int index) {
+        return values[index];
+    }
+
     @Override
     @Override
     public String getName() {
     public String getName() {
         return name;
         return name;
     }
     }
 
 
-    /**
-     * Returns {@code true} if this field has no values, otherwise {@code false}.
-     */
     @Override
     @Override
     public boolean isEmpty() {
     public boolean isEmpty() {
         return count == 0;
         return count == 0;
     }
     }
 
 
-    /**
-     * Returns the number of values this field has.
-     */
     @Override
     @Override
     public int size() {
     public int size() {
         return count;
         return count;
     }
     }
 
 
-    /**
-     * Returns an iterator over elements of type {@code T}.
-     *
-     * @return an Iterator.
-     */
+    public boolean get(boolean defaultValue) {
+        return get(0, defaultValue);
+    }
+
+    public boolean get(int index, boolean defaultValue) {
+        if (isEmpty() || index < 0 || index >= count) {
+            return defaultValue;
+        }
+
+        return values[index];
+    }
+
     @Override
     @Override
     public Iterator<Boolean> iterator() {
     public Iterator<Boolean> iterator() {
         return new Iterator<Boolean>() {
         return new Iterator<Boolean>() {
@@ -120,21 +117,4 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
             }
             }
         };
         };
     }
     }
-
-    public boolean get(boolean defaultValue) {
-        return get(0, defaultValue);
-    }
-
-    public boolean get(int index, boolean defaultValue) {
-        if (isEmpty() || index < 0 || index >= count) {
-            return defaultValue;
-        }
-
-        return values[index];
-    }
-
-    // this method is required to support the old-style "doc" access in ScriptDocValues
-    public boolean getInternal(int index) {
-        return values[index];
-    }
 }
 }

+ 4 - 1
server/src/main/java/org/elasticsearch/script/field/DelegateDocValuesField.java

@@ -23,13 +23,16 @@ public class DelegateDocValuesField implements DocValuesField<Object> {
     private final String name;
     private final String name;
 
 
     public DelegateDocValuesField(ScriptDocValues<?> scriptDocValues, String name) {
     public DelegateDocValuesField(ScriptDocValues<?> scriptDocValues, String name) {
+        // Suppliers provided via ScriptDocValues should never be a DocValuesField
+        // as we expect DelegateDocValuesField to only support old-style ScriptDocValues
+        assert scriptDocValues.getSupplier() instanceof DocValuesField == false;
         this.scriptDocValues = scriptDocValues;
         this.scriptDocValues = scriptDocValues;
         this.name = name;
         this.name = name;
     }
     }
 
 
     @Override
     @Override
     public void setNextDocId(int docId) throws IOException {
     public void setNextDocId(int docId) throws IOException {
-        scriptDocValues.setNextDocId(docId);
+        scriptDocValues.getSupplier().setNextDocId(docId);
     }
     }
 
 
     @Override
     @Override

+ 10 - 9
server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesGeoPointsTests.java

@@ -11,6 +11,7 @@ package org.elasticsearch.index.fielddata;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.geo.GeoPoint;
 import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.common.geo.GeoUtils;
 import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints;
 import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints;
+import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPointsSupplier;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -62,11 +63,11 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
 
 
         GeoPoint[][] points = { { new GeoPoint(lat1, lon1), new GeoPoint(lat2, lon2) } };
         GeoPoint[][] points = { { new GeoPoint(lat1, lon1), new GeoPoint(lat2, lon2) } };
         final MultiGeoPointValues values = wrap(points);
         final MultiGeoPointValues values = wrap(points);
-        final ScriptDocValues.GeoPoints script = new ScriptDocValues.GeoPoints(values);
+        final ScriptDocValues.GeoPoints script = new ScriptDocValues.GeoPoints(new GeoPointsSupplier(values));
 
 
-        script.setNextDocId(1);
+        script.getSupplier().setNextDocId(1);
         assertEquals(true, script.isEmpty());
         assertEquals(true, script.isEmpty());
-        script.setNextDocId(0);
+        script.getSupplier().setNextDocId(0);
         assertEquals(false, script.isEmpty());
         assertEquals(false, script.isEmpty());
         assertEquals(new GeoPoint(lat1, lon1), script.getValue());
         assertEquals(new GeoPoint(lat1, lon1), script.getValue());
         assertEquals(lat1, script.getLat(), 0);
         assertEquals(lat1, script.getLat(), 0);
@@ -80,12 +81,12 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
         final double lon = randomLon();
         final double lon = randomLon();
         GeoPoint[][] points = { { new GeoPoint(lat, lon) } };
         GeoPoint[][] points = { { new GeoPoint(lat, lon) } };
         final MultiGeoPointValues values = wrap(points);
         final MultiGeoPointValues values = wrap(points);
-        final ScriptDocValues.GeoPoints script = new ScriptDocValues.GeoPoints(values);
-        script.setNextDocId(0);
+        final ScriptDocValues.GeoPoints script = new ScriptDocValues.GeoPoints(new GeoPointsSupplier(values));
+        script.getSupplier().setNextDocId(0);
 
 
         GeoPoint[][] points2 = { new GeoPoint[0] };
         GeoPoint[][] points2 = { new GeoPoint[0] };
-        final ScriptDocValues.GeoPoints emptyScript = new ScriptDocValues.GeoPoints(wrap(points2));
-        emptyScript.setNextDocId(0);
+        final ScriptDocValues.GeoPoints emptyScript = new ScriptDocValues.GeoPoints(new GeoPointsSupplier(wrap(points2)));
+        emptyScript.getSupplier().setNextDocId(0);
 
 
         final double otherLat = randomLat();
         final double otherLat = randomLat();
         final double otherLon = randomLon();
         final double otherLon = randomLon();
@@ -115,9 +116,9 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
                 points[d][i] = new GeoPoint(randomLat(), randomLon());
                 points[d][i] = new GeoPoint(randomLat(), randomLon());
             }
             }
         }
         }
-        final ScriptDocValues.GeoPoints geoPoints = new GeoPoints(wrap(points));
+        final ScriptDocValues.GeoPoints geoPoints = new GeoPoints(new GeoPointsSupplier(wrap(points)));
         for (int d = 0; d < points.length; d++) {
         for (int d = 0; d < points.length; d++) {
-            geoPoints.setNextDocId(d);
+            geoPoints.getSupplier().setNextDocId(d);
             if (points[d].length > 0) {
             if (points[d].length > 0) {
                 assertEquals(points[d][0], geoPoints.getValue());
                 assertEquals(points[d][0], geoPoints.getValue());
             } else {
             } else {

+ 4 - 3
server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.index.fielddata;
 package org.elasticsearch.index.fielddata;
 
 
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -27,7 +28,7 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
 
 
         for (int round = 0; round < 10; round++) {
         for (int round = 0; round < 10; round++) {
             int d = between(0, values.length - 1);
             int d = between(0, values.length - 1);
-            longs.setNextDocId(d);
+            longs.getSupplier().setNextDocId(d);
             if (values[d].length > 0) {
             if (values[d].length > 0) {
                 assertEquals(values[d][0], longs.getValue());
                 assertEquals(values[d][0], longs.getValue());
                 assertEquals(values[d][0], (long) longs.get(0));
                 assertEquals(values[d][0], (long) longs.get(0));
@@ -56,7 +57,7 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
     }
     }
 
 
     private Longs wrap(long[][] values) {
     private Longs wrap(long[][] values) {
-        return new Longs(new AbstractSortedNumericDocValues() {
+        return new Longs(new LongsSupplier(new AbstractSortedNumericDocValues() {
             long[] current;
             long[] current;
             int i;
             int i;
 
 
@@ -76,6 +77,6 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
             public long nextValue() {
             public long nextValue() {
                 return current[i++];
                 return current[i++];
             }
             }
-        });
+        }));
     }
     }
 }
 }

+ 3 - 2
server/src/test/java/org/elasticsearch/index/fielddata/plain/HalfFloatFielddataTests.java

@@ -19,6 +19,7 @@ import org.apache.lucene.util.TestUtil;
 import org.elasticsearch.core.internal.io.IOUtils;
 import org.elasticsearch.core.internal.io.IOUtils;
 import org.elasticsearch.index.fielddata.FieldData;
 import org.elasticsearch.index.fielddata.FieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DelegateDocValuesField;
@@ -42,7 +43,7 @@ public class HalfFloatFielddataTests extends ESTestCase {
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
             reader,
             reader,
             "half_float",
             "half_float",
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         ).getDoubleValues();
         ).getDoubleValues();
         assertNotNull(FieldData.unwrapSingleton(values));
         assertNotNull(FieldData.unwrapSingleton(values));
         assertTrue(values.advanceExact(0));
         assertTrue(values.advanceExact(0));
@@ -67,7 +68,7 @@ public class HalfFloatFielddataTests extends ESTestCase {
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
             reader,
             reader,
             "half_float",
             "half_float",
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         ).getDoubleValues();
         ).getDoubleValues();
         assertNull(FieldData.unwrapSingleton(values));
         assertNull(FieldData.unwrapSingleton(values));
         assertTrue(values.advanceExact(0));
         assertTrue(values.advanceExact(0));

+ 2 - 1
server/src/test/java/org/elasticsearch/index/mapper/DateFieldTypeTests.java

@@ -32,6 +32,7 @@ import org.elasticsearch.index.IndexSettings;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData;
 import org.elasticsearch.index.fielddata.LeafNumericFieldData;
 import org.elasticsearch.index.fielddata.LeafNumericFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DatesSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
 import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
 import org.elasticsearch.index.mapper.DateFieldMapper.Resolution;
 import org.elasticsearch.index.mapper.DateFieldMapper.Resolution;
@@ -338,7 +339,7 @@ public class DateFieldTypeTests extends FieldTypeTestCase {
         SortedNumericIndexFieldData fieldData = new SortedNumericIndexFieldData(
         SortedNumericIndexFieldData fieldData = new SortedNumericIndexFieldData(
             "my_date",
             "my_date",
             IndexNumericFieldData.NumericType.DATE_NANOSECONDS,
             IndexNumericFieldData.NumericType.DATE_NANOSECONDS,
-            (dv, n) -> new DelegateDocValuesField(new Dates(dv, true), n)
+            (dv, n) -> new DelegateDocValuesField(new Dates(new DatesSupplier(dv, true)), n)
         );
         );
         // Read index and check the doc values
         // Read index and check the doc values
         DirectoryReader reader = DirectoryReader.open(w);
         DirectoryReader reader = DirectoryReader.open(w);

+ 2 - 2
server/src/test/java/org/elasticsearch/index/mapper/IpScriptFieldTypeTests.java

@@ -27,7 +27,7 @@ import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.Version;
 import org.elasticsearch.Version;
 import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery;
 import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery;
 import org.elasticsearch.index.fielddata.BinaryScriptFieldData;
 import org.elasticsearch.index.fielddata.BinaryScriptFieldData;
-import org.elasticsearch.index.fielddata.IpScriptFieldData;
+import org.elasticsearch.index.fielddata.ScriptDocValues.Strings;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.script.DocReader;
 import org.elasticsearch.script.DocReader;
@@ -145,7 +145,7 @@ public class IpScriptFieldTypeTests extends AbstractScriptFieldTypeTestCase {
                         return new ScoreScript(Map.of(), searchContext.lookup(), docReader) {
                         return new ScoreScript(Map.of(), searchContext.lookup(), docReader) {
                             @Override
                             @Override
                             public double execute(ExplanationHolder explanation) {
                             public double execute(ExplanationHolder explanation) {
-                                IpScriptFieldData.IpScriptDocValues bytes = (IpScriptFieldData.IpScriptDocValues) getDoc().get("test");
+                                Strings bytes = (Strings) getDoc().get("test");
                                 return Integer.parseInt(bytes.getValue().substring(bytes.getValue().lastIndexOf(".") + 1));
                                 return Integer.parseInt(bytes.getValue().substring(bytes.getValue().lastIndexOf(".") + 1));
                             }
                             }
                         };
                         };

+ 13 - 3
server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java

@@ -500,7 +500,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                         return new LeafFieldData() {
                         return new LeafFieldData() {
                             @Override
                             @Override
                             public DocValuesField<?> getScriptField(String name) {
                             public DocValuesField<?> getScriptField(String name) {
-                                return new DelegateDocValuesField(new ScriptDocValues<String>() {
+                                return new DelegateDocValuesField(new ScriptDocValues<String>(new ScriptDocValues.Supplier<String>() {
                                     String value;
                                     String value;
 
 
                                     @Override
                                     @Override
@@ -509,7 +509,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                                     }
                                     }
 
 
                                     @Override
                                     @Override
-                                    public String get(int index) {
+                                    public String getInternal(int index) {
                                         assert index == 0;
                                         assert index == 0;
                                         return value;
                                         return value;
                                     }
                                     }
@@ -521,6 +521,16 @@ public class SearchExecutionContextTests extends ESTestCase {
                                         leafLookup.setDocument(docId);
                                         leafLookup.setDocument(docId);
                                         value = runtimeDocValues.apply(leafLookup, docId);
                                         value = runtimeDocValues.apply(leafLookup, docId);
                                     }
                                     }
+                                }) {
+                                    @Override
+                                    public int size() {
+                                        return supplier.size();
+                                    }
+
+                                    @Override
+                                    public String get(int i) {
+                                        return supplier.getInternal(i);
+                                    }
                                 }, name);
                                 }, name);
                             }
                             }
 
 
@@ -616,7 +626,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                                     scriptDocValues = indexFieldData.load(context).getScriptField("test").getScriptDocValues();
                                     scriptDocValues = indexFieldData.load(context).getScriptField("test").getScriptDocValues();
                                     ;
                                     ;
                                 }
                                 }
-                                scriptDocValues.setNextDocId(doc);
+                                scriptDocValues.getSupplier().setNextDocId(doc);
                                 for (int i = 0; i < scriptDocValues.size(); i++) {
                                 for (int i = 0; i < scriptDocValues.size(); i++) {
                                     result.add(scriptDocValues.get(i).toString());
                                     result.add(scriptDocValues.get(i).toString());
                                 }
                                 }

+ 2 - 1
server/src/test/java/org/elasticsearch/search/aggregations/bucket/sampler/DiversifiedSamplerTests.java

@@ -25,6 +25,7 @@ import org.elasticsearch.common.lucene.search.function.FieldValueFactorFunction;
 import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
 import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData;
 import org.elasticsearch.index.fielddata.IndexNumericFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.mapper.MappedFieldType;
@@ -167,7 +168,7 @@ public class DiversifiedSamplerTests extends AggregatorTestCase {
         SortedDoublesIndexFieldData fieldData = new SortedDoublesIndexFieldData(
         SortedDoublesIndexFieldData fieldData = new SortedDoublesIndexFieldData(
             "price",
             "price",
             IndexNumericFieldData.NumericType.DOUBLE,
             IndexNumericFieldData.NumericType.DOUBLE,
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         );
         );
         FunctionScoreQuery query = new FunctionScoreQuery(
         FunctionScoreQuery query = new FunctionScoreQuery(
             new MatchAllDocsQuery(),
             new MatchAllDocsQuery(),

+ 9 - 3
server/src/test/java/org/elasticsearch/search/lookup/LeafDocLookupTests.java

@@ -18,6 +18,7 @@ import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 import org.junit.Before;
 import org.junit.Before;
 
 
+import java.io.IOException;
 import java.util.function.Function;
 import java.util.function.Function;
 
 
 import static org.mockito.AdditionalAnswers.returnsFirstArg;
 import static org.mockito.AdditionalAnswers.returnsFirstArg;
@@ -64,7 +65,7 @@ public class LeafDocLookupTests extends ESTestCase {
         assertEquals(docValues, fetchedDocValues);
         assertEquals(docValues, fetchedDocValues);
     }
     }
 
 
-    public void testFlattenedField() {
+    public void testFlattenedField() throws IOException {
         ScriptDocValues<?> docValues1 = mock(ScriptDocValues.class);
         ScriptDocValues<?> docValues1 = mock(ScriptDocValues.class);
         IndexFieldData<?> fieldData1 = createFieldData(docValues1, "flattened.key1");
         IndexFieldData<?> fieldData1 = createFieldData(docValues1, "flattened.key1");
 
 
@@ -95,8 +96,13 @@ public class LeafDocLookupTests extends ESTestCase {
         assertEquals(docValues2, docLookup.get("flattened.key2"));
         assertEquals(docValues2, docLookup.get("flattened.key2"));
     }
     }
 
 
-    private IndexFieldData<?> createFieldData(ScriptDocValues<?> scriptDocValues, String name) {
-        DelegateDocValuesField delegateDocValuesField = new DelegateDocValuesField(scriptDocValues, name);
+    private IndexFieldData<?> createFieldData(ScriptDocValues<?> scriptDocValues, String name) throws IOException {
+        DelegateDocValuesField delegateDocValuesField = new DelegateDocValuesField(scriptDocValues, name) {
+            @Override
+            public void setNextDocId(int id) {
+                // do nothing
+            }
+        };
         LeafFieldData leafFieldData = mock(LeafFieldData.class);
         LeafFieldData leafFieldData = mock(LeafFieldData.class);
         doReturn(delegateDocValuesField).when(leafFieldData).getScriptField(name);
         doReturn(delegateDocValuesField).when(leafFieldData).getScriptField(name);
 
 

+ 5 - 1
x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateDoubleMetricFieldMapper.java

@@ -19,6 +19,7 @@ import org.elasticsearch.common.time.DateMathParser;
 import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.mapper.DocumentParserContext;
 import org.elasticsearch.index.mapper.DocumentParserContext;
@@ -423,7 +424,10 @@ public class AggregateDoubleMetricFieldMapper extends FieldMapper {
                         @Override
                         @Override
                         public DocValuesField<?> getScriptField(String name) {
                         public DocValuesField<?> getScriptField(String name) {
                             // getAggregateMetricValues returns all metric as doubles, including `value_count`
                             // getAggregateMetricValues returns all metric as doubles, including `value_count`
-                            return new DelegateDocValuesField(new ScriptDocValues.Doubles(getAggregateMetricValues(defaultMetric)), name);
+                            return new DelegateDocValuesField(
+                                new ScriptDocValues.Doubles(new DoublesSupplier(getAggregateMetricValues(defaultMetric))),
+                                name
+                            );
                         }
                         }
 
 
                         @Override
                         @Override

+ 26 - 9
x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongDocValuesField.java

@@ -23,7 +23,7 @@ import java.util.PrimitiveIterator;
 import static org.elasticsearch.search.DocValueFormat.MASK_2_63;
 import static org.elasticsearch.search.DocValueFormat.MASK_2_63;
 import static org.elasticsearch.xpack.unsignedlong.UnsignedLongFieldMapper.BIGINTEGER_2_64_MINUS_ONE;
 import static org.elasticsearch.xpack.unsignedlong.UnsignedLongFieldMapper.BIGINTEGER_2_64_MINUS_ONE;
 
 
-public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesField<Long> {
+public class UnsignedLongDocValuesField implements DocValuesField<Long>, ScriptDocValues.Supplier<Long> {
 
 
     private final SortedNumericDocValues input;
     private final SortedNumericDocValues input;
     private final String name;
     private final String name;
@@ -76,6 +76,13 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return count == 0;
         return count == 0;
     }
     }
 
 
+    // this method is required to support the Long return values
+    // for the old-style "doc" access in ScriptDocValues
+    @Override
+    public Long getInternal(int index) {
+        return toFormatted(index);
+    }
+
     @Override
     @Override
     public int size() {
     public int size() {
         return count;
         return count;
@@ -89,7 +96,7 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return values[index] ^ MASK_2_63;
         return values[index] ^ MASK_2_63;
     }
     }
 
 
-    @Override
+    /** Return all the values as a {@code List}. */
     public List<Long> getValues() {
     public List<Long> getValues() {
         if (isEmpty()) {
         if (isEmpty()) {
             return Collections.emptyList();
             return Collections.emptyList();
@@ -104,13 +111,13 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return values;
         return values;
     }
     }
 
 
-    @Override
-    public long getValue(long defaultValue) {
+    /** Returns the 0th index value as an {@code long} if it exists, otherwise {@code defaultValue}. */
+    public long get(long defaultValue) {
         return getValue(0, defaultValue);
         return getValue(0, defaultValue);
     }
     }
 
 
-    @Override
-    public long getValue(int index, long defaultValue) {
+    /** Returns the value at {@code index} as an {@code long} if it exists, otherwise {@code defaultValue}. */
+    public long get(int index, long defaultValue) {
         if (isEmpty() || index < 0 || index >= count) {
         if (isEmpty() || index < 0 || index >= count) {
             return defaultValue;
             return defaultValue;
         }
         }
@@ -118,6 +125,16 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return toFormatted(index);
         return toFormatted(index);
     }
     }
 
 
+    /** Returns the 0th index value as an {@code long} if it exists, otherwise {@code defaultValue}. */
+    public long getValue(long defaultValue) {
+        return get(0, defaultValue);
+    }
+
+    /** Returns the value at {@code index} as an {@code long} if it exists, otherwise {@code defaultValue}. */
+    public long getValue(int index, long defaultValue) {
+        return get(index, defaultValue);
+    }
+
     @Override
     @Override
     public PrimitiveIterator.OfLong iterator() {
     public PrimitiveIterator.OfLong iterator() {
         return new PrimitiveIterator.OfLong() {
         return new PrimitiveIterator.OfLong() {
@@ -148,7 +165,7 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return BigInteger.valueOf(toFormatted(index)).and(BIGINTEGER_2_64_MINUS_ONE);
         return BigInteger.valueOf(toFormatted(index)).and(BIGINTEGER_2_64_MINUS_ONE);
     }
     }
 
 
-    @Override
+    /** Converts all the values to {@code BigInteger} and returns them as a {@code List}. */
     public List<BigInteger> asBigIntegers() {
     public List<BigInteger> asBigIntegers() {
         if (isEmpty()) {
         if (isEmpty()) {
             return Collections.emptyList();
             return Collections.emptyList();
@@ -163,12 +180,12 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return values;
         return values;
     }
     }
 
 
-    @Override
+    /** Returns the 0th index value as a {@code BigInteger} if it exists, otherwise {@code defaultValue}. */
     public BigInteger asBigInteger(BigInteger defaultValue) {
     public BigInteger asBigInteger(BigInteger defaultValue) {
         return asBigInteger(0, defaultValue);
         return asBigInteger(0, defaultValue);
     }
     }
 
 
-    @Override
+    /** Returns the value at {@code index} as a {@code BigInteger} if it exists, otherwise {@code defaultValue}. */
     public BigInteger asBigInteger(int index, BigInteger defaultValue) {
     public BigInteger asBigInteger(int index, BigInteger defaultValue) {
         if (isEmpty() || index < 0 || index >= count) {
         if (isEmpty() || index < 0 || index >= count) {
             return defaultValue;
             return defaultValue;

+ 5 - 18
x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java

@@ -9,37 +9,24 @@ package org.elasticsearch.xpack.unsignedlong;
 
 
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 
 
-import java.io.IOException;
-
 public class UnsignedLongScriptDocValues extends ScriptDocValues<Long> {
 public class UnsignedLongScriptDocValues extends ScriptDocValues<Long> {
 
 
-    private final UnsignedLongDocValuesField unsignedLongDocValuesField;
-
-    /**
-     * Standard constructor.
-     */
-    public UnsignedLongScriptDocValues(UnsignedLongDocValuesField unsignedLongDocValuesField) {
-        this.unsignedLongDocValuesField = unsignedLongDocValuesField;
-    }
-
-    @Override
-    public void setNextDocId(int docId) throws IOException {
-        throw new UnsupportedOperationException();
+    public UnsignedLongScriptDocValues(Supplier<Long> supplier) {
+        super(supplier);
     }
     }
 
 
     public long getValue() {
     public long getValue() {
-        throwIfEmpty();
-        return unsignedLongDocValuesField.getValue(0L); // default is ignored
+        return get(0);
     }
     }
 
 
     @Override
     @Override
     public Long get(int index) {
     public Long get(int index) {
         throwIfEmpty();
         throwIfEmpty();
-        return unsignedLongDocValuesField.getValue(0L); // default is ignored
+        return supplier.getInternal(index);
     }
     }
 
 
     @Override
     @Override
     public int size() {
     public int size() {
-        return unsignedLongDocValuesField.size();
+        return supplier.size();
     }
     }
 }
 }

+ 3 - 4
x-pack/plugin/mapper-unsigned-long/src/main/resources/org/elasticsearch/xpack/unsignedlong/org.elasticsearch.xpack.unsignedlong.txt

@@ -11,14 +11,13 @@ class org.elasticsearch.xpack.unsignedlong.UnsignedLongScriptDocValues {
   long getValue()
   long getValue()
 }
 }
 
 
-class org.elasticsearch.xpack.unsignedlong.UnsignedLongField @dynamic_type {
+class org.elasticsearch.xpack.unsignedlong.UnsignedLongDocValuesField @dynamic_type {
+  long get(long)
+  long get(int, long)
   long getValue(long)
   long getValue(long)
   long getValue(int, long)
   long getValue(int, long)
   List getValues()
   List getValues()
   BigInteger asBigInteger(BigInteger)
   BigInteger asBigInteger(BigInteger)
   BigInteger asBigInteger(int, BigInteger)
   BigInteger asBigInteger(int, BigInteger)
   List asBigIntegers()
   List asBigIntegers()
-}
-
-class org.elasticsearch.xpack.unsignedlong.UnsignedLongDocValuesField @dynamic_type {
 }
 }

+ 37 - 20
x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionScriptDocValues.java

@@ -15,23 +15,44 @@ import java.io.IOException;
 
 
 public final class VersionScriptDocValues extends ScriptDocValues<String> {
 public final class VersionScriptDocValues extends ScriptDocValues<String> {
 
 
-    private final SortedSetDocValues in;
-    private long[] ords = new long[0];
-    private int count;
+    public static final class VersionScriptSupplier implements ScriptDocValues.Supplier<String> {
 
 
-    public VersionScriptDocValues(SortedSetDocValues in) {
-        this.in = in;
-    }
+        private final SortedSetDocValues in;
+        private long[] ords = new long[0];
+        private int count;
 
 
-    @Override
-    public void setNextDocId(int docId) throws IOException {
-        count = 0;
-        if (in.advanceExact(docId)) {
-            for (long ord = in.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = in.nextOrd()) {
-                ords = ArrayUtil.grow(ords, count + 1);
-                ords[count++] = ord;
+        public VersionScriptSupplier(SortedSetDocValues in) {
+            this.in = in;
+        }
+
+        @Override
+        public void setNextDocId(int docId) throws IOException {
+            count = 0;
+            if (in.advanceExact(docId)) {
+                for (long ord = in.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = in.nextOrd()) {
+                    ords = ArrayUtil.grow(ords, count + 1);
+                    ords[count++] = ord;
+                }
+            }
+        }
+
+        @Override
+        public String getInternal(int index) {
+            try {
+                return VersionEncoder.decodeVersion(in.lookupOrd(ords[index]));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
             }
             }
         }
         }
+
+        @Override
+        public int size() {
+            return count;
+        }
+    }
+
+    public VersionScriptDocValues(VersionScriptSupplier supplier) {
+        super(supplier);
     }
     }
 
 
     public String getValue() {
     public String getValue() {
@@ -40,20 +61,16 @@ public final class VersionScriptDocValues extends ScriptDocValues<String> {
 
 
     @Override
     @Override
     public String get(int index) {
     public String get(int index) {
-        if (count == 0) {
+        if (supplier.size() == 0) {
             throw new IllegalStateException(
             throw new IllegalStateException(
                 "A document doesn't have a value for a field! " + "Use doc[<field>].size()==0 to check if a document is missing a field!"
                 "A document doesn't have a value for a field! " + "Use doc[<field>].size()==0 to check if a document is missing a field!"
             );
             );
         }
         }
-        try {
-            return VersionEncoder.decodeVersion(in.lookupOrd(ords[index]));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        return supplier.getInternal(index);
     }
     }
 
 
     @Override
     @Override
     public int size() {
     public int size() {
-        return count;
+        return supplier.size();
     }
     }
 }
 }

+ 6 - 1
x-pack/plugin/mapper-version/src/main/java/org/elasticsearch/xpack/versionfield/VersionStringFieldMapper.java

@@ -49,6 +49,7 @@ import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
 import org.elasticsearch.search.lookup.SearchLookup;
 import org.elasticsearch.search.lookup.SearchLookup;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.xcontent.XContentParser;
 import org.elasticsearch.xpack.versionfield.VersionEncoder.EncodedVersion;
 import org.elasticsearch.xpack.versionfield.VersionEncoder.EncodedVersion;
+import org.elasticsearch.xpack.versionfield.VersionScriptDocValues.VersionScriptSupplier;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
@@ -279,7 +280,11 @@ public class VersionStringFieldMapper extends FieldMapper {
 
 
         @Override
         @Override
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
-            return new SortedSetOrdinalsIndexFieldData.Builder(name(), VersionScriptDocValues::new, CoreValuesSourceType.KEYWORD);
+            return new SortedSetOrdinalsIndexFieldData.Builder(
+                name(),
+                dv -> new VersionScriptDocValues(new VersionScriptSupplier(dv)),
+                CoreValuesSourceType.KEYWORD
+            );
         }
         }
 
 
         @Override
         @Override

+ 45 - 10
x-pack/plugin/spatial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/plain/AbstractAtomicGeoShapeShapeFieldData.java

@@ -15,6 +15,7 @@ import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
 import org.elasticsearch.xpack.spatial.index.fielddata.GeoShapeValues;
 import org.elasticsearch.xpack.spatial.index.fielddata.GeoShapeValues;
+import org.elasticsearch.xpack.spatial.index.fielddata.GeoShapeValues.GeoShapeValue;
 import org.elasticsearch.xpack.spatial.index.fielddata.LeafGeoShapeFieldData;
 import org.elasticsearch.xpack.spatial.index.fielddata.LeafGeoShapeFieldData;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -33,7 +34,7 @@ public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoSha
 
 
     @Override
     @Override
     public final DocValuesField<?> getScriptField(String name) {
     public final DocValuesField<?> getScriptField(String name) {
-        return new DelegateDocValuesField(new GeoShapeScriptValues(getGeoShapeValues()), name);
+        return new DelegateDocValuesField(new GeoShapeScriptValues(new GeoShapeSupplier(getGeoShapeValues())), name);
     }
     }
 
 
     public static LeafGeoShapeFieldData empty(final int maxDoc) {
     public static LeafGeoShapeFieldData empty(final int maxDoc) {
@@ -59,14 +60,14 @@ public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoSha
         };
         };
     }
     }
 
 
-    private static final class GeoShapeScriptValues extends ScriptDocValues.Geometry<GeoShapeValues.GeoShapeValue> {
+    private static final class GeoShapeSupplier implements ScriptDocValues.GeometrySupplier<GeoShapeValue> {
 
 
         private final GeoShapeValues in;
         private final GeoShapeValues in;
         private final GeoPoint centroid = new GeoPoint();
         private final GeoPoint centroid = new GeoPoint();
         private final GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
         private final GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
         private GeoShapeValues.GeoShapeValue value;
         private GeoShapeValues.GeoShapeValue value;
 
 
-        private GeoShapeScriptValues(GeoShapeValues in) {
+        private GeoShapeSupplier(GeoShapeValues in) {
             this.in = in;
             this.in = in;
         }
         }
 
 
@@ -82,39 +83,73 @@ public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoSha
             }
             }
         }
         }
 
 
+        @Override
+        public GeoShapeValue getInternal(int index) {
+            throw new UnsupportedOperationException();
+        }
+
+        public GeoShapeValue getInternal() {
+            return value;
+        }
+
+        @Override
+        public int size() {
+            return value == null ? 0 : 1;
+        }
+
+        @Override
+        public GeoPoint getCentroid() {
+            return centroid;
+        }
+
+        @Override
+        public GeoBoundingBox getBoundingBox() {
+            return boundingBox;
+        }
+    }
+
+    private static final class GeoShapeScriptValues extends ScriptDocValues.Geometry<GeoShapeValue> {
+
+        private final GeoShapeSupplier gsSupplier;
+
+        private GeoShapeScriptValues(GeoShapeSupplier supplier) {
+            super(supplier);
+            this.gsSupplier = supplier;
+        }
+
         @Override
         @Override
         public int getDimensionalType() {
         public int getDimensionalType() {
-            return value == null ? -1 : value.dimensionalShapeType().ordinal();
+            return gsSupplier.getInternal() == null ? -1 : gsSupplier.getInternal().dimensionalShapeType().ordinal();
         }
         }
 
 
         @Override
         @Override
         public GeoPoint getCentroid() {
         public GeoPoint getCentroid() {
-            return value == null ? null : centroid;
+            return gsSupplier.getInternal() == null ? null : gsSupplier.getCentroid();
         }
         }
 
 
         @Override
         @Override
         public double getMercatorWidth() {
         public double getMercatorWidth() {
-            return lonToSphericalMercator(boundingBox.right()) - lonToSphericalMercator(boundingBox.left());
+            return lonToSphericalMercator(getBoundingBox().right()) - lonToSphericalMercator(getBoundingBox().left());
         }
         }
 
 
         @Override
         @Override
         public double getMercatorHeight() {
         public double getMercatorHeight() {
-            return latToSphericalMercator(boundingBox.top()) - latToSphericalMercator(boundingBox.bottom());
+            return latToSphericalMercator(getBoundingBox().top()) - latToSphericalMercator(getBoundingBox().bottom());
         }
         }
 
 
         @Override
         @Override
         public GeoBoundingBox getBoundingBox() {
         public GeoBoundingBox getBoundingBox() {
-            return value == null ? null : boundingBox;
+            return gsSupplier.getInternal() == null ? null : gsSupplier.getBoundingBox();
         }
         }
 
 
         @Override
         @Override
         public GeoShapeValues.GeoShapeValue get(int index) {
         public GeoShapeValues.GeoShapeValue get(int index) {
-            return value;
+            return gsSupplier.getInternal();
         }
         }
 
 
         @Override
         @Override
         public int size() {
         public int size() {
-            return value == null ? 0 : 1;
+            return supplier.size();
         }
         }
     }
     }
 }
 }

+ 48 - 22
x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/BinaryDenseVectorScriptDocValues.java

@@ -17,49 +17,73 @@ import java.nio.ByteBuffer;
 
 
 public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
 public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
 
 
-    private final BinaryDocValues in;
+    public static class BinaryDenseVectorSupplier implements DenseVectorSupplier<BytesRef> {
+
+        private final BinaryDocValues in;
+        private BytesRef value;
+
+        public BinaryDenseVectorSupplier(BinaryDocValues in) {
+            this.in = in;
+        }
+
+        @Override
+        public void setNextDocId(int docId) throws IOException {
+            if (in.advanceExact(docId)) {
+                value = in.binaryValue();
+            } else {
+                value = null;
+            }
+        }
+
+        @Override
+        public BytesRef getInternal(int index) {
+            throw new UnsupportedOperationException();
+        }
+
+        public BytesRef getInternal() {
+            return value;
+        }
+
+        @Override
+        public int size() {
+            if (value == null) {
+                return 0;
+            } else {
+                return 1;
+            }
+        }
+    }
+
+    private final BinaryDenseVectorSupplier bdvSupplier;
     private final Version indexVersion;
     private final Version indexVersion;
     private final float[] vector;
     private final float[] vector;
-    private BytesRef value;
 
 
-    BinaryDenseVectorScriptDocValues(BinaryDocValues in, Version indexVersion, int dims) {
-        super(dims);
-        this.in = in;
+    BinaryDenseVectorScriptDocValues(BinaryDenseVectorSupplier supplier, Version indexVersion, int dims) {
+        super(supplier, dims);
+        this.bdvSupplier = supplier;
         this.indexVersion = indexVersion;
         this.indexVersion = indexVersion;
         this.vector = new float[dims];
         this.vector = new float[dims];
     }
     }
 
 
     @Override
     @Override
-    public void setNextDocId(int docId) throws IOException {
-        if (in.advanceExact(docId)) {
-            value = in.binaryValue();
-        } else {
-            value = null;
-        }
+    public int size() {
+        return supplier.size();
     }
     }
 
 
     @Override
     @Override
     public float[] getVectorValue() {
     public float[] getVectorValue() {
-        VectorEncoderDecoder.decodeDenseVector(value, vector);
+        VectorEncoderDecoder.decodeDenseVector(bdvSupplier.getInternal(), vector);
         return vector;
         return vector;
     }
     }
 
 
     @Override
     @Override
     public float getMagnitude() {
     public float getMagnitude() {
-        return VectorEncoderDecoder.getMagnitude(indexVersion, value);
-    }
-
-    @Override
-    public int size() {
-        if (value == null) {
-            return 0;
-        } else {
-            return 1;
-        }
+        return VectorEncoderDecoder.getMagnitude(indexVersion, bdvSupplier.getInternal());
     }
     }
 
 
     @Override
     @Override
     public double dotProduct(float[] queryVector) {
     public double dotProduct(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
 
 
         double dotProduct = 0;
         double dotProduct = 0;
@@ -71,6 +95,7 @@ public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues
 
 
     @Override
     @Override
     public double l1Norm(float[] queryVector) {
     public double l1Norm(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
 
 
         double l1norm = 0;
         double l1norm = 0;
@@ -82,6 +107,7 @@ public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues
 
 
     @Override
     @Override
     public double l2Norm(float[] queryVector) {
     public double l2Norm(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
         double l2norm = 0;
         double l2norm = 0;
         for (float queryValue : queryVector) {
         for (float queryValue : queryVector) {

+ 16 - 9
x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/DenseVectorScriptDocValues.java

@@ -11,11 +11,23 @@ import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
 
 
 public abstract class DenseVectorScriptDocValues extends ScriptDocValues<BytesRef> {
 public abstract class DenseVectorScriptDocValues extends ScriptDocValues<BytesRef> {
+
+    public interface DenseVectorSupplier<T> extends Supplier<BytesRef> {
+
+        @Override
+        default BytesRef getInternal(int index) {
+            throw new UnsupportedOperationException();
+        }
+
+        T getInternal();
+    }
+
     public static final String MISSING_VECTOR_FIELD_MESSAGE = "A document doesn't have a value for a vector field!";
     public static final String MISSING_VECTOR_FIELD_MESSAGE = "A document doesn't have a value for a vector field!";
 
 
     private final int dims;
     private final int dims;
 
 
-    public DenseVectorScriptDocValues(int dims) {
+    public DenseVectorScriptDocValues(DenseVectorSupplier<?> supplier, int dims) {
+        super(supplier);
         this.dims = dims;
         this.dims = dims;
     }
     }
 
 
@@ -46,8 +58,8 @@ public abstract class DenseVectorScriptDocValues extends ScriptDocValues<BytesRe
         );
         );
     }
     }
 
 
-    public static DenseVectorScriptDocValues empty(int dims) {
-        return new DenseVectorScriptDocValues(dims) {
+    public static DenseVectorScriptDocValues empty(DenseVectorSupplier<?> supplier, int dims) {
+        return new DenseVectorScriptDocValues(supplier, dims) {
             @Override
             @Override
             public float[] getVectorValue() {
             public float[] getVectorValue() {
                 throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
                 throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
@@ -73,14 +85,9 @@ public abstract class DenseVectorScriptDocValues extends ScriptDocValues<BytesRe
                 throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
                 throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
             }
             }
 
 
-            @Override
-            public void setNextDocId(int docId) {
-                // do nothing
-            }
-
             @Override
             @Override
             public int size() {
             public int size() {
-                return 0;
+                return supplier.size();
             }
             }
         };
         };
     }
     }

+ 49 - 24
x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/KnnDenseVectorScriptDocValues.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.xpack.vectors.query;
 package org.elasticsearch.xpack.vectors.query;
 
 
 import org.apache.lucene.index.VectorValues;
 import org.apache.lucene.index.VectorValues;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.VectorUtil;
 import org.apache.lucene.util.VectorUtil;
 
 
 import java.io.IOException;
 import java.io.IOException;
@@ -16,36 +17,64 @@ import java.util.Arrays;
 import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS;
 import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS;
 
 
 public class KnnDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
 public class KnnDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
-    private final VectorValues in;
-    private float[] vector;
 
 
-    KnnDenseVectorScriptDocValues(VectorValues in, int dims) {
-        super(dims);
-        this.in = in;
-    }
+    public static class KnnDenseVectorSupplier implements DenseVectorSupplier<float[]> {
 
 
-    @Override
-    public void setNextDocId(int docId) throws IOException {
-        int currentDoc = in.docID();
-        if (currentDoc == NO_MORE_DOCS || docId < currentDoc) {
-            vector = null;
-        } else if (docId == currentDoc) {
-            vector = in.vectorValue();
-        } else {
-            currentDoc = in.advance(docId);
-            if (currentDoc == docId) {
+        private final VectorValues in;
+        private float[] vector;
+
+        public KnnDenseVectorSupplier(VectorValues in) {
+            this.in = in;
+        }
+
+        @Override
+        public void setNextDocId(int docId) throws IOException {
+            int currentDoc = in.docID();
+            if (currentDoc == NO_MORE_DOCS || docId < currentDoc) {
+                vector = null;
+            } else if (docId == currentDoc) {
                 vector = in.vectorValue();
                 vector = in.vectorValue();
             } else {
             } else {
-                vector = null;
+                currentDoc = in.advance(docId);
+                if (currentDoc == docId) {
+                    vector = in.vectorValue();
+                } else {
+                    vector = null;
+                }
+            }
+        }
+
+        @Override
+        public BytesRef getInternal(int index) {
+            throw new UnsupportedOperationException();
+        }
+
+        public float[] getInternal() {
+            return vector;
+        }
+
+        @Override
+        public int size() {
+            if (vector == null) {
+                return 0;
+            } else {
+                return 1;
             }
             }
         }
         }
     }
     }
 
 
+    private final KnnDenseVectorSupplier kdvSupplier;
+
+    KnnDenseVectorScriptDocValues(KnnDenseVectorSupplier supplier, int dims) {
+        super(supplier, dims);
+        this.kdvSupplier = supplier;
+    }
+
     private float[] getVectorChecked() {
     private float[] getVectorChecked() {
-        if (vector == null) {
+        if (kdvSupplier.getInternal() == null) {
             throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
             throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
         }
         }
-        return vector;
+        return kdvSupplier.getInternal();
     }
     }
 
 
     @Override
     @Override
@@ -88,10 +117,6 @@ public class KnnDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
 
 
     @Override
     @Override
     public int size() {
     public int size() {
-        if (vector == null) {
-            return 0;
-        } else {
-            return 1;
-        }
+        return supplier.size();
     }
     }
 }
 }

+ 1 - 1
x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/ScoreScriptUtils.java

@@ -63,7 +63,7 @@ public class ScoreScriptUtils {
 
 
         void setNextVector() {
         void setNextVector() {
             try {
             try {
-                docValues.setNextDocId(scoreScript._getDocId());
+                docValues.getSupplier().setNextDocId(scoreScript._getDocId());
             } catch (IOException e) {
             } catch (IOException e) {
                 throw ExceptionsHelper.convertToElastic(e);
                 throw ExceptionsHelper.convertToElastic(e);
             }
             }

+ 26 - 3
x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/query/VectorDVLeafFieldData.java

@@ -17,11 +17,16 @@ import org.elasticsearch.index.fielddata.LeafFieldData;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
+import org.elasticsearch.xpack.vectors.query.BinaryDenseVectorScriptDocValues.BinaryDenseVectorSupplier;
+import org.elasticsearch.xpack.vectors.query.DenseVectorScriptDocValues.DenseVectorSupplier;
+import org.elasticsearch.xpack.vectors.query.KnnDenseVectorScriptDocValues.KnnDenseVectorSupplier;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 
 
+import static org.elasticsearch.xpack.vectors.query.DenseVectorScriptDocValues.MISSING_VECTOR_FIELD_MESSAGE;
+
 final class VectorDVLeafFieldData implements LeafFieldData {
 final class VectorDVLeafFieldData implements LeafFieldData {
 
 
     private final LeafReader reader;
     private final LeafReader reader;
@@ -59,12 +64,30 @@ final class VectorDVLeafFieldData implements LeafFieldData {
             if (indexed) {
             if (indexed) {
                 VectorValues values = reader.getVectorValues(field);
                 VectorValues values = reader.getVectorValues(field);
                 if (values == null || values == VectorValues.EMPTY) {
                 if (values == null || values == VectorValues.EMPTY) {
-                    return new DelegateDocValuesField(DenseVectorScriptDocValues.empty(dims), name);
+                    return new DelegateDocValuesField(DenseVectorScriptDocValues.empty(new DenseVectorSupplier<float[]>() {
+                        @Override
+                        public float[] getInternal() {
+                            throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
+                        }
+
+                        @Override
+                        public void setNextDocId(int docId) throws IOException {
+                            // do nothing
+                        }
+
+                        @Override
+                        public int size() {
+                            return 0;
+                        }
+                    }, dims), name);
                 }
                 }
-                return new DelegateDocValuesField(new KnnDenseVectorScriptDocValues(values, dims), name);
+                return new DelegateDocValuesField(new KnnDenseVectorScriptDocValues(new KnnDenseVectorSupplier(values), dims), name);
             } else {
             } else {
                 BinaryDocValues values = DocValues.getBinary(reader, field);
                 BinaryDocValues values = DocValues.getBinary(reader, field);
-                return new DelegateDocValuesField(new BinaryDenseVectorScriptDocValues(values, indexVersion, dims), name);
+                return new DelegateDocValuesField(
+                    new BinaryDenseVectorScriptDocValues(new BinaryDenseVectorSupplier(values), indexVersion, dims),
+                    name
+                );
             }
             }
         } catch (IOException e) {
         } catch (IOException e) {
             throw new IllegalStateException("Cannot load doc values for vector field!", e);
             throw new IllegalStateException("Cannot load doc values for vector field!", e);

+ 15 - 10
x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/BinaryDenseVectorScriptDocValuesTests.java

@@ -12,6 +12,7 @@ import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.Version;
 import org.elasticsearch.Version;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.xpack.vectors.mapper.VectorEncoderDecoder;
 import org.elasticsearch.xpack.vectors.mapper.VectorEncoderDecoder;
+import org.elasticsearch.xpack.vectors.query.BinaryDenseVectorScriptDocValues.BinaryDenseVectorSupplier;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
@@ -28,9 +29,10 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
 
 
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
             BinaryDocValues docValues = wrap(vectors, indexVersion);
             BinaryDocValues docValues = wrap(vectors, indexVersion);
-            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(docValues, indexVersion, dims);
+            BinaryDenseVectorSupplier supplier = new BinaryDenseVectorSupplier(docValues);
+            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(supplier, indexVersion, dims);
             for (int i = 0; i < vectors.length; i++) {
             for (int i = 0; i < vectors.length; i++) {
-                scriptDocValues.setNextDocId(i);
+                supplier.setNextDocId(i);
                 assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
                 assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
                 assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
                 assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
             }
             }
@@ -41,13 +43,14 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
         int dims = 3;
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         BinaryDocValues docValues = wrap(vectors, Version.CURRENT);
         BinaryDocValues docValues = wrap(vectors, Version.CURRENT);
-        DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(docValues, Version.CURRENT, dims);
+        BinaryDenseVectorSupplier supplier = new BinaryDenseVectorSupplier(docValues);
+        DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(supplier, Version.CURRENT, dims);
 
 
-        scriptDocValues.setNextDocId(3);
-        Exception e = expectThrows(IllegalArgumentException.class, () -> scriptDocValues.getVectorValue());
+        supplier.setNextDocId(3);
+        Exception e = expectThrows(IllegalArgumentException.class, scriptDocValues::getVectorValue);
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
 
 
-        e = expectThrows(IllegalArgumentException.class, () -> scriptDocValues.getMagnitude());
+        e = expectThrows(IllegalArgumentException.class, scriptDocValues::getMagnitude);
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
     }
     }
 
 
@@ -55,9 +58,10 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
         int dims = 3;
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         BinaryDocValues docValues = wrap(vectors, Version.CURRENT);
         BinaryDocValues docValues = wrap(vectors, Version.CURRENT);
-        DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(docValues, Version.CURRENT, dims);
+        BinaryDenseVectorSupplier supplier = new BinaryDenseVectorSupplier(docValues);
+        DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(supplier, Version.CURRENT, dims);
 
 
-        scriptDocValues.setNextDocId(0);
+        supplier.setNextDocId(0);
         Exception e = expectThrows(UnsupportedOperationException.class, () -> scriptDocValues.get(0));
         Exception e = expectThrows(UnsupportedOperationException.class, () -> scriptDocValues.get(0));
         assertThat(e.getMessage(), containsString("accessing a vector field's value through 'get' or 'value' is not supported!"));
         assertThat(e.getMessage(), containsString("accessing a vector field's value through 'get' or 'value' is not supported!"));
     }
     }
@@ -69,9 +73,10 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
 
 
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
             BinaryDocValues docValues = wrap(new float[][] { docVector }, indexVersion);
             BinaryDocValues docValues = wrap(new float[][] { docVector }, indexVersion);
-            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(docValues, Version.CURRENT, dims);
+            BinaryDenseVectorSupplier supplier = new BinaryDenseVectorSupplier(docValues);
+            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(supplier, Version.CURRENT, dims);
 
 
-            scriptDocValues.setNextDocId(0);
+            supplier.setNextDocId(0);
 
 
             assertEquals(
             assertEquals(
                 "dotProduct result is not equal to the expected value!",
                 "dotProduct result is not equal to the expected value!",

+ 6 - 1
x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/DenseVectorFunctionTests.java

@@ -11,6 +11,7 @@ import org.apache.lucene.index.BinaryDocValues;
 import org.elasticsearch.Version;
 import org.elasticsearch.Version;
 import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.script.ScoreScript;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.vectors.query.BinaryDenseVectorScriptDocValues.BinaryDenseVectorSupplier;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.CosineSimilarity;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.CosineSimilarity;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.DotProduct;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.DotProduct;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.L1Norm;
 import org.elasticsearch.xpack.vectors.query.ScoreScriptUtils.L1Norm;
@@ -36,7 +37,11 @@ public class DenseVectorFunctionTests extends ESTestCase {
 
 
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
         for (Version indexVersion : Arrays.asList(Version.V_7_4_0, Version.CURRENT)) {
             BinaryDocValues docValues = BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, indexVersion);
             BinaryDocValues docValues = BinaryDenseVectorScriptDocValuesTests.wrap(new float[][] { docVector }, indexVersion);
-            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(docValues, indexVersion, dims);
+            DenseVectorScriptDocValues scriptDocValues = new BinaryDenseVectorScriptDocValues(
+                new BinaryDenseVectorSupplier(docValues),
+                indexVersion,
+                dims
+            );
 
 
             ScoreScript scoreScript = mock(ScoreScript.class);
             ScoreScript scoreScript = mock(ScoreScript.class);
             when(scoreScript.getDoc()).thenReturn(Collections.singletonMap(field, scriptDocValues));
             when(scoreScript.getDoc()).thenReturn(Collections.singletonMap(field, scriptDocValues));

+ 13 - 8
x-pack/plugin/vectors/src/test/java/org/elasticsearch/xpack/vectors/query/KnnDenseVectorScriptDocValuesTests.java

@@ -10,6 +10,7 @@ package org.elasticsearch.xpack.vectors.query;
 import org.apache.lucene.index.VectorValues;
 import org.apache.lucene.index.VectorValues;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.vectors.query.KnnDenseVectorScriptDocValues.KnnDenseVectorSupplier;
 
 
 import java.io.IOException;
 import java.io.IOException;
 
 
@@ -22,9 +23,10 @@ public class KnnDenseVectorScriptDocValuesTests extends ESTestCase {
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[] expectedMagnitudes = { 1.7320f, 2.4495f, 3.3166f };
         float[] expectedMagnitudes = { 1.7320f, 2.4495f, 3.3166f };
 
 
-        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(wrap(vectors), dims);
+        KnnDenseVectorSupplier supplier = new KnnDenseVectorSupplier(wrap(vectors));
+        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(supplier, dims);
         for (int i = 0; i < vectors.length; i++) {
         for (int i = 0; i < vectors.length; i++) {
-            scriptDocValues.setNextDocId(i);
+            supplier.setNextDocId(i);
             assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
             assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
             assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
             assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
         }
         }
@@ -33,9 +35,10 @@ public class KnnDenseVectorScriptDocValuesTests extends ESTestCase {
     public void testMissingValues() throws IOException {
     public void testMissingValues() throws IOException {
         int dims = 3;
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
-        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(wrap(vectors), dims);
+        KnnDenseVectorSupplier supplier = new KnnDenseVectorSupplier(wrap(vectors));
+        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(supplier, dims);
 
 
-        scriptDocValues.setNextDocId(3);
+        supplier.setNextDocId(3);
         Exception e = expectThrows(IllegalArgumentException.class, () -> scriptDocValues.getVectorValue());
         Exception e = expectThrows(IllegalArgumentException.class, () -> scriptDocValues.getVectorValue());
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
         assertEquals("A document doesn't have a value for a vector field!", e.getMessage());
 
 
@@ -46,9 +49,10 @@ public class KnnDenseVectorScriptDocValuesTests extends ESTestCase {
     public void testGetFunctionIsNotAccessible() throws IOException {
     public void testGetFunctionIsNotAccessible() throws IOException {
         int dims = 3;
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
-        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(wrap(vectors), dims);
+        KnnDenseVectorSupplier supplier = new KnnDenseVectorSupplier(wrap(vectors));
+        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(supplier, dims);
 
 
-        scriptDocValues.setNextDocId(0);
+        supplier.setNextDocId(0);
         Exception e = expectThrows(UnsupportedOperationException.class, () -> scriptDocValues.get(0));
         Exception e = expectThrows(UnsupportedOperationException.class, () -> scriptDocValues.get(0));
         assertThat(e.getMessage(), containsString("accessing a vector field's value through 'get' or 'value' is not supported!"));
         assertThat(e.getMessage(), containsString("accessing a vector field's value through 'get' or 'value' is not supported!"));
     }
     }
@@ -58,8 +62,9 @@ public class KnnDenseVectorScriptDocValuesTests extends ESTestCase {
         float[] docVector = new float[] { 230.0f, 300.33f, -34.8988f, 15.555f, -200.0f };
         float[] docVector = new float[] { 230.0f, 300.33f, -34.8988f, 15.555f, -200.0f };
         float[] queryVector = new float[] { 0.5f, 111.3f, -13.0f, 14.8f, -156.0f };
         float[] queryVector = new float[] { 0.5f, 111.3f, -13.0f, 14.8f, -156.0f };
 
 
-        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(wrap(new float[][] { docVector }), dims);
-        scriptDocValues.setNextDocId(0);
+        KnnDenseVectorSupplier supplier = new KnnDenseVectorSupplier(wrap(new float[][] { docVector }));
+        DenseVectorScriptDocValues scriptDocValues = new KnnDenseVectorScriptDocValues(supplier, dims);
+        supplier.setNextDocId(0);
 
 
         assertEquals("dotProduct result is not equal to the expected value!", 65425.624, scriptDocValues.dotProduct(queryVector), 0.001);
         assertEquals("dotProduct result is not equal to the expected value!", 65425.624, scriptDocValues.dotProduct(queryVector), 0.001);
         assertEquals("l1norm result is not equal to the expected value!", 485.184, scriptDocValues.l1Norm(queryVector), 0.001);
         assertEquals("l1norm result is not equal to the expected value!", 485.184, scriptDocValues.l1Norm(queryVector), 0.001);