1
0
Эх сурвалжийг харах

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 жил өмнө
parent
commit
1adb59c041
44 өөрчлөгдсөн 666 нэмэгдсэн , 357 устгасан
  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.NumericDoubleValues;
 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.SortedNumericDoubleValues;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
@@ -269,7 +270,7 @@ public class ScaledFloatFieldMapper extends FieldMapper {
                 return new ScaledFloatIndexFieldData(
                     scaledValues,
                     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.IndexNumericFieldData.NumericType;
 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.mapper.DocumentParserContext;
 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...
     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) {
             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.BytesReference;
 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.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.script.IpFieldScript;
@@ -53,7 +55,7 @@ public class IpScriptFieldData extends BinaryScriptFieldData {
         return new BinaryScriptLeafFieldData() {
             @Override
             public DocValuesField<?> getScriptField(String name) {
-                return new DelegateDocValuesField(new IpScriptDocValues(getBytesValues()), name);
+                return new DelegateDocValuesField(new Strings(new IpSupplier(getBytesValues())), name);
             }
 
             @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
      * 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);
         }
 
         @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);
         }
     }

+ 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.time.DateUtils;
 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.time.Instant;
@@ -39,9 +38,30 @@ import java.util.function.UnaryOperator;
 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.
     @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 long[] values = new long[0];
         private int count;
 
-        /**
-         * Standard constructor.
-         */
-        public Longs(SortedNumericDocValues in) {
+        public LongsSupplier(SortedNumericDocValues 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
          * store at least that many entries.
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             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() {
             return get(0);
         }
@@ -117,16 +152,16 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         public Long get(int index) {
             throwIfEmpty();
-            return values[index];
+            return supplier.getInternal(index);
         }
 
         @Override
         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 boolean isNanos;
@@ -137,32 +172,13 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         private ZonedDateTime[] dates;
         private int count;
 
-        public Dates(SortedNumericDocValues in, boolean isNanos) {
+        public DatesSupplier(SortedNumericDocValues in, boolean isNanos) {
             this.in = in;
             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
-        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];
         }
 
@@ -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.
          */
-        void refreshArray() throws IOException {
+        private void refreshArray() throws IOException {
             if (count == 0) {
                 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 double[] values = new double[0];
         private int count;
 
-        public Doubles(SortedNumericDoubleValues in) {
+        public DoublesSupplier(SortedNumericDoubleValues 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
          * store at least that many entries.
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             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() {
@@ -243,22 +308,27 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
         @Override
         public Double get(int index) {
-            if (count == 0) {
+            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!"
                 );
             }
-            return values[index];
+            return supplier.getInternal(index);
         }
 
         @Override
         public int size() {
-            return count;
+            return supplier.size();
         }
     }
 
     public abstract static class Geometry<T> extends ScriptDocValues<T> {
+
+        public Geometry(Supplier<T> supplier) {
+            super(supplier);
+        }
+
         /** Returns the dimensional type of this geometry */
         public abstract int getDimensionalType();
 
@@ -275,7 +345,14 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         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 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 int count;
 
-        public GeoPoints(MultiGeoPointValues in) {
+        public GeoPointsSupplier(MultiGeoPointValues 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
          * store at least that many entries.
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             if (newSize > 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() {
             return get(0);
         }
@@ -376,19 +483,19 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
         @Override
         public GeoPoint get(int index) {
-            if (count == 0) {
+            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!"
                 );
             }
-            final GeoPoint point = values[index];
+            final GeoPoint point = supplier.getInternal(index);
             return new GeoPoint(point.lat(), point.lon());
         }
 
         @Override
         public int size() {
-            return count;
+            return supplier.size();
         }
 
         public double arcDistance(double lat, double lon) {
@@ -434,7 +541,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
         @Override
         public GeoPoint getCentroid() {
-            return size() == 0 ? null : centroid;
+            return size() == 0 ? null : geometrySupplier.getCentroid();
         }
 
         @Override
@@ -449,21 +556,14 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
 
         @Override
         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() {
@@ -474,22 +574,22 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         public Boolean get(int index) {
             throwIfEmpty();
-            return booleanDocValuesField.getInternal(index);
+            return supplier.getInternal(index);
         }
 
         @Override
         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;
-        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;
         }
 
@@ -512,7 +612,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
          * Set the {@link #size()} and ensure that the {@link #values} array can
          * store at least that many entries.
          */
-        protected void resize(int newSize) {
+        private void resize(int newSize) {
             count = newSize;
             if (newSize > 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
         public int size() {
             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
-        public final String get(int index) {
-            if (count == 0) {
+        public String 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!"
                 );
             }
-            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> {
 
-        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() {
@@ -578,12 +679,12 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
         @Override
         public BytesRef get(int index) {
             throwIfEmpty();
-            return binaryDocValuesField.getInternal(index);
+            return supplier.getInternal(index);
         }
 
         @Override
         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;
 
 import org.apache.lucene.index.LeafReaderContext;
+import org.elasticsearch.index.fielddata.ScriptDocValues.StringsSupplier;
 import org.elasticsearch.indices.breaker.CircuitBreakerService;
 import org.elasticsearch.script.StringFieldScript;
 import org.elasticsearch.script.field.DelegateDocValuesField;
@@ -45,7 +46,7 @@ public class StringScriptFieldData extends BinaryScriptFieldData {
         return new BinaryScriptLeafFieldData() {
             @Override
             public DocValuesField<?> getScriptField(String name) {
-                return new DelegateDocValuesField(new ScriptDocValues.Strings(getBytesValues()), name);
+                return new DelegateDocValuesField(new ScriptDocValues.Strings(new StringsSupplier(getBytesValues())), name);
             }
 
             @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.MultiGeoPointValues;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
+import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPointsSupplier;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.script.field.DelegateDocValuesField;
 import org.elasticsearch.script.field.DocValuesField;
@@ -28,7 +29,7 @@ public abstract class AbstractLeafGeoPointFieldData implements LeafGeoPointField
 
     @Override
     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) {

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

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

@@ -46,7 +46,7 @@ public class BinaryDVLeafFieldData implements LeafFieldData {
 
     @Override
     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

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

@@ -20,6 +20,6 @@ final class StringBinaryDVLeafFieldData extends AbstractBinaryDVLeafFieldData {
 
     @Override
     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.IndexNumericFieldData.NumericType;
 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.query.DateRangeIncludingNowQuery;
 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();
 
     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
             public long convert(Instant instant) {
                 return instant.toEpochMilli();
@@ -111,7 +112,11 @@ public final class DateFieldMapper extends FieldMapper {
                 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
             public long convert(Instant 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.index.fielddata.DoubleScriptFieldData;
 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.query.SearchExecutionContext;
 import org.elasticsearch.script.CompositeFieldScript;
@@ -103,7 +104,7 @@ public final class DoubleScriptFieldType extends AbstractScriptFieldType<DoubleF
         return new DoubleScriptFieldData.Builder(
             name(),
             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
             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

+ 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.ScriptDocValues;
 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.script.IpFieldScript;
 import org.elasticsearch.script.Script;
@@ -351,27 +352,52 @@ public class IpFieldMapper extends FieldMapper {
 
         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() {
-                if (count == 0) {
+                if (supplier.size() == 0) {
                     return null;
                 } else {
                     return get(0);
@@ -380,27 +406,23 @@ public class IpFieldMapper extends FieldMapper {
 
             @Override
             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
             public int size() {
-                return count;
+                return supplier.size();
             }
         }
 
         @Override
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
             failIfNoDocValues();
-            return new SortedSetOrdinalsIndexFieldData.Builder(name(), IpScriptDocValues::new, CoreValuesSourceType.IP);
+            return new SortedSetOrdinalsIndexFieldData.Builder(
+                name(),
+                s -> new IpScriptDocValues(new IpSupplier(s)),
+                CoreValuesSourceType.IP
+            );
         }
 
         @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.index.fielddata.LongScriptFieldData;
 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.query.SearchExecutionContext;
 import org.elasticsearch.script.CompositeFieldScript;
@@ -98,7 +99,7 @@ public final class LongScriptFieldType extends AbstractScriptFieldType<LongField
         return new LongScriptFieldData.Builder(
             name(),
             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.IndexNumericFieldData.NumericType;
 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.LongsSupplier;
 import org.elasticsearch.index.fielddata.plain.SortedDoublesIndexFieldData;
 import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
 import org.elasticsearch.index.mapper.TimeSeriesParams.MetricType;
@@ -335,7 +337,7 @@ public class NumberFieldMapper extends FieldMapper {
                 return new SortedDoublesIndexFieldData.Builder(
                     name,
                     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(
                     name,
                     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(
                     name,
                     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(
                     name,
                     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(
                     name,
                     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(
                     name,
                     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(
                     name,
                     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.IndexNumericFieldData.NumericType;
 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.query.SearchExecutionContext;
 import org.elasticsearch.index.seqno.SequenceNumbers;
@@ -188,7 +189,7 @@ public class SeqNoFieldMapper extends MetadataFieldMapper {
             return new SortedNumericIndexFieldData.Builder(
                 name(),
                 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.IndexNumericFieldData.NumericType;
 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.query.QueryShardException;
 import org.elasticsearch.index.query.SearchExecutionContext;
@@ -62,7 +63,7 @@ public class VersionFieldMapper extends MetadataFieldMapper {
             return new SortedNumericIndexFieldData.Builder(
                 name(),
                 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() {
             try {
-                docValues.setNextDocId(scoreScript._getDocId());
+                docValues.getSupplier().setNextDocId(scoreScript._getDocId());
                 String seedValue = String.valueOf(docValues.get(0));
                 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

+ 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.NoSuchElementException;
 
-public class BinaryDocValuesField implements DocValuesField<ByteBuffer> {
+public class BinaryDocValuesField implements DocValuesField<ByteBuffer>, ScriptDocValues.Supplier<BytesRef> {
 
     private final SortedBinaryDocValues input;
     private final String name;
@@ -74,8 +74,9 @@ public class BinaryDocValuesField implements DocValuesField<ByteBuffer> {
         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
+    @Override
     public BytesRef getInternal(int index) {
         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.NoSuchElementException;
 
-public class BooleanDocValuesField implements DocValuesField<Boolean> {
+public class BooleanDocValuesField implements DocValuesField<Boolean>, ScriptDocValues.Supplier<Boolean> {
 
     private final SortedNumericDocValues input;
     private final String name;
@@ -25,6 +25,8 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
     private boolean[] values = new boolean[0];
     private int count;
 
+    // used for backwards compatibility for old-style "doc" access
+    // as a delegate to this field class
     private ScriptDocValues.Booleans booleans = null;
 
     public BooleanDocValuesField(SortedNumericDocValues input, String name) {
@@ -32,11 +34,6 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
         this.name = name;
     }
 
-    /**
-     * Set the current document ID.
-     *
-     * @param docId
-     */
     @Override
     public void setNextDocId(int docId) throws IOException {
         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
     public ScriptDocValues<?> getScriptDocValues() {
         if (booleans == null) {
@@ -72,35 +64,40 @@ public class BooleanDocValuesField implements DocValuesField<Boolean> {
         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
     public String getName() {
         return name;
     }
 
-    /**
-     * Returns {@code true} if this field has no values, otherwise {@code false}.
-     */
     @Override
     public boolean isEmpty() {
         return count == 0;
     }
 
-    /**
-     * Returns the number of values this field has.
-     */
     @Override
     public int size() {
         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
     public Iterator<Boolean> iterator() {
         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;
 
     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.name = name;
     }
 
     @Override
     public void setNextDocId(int docId) throws IOException {
-        scriptDocValues.setNextDocId(docId);
+        scriptDocValues.getSupplier().setNextDocId(docId);
     }
 
     @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.GeoUtils;
 import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPoints;
+import org.elasticsearch.index.fielddata.ScriptDocValues.GeoPointsSupplier;
 import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
@@ -62,11 +63,11 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
 
         GeoPoint[][] points = { { new GeoPoint(lat1, lon1), new GeoPoint(lat2, lon2) } };
         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());
-        script.setNextDocId(0);
+        script.getSupplier().setNextDocId(0);
         assertEquals(false, script.isEmpty());
         assertEquals(new GeoPoint(lat1, lon1), script.getValue());
         assertEquals(lat1, script.getLat(), 0);
@@ -80,12 +81,12 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
         final double lon = randomLon();
         GeoPoint[][] points = { { new GeoPoint(lat, lon) } };
         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] };
-        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 otherLon = randomLon();
@@ -115,9 +116,9 @@ public class ScriptDocValuesGeoPointsTests extends ESTestCase {
                 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++) {
-            geoPoints.setNextDocId(d);
+            geoPoints.getSupplier().setNextDocId(d);
             if (points[d].length > 0) {
                 assertEquals(points[d][0], geoPoints.getValue());
             } else {

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

@@ -9,6 +9,7 @@
 package org.elasticsearch.index.fielddata;
 
 import org.elasticsearch.index.fielddata.ScriptDocValues.Longs;
+import org.elasticsearch.index.fielddata.ScriptDocValues.LongsSupplier;
 import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
@@ -27,7 +28,7 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
 
         for (int round = 0; round < 10; round++) {
             int d = between(0, values.length - 1);
-            longs.setNextDocId(d);
+            longs.getSupplier().setNextDocId(d);
             if (values[d].length > 0) {
                 assertEquals(values[d][0], longs.getValue());
                 assertEquals(values[d][0], (long) longs.get(0));
@@ -56,7 +57,7 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
     }
 
     private Longs wrap(long[][] values) {
-        return new Longs(new AbstractSortedNumericDocValues() {
+        return new Longs(new LongsSupplier(new AbstractSortedNumericDocValues() {
             long[] current;
             int i;
 
@@ -76,6 +77,6 @@ public class ScriptDocValuesLongsTests extends ESTestCase {
             public long nextValue() {
                 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.index.fielddata.FieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues.Doubles;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.script.field.DelegateDocValuesField;
@@ -42,7 +43,7 @@ public class HalfFloatFielddataTests extends ESTestCase {
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
             reader,
             "half_float",
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         ).getDoubleValues();
         assertNotNull(FieldData.unwrapSingleton(values));
         assertTrue(values.advanceExact(0));
@@ -67,7 +68,7 @@ public class HalfFloatFielddataTests extends ESTestCase {
         SortedNumericDoubleValues values = new SortedDoublesIndexFieldData.SortedNumericHalfFloatFieldData(
             reader,
             "half_float",
-            (dv, n) -> new DelegateDocValuesField(new Doubles(dv), n)
+            (dv, n) -> new DelegateDocValuesField(new Doubles(new DoublesSupplier(dv)), n)
         ).getDoubleValues();
         assertNull(FieldData.unwrapSingleton(values));
         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.LeafNumericFieldData;
 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.mapper.DateFieldMapper.DateFieldType;
 import org.elasticsearch.index.mapper.DateFieldMapper.Resolution;
@@ -338,7 +339,7 @@ public class DateFieldTypeTests extends FieldTypeTestCase {
         SortedNumericIndexFieldData fieldData = new SortedNumericIndexFieldData(
             "my_date",
             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
         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.common.lucene.search.function.ScriptScoreQuery;
 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.query.SearchExecutionContext;
 import org.elasticsearch.script.DocReader;
@@ -145,7 +145,7 @@ public class IpScriptFieldTypeTests extends AbstractScriptFieldTypeTestCase {
                         return new ScoreScript(Map.of(), searchContext.lookup(), docReader) {
                             @Override
                             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));
                             }
                         };

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

@@ -500,7 +500,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                         return new LeafFieldData() {
                             @Override
                             public DocValuesField<?> getScriptField(String name) {
-                                return new DelegateDocValuesField(new ScriptDocValues<String>() {
+                                return new DelegateDocValuesField(new ScriptDocValues<String>(new ScriptDocValues.Supplier<String>() {
                                     String value;
 
                                     @Override
@@ -509,7 +509,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                                     }
 
                                     @Override
-                                    public String get(int index) {
+                                    public String getInternal(int index) {
                                         assert index == 0;
                                         return value;
                                     }
@@ -521,6 +521,16 @@ public class SearchExecutionContextTests extends ESTestCase {
                                         leafLookup.setDocument(docId);
                                         value = runtimeDocValues.apply(leafLookup, docId);
                                     }
+                                }) {
+                                    @Override
+                                    public int size() {
+                                        return supplier.size();
+                                    }
+
+                                    @Override
+                                    public String get(int i) {
+                                        return supplier.getInternal(i);
+                                    }
                                 }, name);
                             }
 
@@ -616,7 +626,7 @@ public class SearchExecutionContextTests extends ESTestCase {
                                     scriptDocValues = indexFieldData.load(context).getScriptField("test").getScriptDocValues();
                                     ;
                                 }
-                                scriptDocValues.setNextDocId(doc);
+                                scriptDocValues.getSupplier().setNextDocId(doc);
                                 for (int i = 0; i < scriptDocValues.size(); i++) {
                                     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.index.fielddata.IndexNumericFieldData;
 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.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
@@ -167,7 +168,7 @@ public class DiversifiedSamplerTests extends AggregatorTestCase {
         SortedDoublesIndexFieldData fieldData = new SortedDoublesIndexFieldData(
             "price",
             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(
             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.junit.Before;
 
+import java.io.IOException;
 import java.util.function.Function;
 
 import static org.mockito.AdditionalAnswers.returnsFirstArg;
@@ -64,7 +65,7 @@ public class LeafDocLookupTests extends ESTestCase {
         assertEquals(docValues, fetchedDocValues);
     }
 
-    public void testFlattenedField() {
+    public void testFlattenedField() throws IOException {
         ScriptDocValues<?> docValues1 = mock(ScriptDocValues.class);
         IndexFieldData<?> fieldData1 = createFieldData(docValues1, "flattened.key1");
 
@@ -95,8 +96,13 @@ public class LeafDocLookupTests extends ESTestCase {
         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);
         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.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.ScriptDocValues;
+import org.elasticsearch.index.fielddata.ScriptDocValues.DoublesSupplier;
 import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
 import org.elasticsearch.index.mapper.DocumentParserContext;
@@ -423,7 +424,10 @@ public class AggregateDoubleMetricFieldMapper extends FieldMapper {
                         @Override
                         public DocValuesField<?> getScriptField(String name) {
                             // 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

+ 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.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 String name;
@@ -76,6 +76,13 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         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
     public int size() {
         return count;
@@ -89,7 +96,7 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return values[index] ^ MASK_2_63;
     }
 
-    @Override
+    /** Return all the values as a {@code List}. */
     public List<Long> getValues() {
         if (isEmpty()) {
             return Collections.emptyList();
@@ -104,13 +111,13 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         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);
     }
 
-    @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) {
             return defaultValue;
         }
@@ -118,6 +125,16 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         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
     public PrimitiveIterator.OfLong iterator() {
         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);
     }
 
-    @Override
+    /** Converts all the values to {@code BigInteger} and returns them as a {@code List}. */
     public List<BigInteger> asBigIntegers() {
         if (isEmpty()) {
             return Collections.emptyList();
@@ -163,12 +180,12 @@ public class UnsignedLongDocValuesField implements UnsignedLongField, DocValuesF
         return values;
     }
 
-    @Override
+    /** Returns the 0th index value as a {@code BigInteger} if it exists, otherwise {@code defaultValue}. */
     public BigInteger asBigInteger(BigInteger 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) {
         if (isEmpty() || index < 0 || index >= count) {
             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 java.io.IOException;
-
 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() {
-        throwIfEmpty();
-        return unsignedLongDocValuesField.getValue(0L); // default is ignored
+        return get(0);
     }
 
     @Override
     public Long get(int index) {
         throwIfEmpty();
-        return unsignedLongDocValuesField.getValue(0L); // default is ignored
+        return supplier.getInternal(index);
     }
 
     @Override
     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()
 }
 
-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(int, long)
   List getValues()
   BigInteger asBigInteger(BigInteger)
   BigInteger asBigInteger(int, BigInteger)
   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> {
 
-    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() {
@@ -40,20 +61,16 @@ public final class VersionScriptDocValues extends ScriptDocValues<String> {
 
     @Override
     public String get(int index) {
-        if (count == 0) {
+        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!"
             );
         }
-        try {
-            return VersionEncoder.decodeVersion(in.lookupOrd(ords[index]));
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        return supplier.getInternal(index);
     }
 
     @Override
     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.xcontent.XContentParser;
 import org.elasticsearch.xpack.versionfield.VersionEncoder.EncodedVersion;
+import org.elasticsearch.xpack.versionfield.VersionScriptDocValues.VersionScriptSupplier;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -279,7 +280,11 @@ public class VersionStringFieldMapper extends FieldMapper {
 
         @Override
         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

+ 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.DocValuesField;
 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 java.io.IOException;
@@ -33,7 +34,7 @@ public abstract class AbstractAtomicGeoShapeShapeFieldData implements LeafGeoSha
 
     @Override
     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) {
@@ -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 GeoPoint centroid = new GeoPoint();
         private final GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
         private GeoShapeValues.GeoShapeValue value;
 
-        private GeoShapeScriptValues(GeoShapeValues in) {
+        private GeoShapeSupplier(GeoShapeValues 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
         public int getDimensionalType() {
-            return value == null ? -1 : value.dimensionalShapeType().ordinal();
+            return gsSupplier.getInternal() == null ? -1 : gsSupplier.getInternal().dimensionalShapeType().ordinal();
         }
 
         @Override
         public GeoPoint getCentroid() {
-            return value == null ? null : centroid;
+            return gsSupplier.getInternal() == null ? null : gsSupplier.getCentroid();
         }
 
         @Override
         public double getMercatorWidth() {
-            return lonToSphericalMercator(boundingBox.right()) - lonToSphericalMercator(boundingBox.left());
+            return lonToSphericalMercator(getBoundingBox().right()) - lonToSphericalMercator(getBoundingBox().left());
         }
 
         @Override
         public double getMercatorHeight() {
-            return latToSphericalMercator(boundingBox.top()) - latToSphericalMercator(boundingBox.bottom());
+            return latToSphericalMercator(getBoundingBox().top()) - latToSphericalMercator(getBoundingBox().bottom());
         }
 
         @Override
         public GeoBoundingBox getBoundingBox() {
-            return value == null ? null : boundingBox;
+            return gsSupplier.getInternal() == null ? null : gsSupplier.getBoundingBox();
         }
 
         @Override
         public GeoShapeValues.GeoShapeValue get(int index) {
-            return value;
+            return gsSupplier.getInternal();
         }
 
         @Override
         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 {
 
-    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 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.vector = new float[dims];
     }
 
     @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
     public float[] getVectorValue() {
-        VectorEncoderDecoder.decodeDenseVector(value, vector);
+        VectorEncoderDecoder.decodeDenseVector(bdvSupplier.getInternal(), vector);
         return vector;
     }
 
     @Override
     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
     public double dotProduct(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
 
         double dotProduct = 0;
@@ -71,6 +95,7 @@ public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues
 
     @Override
     public double l1Norm(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
 
         double l1norm = 0;
@@ -82,6 +107,7 @@ public class BinaryDenseVectorScriptDocValues extends DenseVectorScriptDocValues
 
     @Override
     public double l2Norm(float[] queryVector) {
+        BytesRef value = bdvSupplier.getInternal();
         ByteBuffer byteBuffer = ByteBuffer.wrap(value.bytes, value.offset, value.length);
         double l2norm = 0;
         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;
 
 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!";
 
     private final int dims;
 
-    public DenseVectorScriptDocValues(int dims) {
+    public DenseVectorScriptDocValues(DenseVectorSupplier<?> supplier, int dims) {
+        super(supplier);
         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
             public float[] getVectorValue() {
                 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);
             }
 
-            @Override
-            public void setNextDocId(int docId) {
-                // do nothing
-            }
-
             @Override
             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;
 
 import org.apache.lucene.index.VectorValues;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.VectorUtil;
 
 import java.io.IOException;
@@ -16,36 +17,64 @@ import java.util.Arrays;
 import static org.apache.lucene.search.DocIdSetIterator.NO_MORE_DOCS;
 
 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();
             } 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() {
-        if (vector == null) {
+        if (kdvSupplier.getInternal() == null) {
             throw new IllegalArgumentException(MISSING_VECTOR_FIELD_MESSAGE);
         }
-        return vector;
+        return kdvSupplier.getInternal();
     }
 
     @Override
@@ -88,10 +117,6 @@ public class KnnDenseVectorScriptDocValues extends DenseVectorScriptDocValues {
 
     @Override
     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() {
             try {
-                docValues.setNextDocId(scoreScript._getDocId());
+                docValues.getSupplier().setNextDocId(scoreScript._getDocId());
             } catch (IOException 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.script.field.DelegateDocValuesField;
 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.util.Collection;
 import java.util.Collections;
 
+import static org.elasticsearch.xpack.vectors.query.DenseVectorScriptDocValues.MISSING_VECTOR_FIELD_MESSAGE;
+
 final class VectorDVLeafFieldData implements LeafFieldData {
 
     private final LeafReader reader;
@@ -59,12 +64,30 @@ final class VectorDVLeafFieldData implements LeafFieldData {
             if (indexed) {
                 VectorValues values = reader.getVectorValues(field);
                 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 {
                 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) {
             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.test.ESTestCase;
 import org.elasticsearch.xpack.vectors.mapper.VectorEncoderDecoder;
+import org.elasticsearch.xpack.vectors.query.BinaryDenseVectorScriptDocValues.BinaryDenseVectorSupplier;
 
 import java.io.IOException;
 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)) {
             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++) {
-                scriptDocValues.setNextDocId(i);
+                supplier.setNextDocId(i);
                 assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
                 assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
             }
@@ -41,13 +43,14 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         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());
 
-        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());
     }
 
@@ -55,9 +58,10 @@ public class BinaryDenseVectorScriptDocValuesTests extends ESTestCase {
         int dims = 3;
         float[][] vectors = { { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 3 } };
         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));
         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)) {
             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(
                 "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.script.ScoreScript;
 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.DotProduct;
 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)) {
             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);
             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.util.BytesRef;
 import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.vectors.query.KnnDenseVectorScriptDocValues.KnnDenseVectorSupplier;
 
 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[] 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++) {
-            scriptDocValues.setNextDocId(i);
+            supplier.setNextDocId(i);
             assertArrayEquals(vectors[i], scriptDocValues.getVectorValue(), 0.0001f);
             assertEquals(expectedMagnitudes[i], scriptDocValues.getMagnitude(), 0.0001f);
         }
@@ -33,9 +35,10 @@ public class KnnDenseVectorScriptDocValuesTests extends ESTestCase {
     public void testMissingValues() throws IOException {
         int dims = 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());
         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 {
         int dims = 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));
         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[] 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("l1norm result is not equal to the expected value!", 485.184, scriptDocValues.l1Norm(queryVector), 0.001);