Forráskód Böngészése

ESQL: Move serialization for EsField (#109222)

This moves the serialization logic for `EsField` into the `EsField`
subclasses to better align with the way rest of Elasticsearch works. It
also switches them from ESQL's home grown `writeNamed` thing to
`NamedWriteable`. These are wire compatible with one another.
Nik Everett 1 éve
szülő
commit
5bd1bfc425
19 módosított fájl, 543 hozzáadás és 231 törlés
  1. 23 1
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java
  2. 42 2
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java
  3. 27 2
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/InvalidMappedField.java
  4. 38 1
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java
  5. 23 1
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java
  6. 29 2
      x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/UnsupportedEsField.java
  7. 65 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/AbstractEsFieldTypeTests.java
  8. 36 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/DateEsFieldTests.java
  9. 45 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/EsFieldTests.java
  10. 39 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/InvalidMappedFieldTests.java
  11. 50 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/KeywordEsFieldTests.java
  12. 42 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/TextEsFieldTests.java
  13. 42 0
      x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/UnsupportedEsFieldTests.java
  14. 6 117
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java
  15. 0 5
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanStreamInput.java
  16. 21 21
      x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java
  17. 14 13
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/SerializationTestUtils.java
  18. 0 66
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java
  19. 1 0
      x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java

+ 23 - 1
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java

@@ -6,12 +6,18 @@
  */
  */
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+
+import java.io.IOException;
 import java.util.Map;
 import java.util.Map;
 
 
 /**
 /**
- * SQL-related information about an index field with date type
+ * Information about a field in an ES index with the {@code date} type
  */
  */
 public class DateEsField extends EsField {
 public class DateEsField extends EsField {
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(EsField.class, "DateEsField", DateEsField::new);
 
 
     public static DateEsField dateEsField(String name, Map<String, EsField> properties, boolean hasDocValues) {
     public static DateEsField dateEsField(String name, Map<String, EsField> properties, boolean hasDocValues) {
         return new DateEsField(name, DataTypes.DATETIME, properties, hasDocValues);
         return new DateEsField(name, DataTypes.DATETIME, properties, hasDocValues);
@@ -20,4 +26,20 @@ public class DateEsField extends EsField {
     private DateEsField(String name, DataType dataType, Map<String, EsField> properties, boolean hasDocValues) {
     private DateEsField(String name, DataType dataType, Map<String, EsField> properties, boolean hasDocValues) {
         super(name, dataType, properties, hasDocValues);
         super(name, dataType, properties, hasDocValues);
     }
     }
+
+    private DateEsField(StreamInput in) throws IOException {
+        this(in.readString(), DataTypes.DATETIME, in.readMap(i -> i.readNamedWriteable(EsField.class)), in.readBoolean());
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(getName());
+        out.writeMap(getProperties(), StreamOutput::writeNamedWriteable);
+        out.writeBoolean(isAggregatable());
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
 }
 }

+ 42 - 2
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java

@@ -6,15 +6,33 @@
  */
  */
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteable;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.core.Nullable;
 import org.elasticsearch.core.Nullable;
 
 
+import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 
 
 /**
 /**
- * SQL-related information about an index field
+ * Information about a field in an ES index.
  */
  */
-public class EsField {
+public class EsField implements NamedWriteable {
+    public static List<NamedWriteableRegistry.Entry> getNamedWriteables() {
+        return List.of(
+            EsField.ENTRY,
+            DateEsField.ENTRY,
+            InvalidMappedField.ENTRY,
+            KeywordEsField.ENTRY,
+            TextEsField.ENTRY,
+            UnsupportedEsField.ENTRY
+        );
+    }
+
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(EsField.class, "EsField", EsField::new);
 
 
     private final DataType esDataType;
     private final DataType esDataType;
     private final boolean aggregatable;
     private final boolean aggregatable;
@@ -34,6 +52,28 @@ public class EsField {
         this.isAlias = isAlias;
         this.isAlias = isAlias;
     }
     }
 
 
+    public EsField(StreamInput in) throws IOException {
+        this.name = in.readString();
+        this.esDataType = DataType.readFrom(in);
+        this.properties = in.readImmutableMap(StreamInput::readString, i -> i.readNamedWriteable(EsField.class));
+        this.aggregatable = in.readBoolean();
+        this.isAlias = in.readBoolean();
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(name);
+        out.writeString(esDataType.typeName());
+        out.writeMap(properties, StreamOutput::writeNamedWriteable);
+        out.writeBoolean(aggregatable);
+        out.writeBoolean(isAlias);
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
+
     /**
     /**
      * Returns the field path
      * Returns the field path
      */
      */

+ 27 - 2
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/InvalidMappedField.java

@@ -7,8 +7,12 @@
 
 
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
 import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
 
 
+import java.io.IOException;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.TreeMap;
@@ -18,6 +22,11 @@ import java.util.TreeMap;
  * Used during mapping discovery only.
  * Used during mapping discovery only.
  */
  */
 public class InvalidMappedField extends EsField {
 public class InvalidMappedField extends EsField {
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+        EsField.class,
+        "InvalidMappedField",
+        InvalidMappedField::new
+    );
 
 
     private final String errorMessage;
     private final String errorMessage;
 
 
@@ -27,11 +36,27 @@ public class InvalidMappedField extends EsField {
     }
     }
 
 
     public InvalidMappedField(String name, String errorMessage) {
     public InvalidMappedField(String name, String errorMessage) {
-        this(name, errorMessage, new TreeMap<String, EsField>());
+        this(name, errorMessage, new TreeMap<>());
     }
     }
 
 
     public InvalidMappedField(String name) {
     public InvalidMappedField(String name) {
-        this(name, StringUtils.EMPTY, new TreeMap<String, EsField>());
+        this(name, StringUtils.EMPTY, new TreeMap<>());
+    }
+
+    private InvalidMappedField(StreamInput in) throws IOException {
+        this(in.readString(), in.readString(), in.readImmutableMap(StreamInput::readString, i -> i.readNamedWriteable(EsField.class)));
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(getName());
+        out.writeString(errorMessage);
+        out.writeMap(getProperties(), StreamOutput::writeNamedWriteable);
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
     }
     }
 
 
     public String errorMessage() {
     public String errorMessage() {

+ 38 - 1
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java

@@ -6,6 +6,11 @@
  */
  */
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+
+import java.io.IOException;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
@@ -13,9 +18,14 @@ import java.util.Objects;
 import static org.elasticsearch.xpack.esql.core.type.DataTypes.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataTypes.KEYWORD;
 
 
 /**
 /**
- * SQL-related information about an index field with keyword type
+ * Information about a field in an ES index with the {@code keyword} type.
  */
  */
 public class KeywordEsField extends EsField {
 public class KeywordEsField extends EsField {
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+        EsField.class,
+        "KeywordEsField",
+        KeywordEsField::new
+    );
 
 
     private final int precision;
     private final int precision;
     private final boolean normalized;
     private final boolean normalized;
@@ -53,6 +63,33 @@ public class KeywordEsField extends EsField {
         this.normalized = normalized;
         this.normalized = normalized;
     }
     }
 
 
+    private KeywordEsField(StreamInput in) throws IOException {
+        this(
+            in.readString(),
+            KEYWORD,
+            in.readMap(i -> i.readNamedWriteable(EsField.class)),
+            in.readBoolean(),
+            in.readInt(),
+            in.readBoolean(),
+            in.readBoolean()
+        );
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(getName());
+        out.writeMap(getProperties(), StreamOutput::writeNamedWriteable);
+        out.writeBoolean(isAggregatable());
+        out.writeInt(precision);
+        out.writeBoolean(normalized);
+        out.writeBoolean(isAlias());
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
+
     public int getPrecision() {
     public int getPrecision() {
         return precision;
         return precision;
     }
     }

+ 23 - 1
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java

@@ -6,9 +6,13 @@
  */
  */
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.core.Tuple;
 import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
 import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException;
 
 
+import java.io.IOException;
 import java.util.Map;
 import java.util.Map;
 import java.util.function.Function;
 import java.util.function.Function;
 
 
@@ -16,9 +20,10 @@ import static org.elasticsearch.xpack.esql.core.type.DataTypes.KEYWORD;
 import static org.elasticsearch.xpack.esql.core.type.DataTypes.TEXT;
 import static org.elasticsearch.xpack.esql.core.type.DataTypes.TEXT;
 
 
 /**
 /**
- * SQL-related information about an index field with text type
+ * Information about a field in an es index with the {@code text} type.
  */
  */
 public class TextEsField extends EsField {
 public class TextEsField extends EsField {
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(EsField.class, "TextEsField", TextEsField::new);
 
 
     public TextEsField(String name, Map<String, EsField> properties, boolean hasDocValues) {
     public TextEsField(String name, Map<String, EsField> properties, boolean hasDocValues) {
         this(name, properties, hasDocValues, false);
         this(name, properties, hasDocValues, false);
@@ -28,6 +33,23 @@ public class TextEsField extends EsField {
         super(name, TEXT, properties, hasDocValues, isAlias);
         super(name, TEXT, properties, hasDocValues, isAlias);
     }
     }
 
 
+    private TextEsField(StreamInput in) throws IOException {
+        this(in.readString(), in.readMap(i -> i.readNamedWriteable(EsField.class)), in.readBoolean(), in.readBoolean());
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(getName());
+        out.writeMap(getProperties(), StreamOutput::writeNamedWriteable);
+        out.writeBoolean(isAggregatable());
+        out.writeBoolean(isAlias());
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
+
     @Override
     @Override
     public EsField getExactField() {
     public EsField getExactField() {
         Tuple<EsField, String> findExact = findExact();
         Tuple<EsField, String> findExact = findExact();

+ 29 - 2
x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/UnsupportedEsField.java

@@ -6,15 +6,25 @@
  */
  */
 package org.elasticsearch.xpack.esql.core.type;
 package org.elasticsearch.xpack.esql.core.type;
 
 
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+
+import java.io.IOException;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.TreeMap;
 
 
 /**
 /**
- * SQL-related information about an index field that cannot be supported by SQL.
- * All the subfields (properties) of an unsupported type should also be unsupported.
+ * Information about a field in an ES index that cannot be supported by ESQL.
+ * All the subfields (properties) of an unsupported type are also be unsupported.
  */
  */
 public class UnsupportedEsField extends EsField {
 public class UnsupportedEsField extends EsField {
+    static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+        EsField.class,
+        "UnsupportedEsField",
+        UnsupportedEsField::new
+    );
 
 
     private final String originalType;
     private final String originalType;
     private final String inherited; // for fields belonging to parents (or grandparents) that have an unsupported type
     private final String inherited; // for fields belonging to parents (or grandparents) that have an unsupported type
@@ -29,6 +39,23 @@ public class UnsupportedEsField extends EsField {
         this.inherited = inherited;
         this.inherited = inherited;
     }
     }
 
 
+    public UnsupportedEsField(StreamInput in) throws IOException {
+        this(in.readString(), in.readString(), in.readOptionalString(), in.readMap(i -> i.readNamedWriteable(EsField.class)));
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(getName());
+        out.writeString(getOriginalType());
+        out.writeOptionalString(getInherited());
+        out.writeMap(getProperties(), StreamOutput::writeNamedWriteable);
+    }
+
+    @Override
+    public String getWriteableName() {
+        return ENTRY.name;
+    }
+
     public String getOriginalType() {
     public String getOriginalType() {
         return originalType;
         return originalType;
     }
     }

+ 65 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/AbstractEsFieldTypeTests.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.test.AbstractNamedWriteableTestCase;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+public abstract class AbstractEsFieldTypeTests<T extends EsField> extends AbstractNamedWriteableTestCase<EsField> {
+    @Override
+    protected abstract T createTestInstance();
+
+    protected abstract T mutate(T instance) throws IOException;
+
+    /**
+     * Generate sub-properties.
+     * @param maxDepth the maximum number of levels of properties to make
+     */
+    static Map<String, EsField> randomProperties(int maxDepth) {
+        if (maxDepth < 0) {
+            throw new IllegalArgumentException("depth must be >= 0");
+        }
+        if (maxDepth == 0 || randomBoolean()) {
+            return Map.of();
+        }
+        int targetSize = between(1, 5);
+        Map<String, EsField> properties = new TreeMap<>();
+        while (properties.size() < targetSize) {
+            properties.put(randomAlphaOfLength(properties.size() + 1), switch (between(0, 5)) {
+                case 0 -> EsFieldTests.randomEsField(maxDepth - 1);
+                case 1 -> DateEsFieldTests.randomDateEsField(maxDepth - 1);
+                case 2 -> InvalidMappedFieldTests.randomInvalidMappedField(maxDepth - 1);
+                case 3 -> KeywordEsFieldTests.randomKeywordEsField(maxDepth - 1);
+                case 4 -> TextEsFieldTests.randomTextEsField(maxDepth - 1);
+                case 5 -> UnsupportedEsFieldTests.randomUnsupportedEsField(maxDepth - 1);
+                default -> throw new IllegalArgumentException();
+            });
+        }
+        return properties;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected final T mutateInstance(EsField instance) throws IOException {
+        return mutate((T) instance);
+    }
+
+    @Override
+    protected final NamedWriteableRegistry getNamedWriteableRegistry() {
+        return new NamedWriteableRegistry(EsField.getNamedWriteables());
+    }
+
+    @Override
+    protected final Class<EsField> categoryClass() {
+        return EsField.class;
+    }
+}

+ 36 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/DateEsFieldTests.java

@@ -0,0 +1,36 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class DateEsFieldTests extends AbstractEsFieldTypeTests<DateEsField> {
+    static DateEsField randomDateEsField(int maxPropertiesDepth) {
+        return DateEsField.dateEsField(randomAlphaOfLength(5), randomProperties(maxPropertiesDepth), randomBoolean());
+    }
+
+    @Override
+    protected DateEsField createTestInstance() {
+        return randomDateEsField(4);
+    }
+
+    @Override
+    protected DateEsField mutate(DateEsField instance) throws IOException {
+        String name = instance.getName();
+        Map<String, EsField> properties = instance.getProperties();
+        boolean aggregatable = instance.isAggregatable();
+        switch (between(0, 2)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            case 2 -> aggregatable = false == aggregatable;
+            default -> throw new IllegalArgumentException();
+        }
+        return DateEsField.dateEsField(name, properties, aggregatable);
+    }
+}

+ 45 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/EsFieldTests.java

@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class EsFieldTests extends AbstractEsFieldTypeTests<EsField> {
+    static EsField randomEsField(int maxPropertiesDepth) {
+        String name = randomAlphaOfLength(4);
+        DataType esDataType = randomFrom(DataTypes.types());
+        Map<String, EsField> properties = randomProperties(maxPropertiesDepth);
+        boolean aggregatable = randomBoolean();
+        boolean isAlias = randomBoolean();
+        return new EsField(name, esDataType, properties, aggregatable, isAlias);
+    }
+
+    @Override
+    protected EsField createTestInstance() {
+        return randomEsField(4);
+    }
+
+    @Override
+    protected EsField mutate(EsField instance) throws IOException {
+        String name = instance.getName();
+        DataType esDataType = instance.getDataType();
+        Map<String, EsField> properties = instance.getProperties();
+        boolean aggregatable = instance.isAggregatable();
+        boolean isAlias = instance.isAlias();
+        switch (between(0, 4)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> esDataType = randomValueOtherThan(esDataType, () -> randomFrom(DataTypes.types()));
+            case 2 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            case 3 -> aggregatable = false == aggregatable;
+            case 4 -> isAlias = false == isAlias;
+            default -> throw new IllegalArgumentException();
+        }
+        return new EsField(name, esDataType, properties, aggregatable, isAlias);
+    }
+}

+ 39 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/InvalidMappedFieldTests.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class InvalidMappedFieldTests extends AbstractEsFieldTypeTests<InvalidMappedField> {
+    static InvalidMappedField randomInvalidMappedField(int maxPropertiesDepth) {
+        String name = randomAlphaOfLength(4);
+        String errorMessage = randomAlphaOfLengthBetween(1, 100);
+        Map<String, EsField> properties = randomProperties(maxPropertiesDepth);
+        return new InvalidMappedField(name, errorMessage, properties);
+    }
+
+    @Override
+    protected InvalidMappedField createTestInstance() {
+        return randomInvalidMappedField(4);
+    }
+
+    @Override
+    protected InvalidMappedField mutate(InvalidMappedField instance) throws IOException {
+        String name = instance.getName();
+        String errorMessage = instance.errorMessage();
+        Map<String, EsField> properties = instance.getProperties();
+        switch (between(0, 2)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> errorMessage = randomValueOtherThan(errorMessage, () -> randomAlphaOfLengthBetween(1, 100));
+            case 2 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            default -> throw new IllegalArgumentException();
+        }
+        return new InvalidMappedField(name, errorMessage, properties);
+    }
+}

+ 50 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/KeywordEsFieldTests.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class KeywordEsFieldTests extends AbstractEsFieldTypeTests<KeywordEsField> {
+    static KeywordEsField randomKeywordEsField(int maxPropertiesDepth) {
+        String name = randomAlphaOfLength(4);
+        Map<String, EsField> properties = randomProperties(maxPropertiesDepth);
+        boolean hasDocValues = randomBoolean();
+        int precision = randomInt();
+        boolean normalized = randomBoolean();
+        boolean isAlias = randomBoolean();
+        return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias);
+    }
+
+    @Override
+    protected KeywordEsField createTestInstance() {
+        return randomKeywordEsField(4);
+    }
+
+    @Override
+    protected KeywordEsField mutate(KeywordEsField instance) throws IOException {
+        String name = instance.getName();
+        Map<String, EsField> properties = instance.getProperties();
+        boolean hasDocValues = instance.isAggregatable();
+        int precision = instance.getPrecision();
+        boolean normalized = instance.getNormalized();
+        boolean isAlias = instance.isAlias();
+        switch (between(0, 5)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            case 2 -> hasDocValues = false == hasDocValues;
+            case 3 -> precision = randomValueOtherThan(precision, ESTestCase::randomInt);
+            case 4 -> normalized = false == normalized;
+            case 5 -> isAlias = false == isAlias;
+            default -> throw new IllegalArgumentException();
+        }
+        return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias);
+    }
+}

+ 42 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/TextEsFieldTests.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class TextEsFieldTests extends AbstractEsFieldTypeTests<TextEsField> {
+    static TextEsField randomTextEsField(int maxPropertiesDepth) {
+        String name = randomAlphaOfLength(4);
+        Map<String, EsField> properties = randomProperties(maxPropertiesDepth);
+        boolean hasDocValues = randomBoolean();
+        boolean isAlias = randomBoolean();
+        return new TextEsField(name, properties, hasDocValues, isAlias);
+    }
+
+    @Override
+    protected TextEsField createTestInstance() {
+        return randomTextEsField(4);
+    }
+
+    @Override
+    protected TextEsField mutate(TextEsField instance) throws IOException {
+        String name = instance.getName();
+        Map<String, EsField> properties = instance.getProperties();
+        boolean hasDocValues = instance.isAggregatable();
+        boolean isAlias = instance.isAlias();
+        switch (between(0, 3)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            case 2 -> hasDocValues = false == hasDocValues;
+            case 3 -> isAlias = false == isAlias;
+            default -> throw new IllegalArgumentException();
+        }
+        return new TextEsField(name, properties, hasDocValues, isAlias);
+    }
+}

+ 42 - 0
x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/type/UnsupportedEsFieldTests.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.esql.core.type;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class UnsupportedEsFieldTests extends AbstractEsFieldTypeTests<UnsupportedEsField> {
+    static UnsupportedEsField randomUnsupportedEsField(int maxPropertiesDepth) {
+        String name = randomAlphaOfLength(4);
+        String originalType = randomAlphaOfLength(5);
+        String inherited = randomBoolean() ? null : randomAlphaOfLength(5);
+        Map<String, EsField> properties = randomProperties(maxPropertiesDepth);
+        return new UnsupportedEsField(name, originalType, inherited, properties);
+    }
+
+    @Override
+    protected UnsupportedEsField createTestInstance() {
+        return randomUnsupportedEsField(4);
+    }
+
+    @Override
+    protected UnsupportedEsField mutate(UnsupportedEsField instance) throws IOException {
+        String name = instance.getName();
+        String originalType = randomAlphaOfLength(5);
+        String inherited = randomBoolean() ? null : randomAlphaOfLength(5);
+        Map<String, EsField> properties = instance.getProperties();
+        switch (between(0, 3)) {
+            case 0 -> name = randomAlphaOfLength(name.length() + 1);
+            case 1 -> originalType = randomValueOtherThan(originalType, () -> randomAlphaOfLength(4));
+            case 2 -> inherited = randomValueOtherThan(inherited, () -> randomBoolean() ? null : randomAlphaOfLength(4));
+            case 3 -> properties = randomValueOtherThan(properties, () -> randomProperties(4));
+            default -> throw new IllegalArgumentException();
+        }
+        return new UnsupportedEsField(name, originalType, inherited, properties);
+    }
+}

+ 6 - 117
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypes.java

@@ -50,11 +50,7 @@ import org.elasticsearch.xpack.esql.core.plan.logical.LogicalPlan;
 import org.elasticsearch.xpack.esql.core.plan.logical.OrderBy;
 import org.elasticsearch.xpack.esql.core.plan.logical.OrderBy;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.core.type.DataType;
 import org.elasticsearch.xpack.esql.core.type.DataType;
-import org.elasticsearch.xpack.esql.core.type.DateEsField;
 import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.core.type.EsField;
-import org.elasticsearch.xpack.esql.core.type.InvalidMappedField;
-import org.elasticsearch.xpack.esql.core.type.KeywordEsField;
-import org.elasticsearch.xpack.esql.core.type.TextEsField;
 import org.elasticsearch.xpack.esql.core.type.UnsupportedEsField;
 import org.elasticsearch.xpack.esql.core.type.UnsupportedEsField;
 import org.elasticsearch.xpack.esql.expression.function.UnsupportedAttribute;
 import org.elasticsearch.xpack.esql.expression.function.UnsupportedAttribute;
 import org.elasticsearch.xpack.esql.expression.function.aggregate.AggregateFunction;
 import org.elasticsearch.xpack.esql.expression.function.aggregate.AggregateFunction;
@@ -307,13 +303,6 @@ public final class PlanNamedTypes {
             of(Attribute.class, ReferenceAttribute.class, PlanNamedTypes::writeReferenceAttr, PlanNamedTypes::readReferenceAttr),
             of(Attribute.class, ReferenceAttribute.class, PlanNamedTypes::writeReferenceAttr, PlanNamedTypes::readReferenceAttr),
             of(Attribute.class, MetadataAttribute.class, PlanNamedTypes::writeMetadataAttr, PlanNamedTypes::readMetadataAttr),
             of(Attribute.class, MetadataAttribute.class, PlanNamedTypes::writeMetadataAttr, PlanNamedTypes::readMetadataAttr),
             of(Attribute.class, UnsupportedAttribute.class, PlanNamedTypes::writeUnsupportedAttr, PlanNamedTypes::readUnsupportedAttr),
             of(Attribute.class, UnsupportedAttribute.class, PlanNamedTypes::writeUnsupportedAttr, PlanNamedTypes::readUnsupportedAttr),
-            // EsFields
-            of(EsField.class, EsField.class, PlanNamedTypes::writeEsField, PlanNamedTypes::readEsField),
-            of(EsField.class, DateEsField.class, PlanNamedTypes::writeDateEsField, PlanNamedTypes::readDateEsField),
-            of(EsField.class, InvalidMappedField.class, PlanNamedTypes::writeInvalidMappedField, PlanNamedTypes::readInvalidMappedField),
-            of(EsField.class, KeywordEsField.class, PlanNamedTypes::writeKeywordEsField, PlanNamedTypes::readKeywordEsField),
-            of(EsField.class, TextEsField.class, PlanNamedTypes::writeTextEsField, PlanNamedTypes::readTextEsField),
-            of(EsField.class, UnsupportedEsField.class, PlanNamedTypes::writeUnsupportedEsField, PlanNamedTypes::readUnsupportedEsField),
             // NamedExpressions
             // NamedExpressions
             of(NamedExpression.class, Alias.class, PlanNamedTypes::writeAlias, PlanNamedTypes::readAlias),
             of(NamedExpression.class, Alias.class, PlanNamedTypes::writeAlias, PlanNamedTypes::readAlias),
             // BinaryComparison
             // BinaryComparison
@@ -1071,7 +1060,7 @@ public final class PlanNamedTypes {
             in.readOptionalWithReader(PlanNamedTypes::readFieldAttribute),
             in.readOptionalWithReader(PlanNamedTypes::readFieldAttribute),
             in.readString(),
             in.readString(),
             DataType.readFrom(in),
             DataType.readFrom(in),
-            in.readEsFieldNamed(),
+            in.readNamedWriteable(EsField.class),
             in.readOptionalString(),
             in.readOptionalString(),
             in.readEnum(Nullability.class),
             in.readEnum(Nullability.class),
             NameId.readFrom(in),
             NameId.readFrom(in),
@@ -1084,7 +1073,7 @@ public final class PlanNamedTypes {
         out.writeOptionalWriteable(fieldAttribute.parent() == null ? null : o -> writeFieldAttribute(out, fieldAttribute.parent()));
         out.writeOptionalWriteable(fieldAttribute.parent() == null ? null : o -> writeFieldAttribute(out, fieldAttribute.parent()));
         out.writeString(fieldAttribute.name());
         out.writeString(fieldAttribute.name());
         out.writeString(fieldAttribute.dataType().typeName());
         out.writeString(fieldAttribute.dataType().typeName());
-        out.writeNamed(EsField.class, fieldAttribute.field());
+        out.writeNamedWriteable(fieldAttribute.field());
         out.writeOptionalString(fieldAttribute.qualifier());
         out.writeOptionalString(fieldAttribute.qualifier());
         out.writeEnum(fieldAttribute.nullable());
         out.writeEnum(fieldAttribute.nullable());
         fieldAttribute.id().writeTo(out);
         fieldAttribute.id().writeTo(out);
@@ -1141,7 +1130,7 @@ public final class PlanNamedTypes {
         return new UnsupportedAttribute(
         return new UnsupportedAttribute(
             Source.readFrom(in),
             Source.readFrom(in),
             in.readString(),
             in.readString(),
-            readUnsupportedEsField(in),
+            new UnsupportedEsField(in),
             in.readOptionalString(),
             in.readOptionalString(),
             NameId.readFrom(in)
             NameId.readFrom(in)
         );
         );
@@ -1150,111 +1139,11 @@ public final class PlanNamedTypes {
     static void writeUnsupportedAttr(PlanStreamOutput out, UnsupportedAttribute unsupportedAttribute) throws IOException {
     static void writeUnsupportedAttr(PlanStreamOutput out, UnsupportedAttribute unsupportedAttribute) throws IOException {
         Source.EMPTY.writeTo(out);
         Source.EMPTY.writeTo(out);
         out.writeString(unsupportedAttribute.name());
         out.writeString(unsupportedAttribute.name());
-        writeUnsupportedEsField(out, unsupportedAttribute.field());
+        unsupportedAttribute.field().writeTo(out);
         out.writeOptionalString(unsupportedAttribute.hasCustomMessage() ? unsupportedAttribute.unresolvedMessage() : null);
         out.writeOptionalString(unsupportedAttribute.hasCustomMessage() ? unsupportedAttribute.unresolvedMessage() : null);
         unsupportedAttribute.id().writeTo(out);
         unsupportedAttribute.id().writeTo(out);
     }
     }
 
 
-    // -- EsFields
-
-    static EsField readEsField(PlanStreamInput in) throws IOException {
-        return new EsField(
-            in.readString(),
-            DataType.readFrom(in),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed)),
-            in.readBoolean(),
-            in.readBoolean()
-        );
-    }
-
-    static void writeEsField(PlanStreamOutput out, EsField esField) throws IOException {
-        out.writeString(esField.getName());
-        out.writeString(esField.getDataType().typeName());
-        out.writeMap(esField.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-        out.writeBoolean(esField.isAggregatable());
-        out.writeBoolean(esField.isAlias());
-    }
-
-    static DateEsField readDateEsField(PlanStreamInput in) throws IOException {
-        return DateEsField.dateEsField(
-            in.readString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed)),
-            in.readBoolean()
-        );
-    }
-
-    static void writeDateEsField(PlanStreamOutput out, DateEsField dateEsField) throws IOException {
-        out.writeString(dateEsField.getName());
-        out.writeMap(dateEsField.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-        out.writeBoolean(dateEsField.isAggregatable());
-    }
-
-    static InvalidMappedField readInvalidMappedField(PlanStreamInput in) throws IOException {
-        return new InvalidMappedField(
-            in.readString(),
-            in.readString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed))
-        );
-    }
-
-    static void writeInvalidMappedField(PlanStreamOutput out, InvalidMappedField field) throws IOException {
-        out.writeString(field.getName());
-        out.writeString(field.errorMessage());
-        out.writeMap(field.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-    }
-
-    static KeywordEsField readKeywordEsField(PlanStreamInput in) throws IOException {
-        return new KeywordEsField(
-            in.readString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed)),
-            in.readBoolean(),
-            in.readInt(),
-            in.readBoolean(),
-            in.readBoolean()
-        );
-    }
-
-    static void writeKeywordEsField(PlanStreamOutput out, KeywordEsField keywordEsField) throws IOException {
-        out.writeString(keywordEsField.getName());
-        out.writeMap(keywordEsField.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-        out.writeBoolean(keywordEsField.isAggregatable());
-        out.writeInt(keywordEsField.getPrecision());
-        out.writeBoolean(keywordEsField.getNormalized());
-        out.writeBoolean(keywordEsField.isAlias());
-    }
-
-    static TextEsField readTextEsField(PlanStreamInput in) throws IOException {
-        return new TextEsField(
-            in.readString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed)),
-            in.readBoolean(),
-            in.readBoolean()
-        );
-    }
-
-    static void writeTextEsField(PlanStreamOutput out, TextEsField textEsField) throws IOException {
-        out.writeString(textEsField.getName());
-        out.writeMap(textEsField.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-        out.writeBoolean(textEsField.isAggregatable());
-        out.writeBoolean(textEsField.isAlias());
-    }
-
-    static UnsupportedEsField readUnsupportedEsField(PlanStreamInput in) throws IOException {
-        return new UnsupportedEsField(
-            in.readString(),
-            in.readString(),
-            in.readOptionalString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed))
-        );
-    }
-
-    static void writeUnsupportedEsField(PlanStreamOutput out, UnsupportedEsField unsupportedEsField) throws IOException {
-        out.writeString(unsupportedEsField.getName());
-        out.writeString(unsupportedEsField.getOriginalType());
-        out.writeOptionalString(unsupportedEsField.getInherited());
-        out.writeMap(unsupportedEsField.getProperties(), (o, v) -> out.writeNamed(EsField.class, v));
-    }
-
     // -- BinaryComparison
     // -- BinaryComparison
 
 
     static EsqlBinaryComparison readBinComparison(PlanStreamInput in, String name) throws IOException {
     static EsqlBinaryComparison readBinComparison(PlanStreamInput in, String name) throws IOException {
@@ -1967,14 +1856,14 @@ public final class PlanNamedTypes {
     static EsIndex readEsIndex(PlanStreamInput in) throws IOException {
     static EsIndex readEsIndex(PlanStreamInput in) throws IOException {
         return new EsIndex(
         return new EsIndex(
             in.readString(),
             in.readString(),
-            in.readImmutableMap(StreamInput::readString, readerFromPlanReader(PlanStreamInput::readEsFieldNamed)),
+            in.readImmutableMap(StreamInput::readString, i -> i.readNamedWriteable(EsField.class)),
             (Set<String>) in.readGenericValue()
             (Set<String>) in.readGenericValue()
         );
         );
     }
     }
 
 
     static void writeEsIndex(PlanStreamOutput out, EsIndex esIndex) throws IOException {
     static void writeEsIndex(PlanStreamOutput out, EsIndex esIndex) throws IOException {
         out.writeString(esIndex.name());
         out.writeString(esIndex.name());
-        out.writeMap(esIndex.mapping(), (o, v) -> out.writeNamed(EsField.class, v));
+        out.writeMap(esIndex.mapping(), StreamOutput::writeNamedWriteable);
         out.writeGenericValue(esIndex.concreteIndices());
         out.writeGenericValue(esIndex.concreteIndices());
     }
     }
 
 

+ 0 - 5
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/io/stream/PlanStreamInput.java

@@ -30,7 +30,6 @@ import org.elasticsearch.xpack.esql.core.expression.Expression;
 import org.elasticsearch.xpack.esql.core.expression.NameId;
 import org.elasticsearch.xpack.esql.core.expression.NameId;
 import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
 import org.elasticsearch.xpack.esql.core.expression.NamedExpression;
 import org.elasticsearch.xpack.esql.core.plan.logical.LogicalPlan;
 import org.elasticsearch.xpack.esql.core.plan.logical.LogicalPlan;
-import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry.PlanNamedReader;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry.PlanNamedReader;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry.PlanReader;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry.PlanReader;
 import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan;
 import org.elasticsearch.xpack.esql.plan.physical.PhysicalPlan;
@@ -111,10 +110,6 @@ public final class PlanStreamInput extends NamedWriteableAwareStreamInput
         return readNamed(Attribute.class);
         return readNamed(Attribute.class);
     }
     }
 
 
-    public EsField readEsFieldNamed() throws IOException {
-        return readNamed(EsField.class);
-    }
-
     public <T> T readNamed(Class<T> type) throws IOException {
     public <T> T readNamed(Class<T> type) throws IOException {
         String name = readString();
         String name = readString();
         @SuppressWarnings("unchecked")
         @SuppressWarnings("unchecked")

+ 21 - 21
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plugin/EsqlPlugin.java

@@ -56,6 +56,7 @@ import org.elasticsearch.xpack.esql.action.RestEsqlDeleteAsyncResultAction;
 import org.elasticsearch.xpack.esql.action.RestEsqlGetAsyncResultAction;
 import org.elasticsearch.xpack.esql.action.RestEsqlGetAsyncResultAction;
 import org.elasticsearch.xpack.esql.action.RestEsqlQueryAction;
 import org.elasticsearch.xpack.esql.action.RestEsqlQueryAction;
 import org.elasticsearch.xpack.esql.core.index.IndexResolver;
 import org.elasticsearch.xpack.esql.core.index.IndexResolver;
+import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.enrich.EnrichLookupOperator;
 import org.elasticsearch.xpack.esql.enrich.EnrichLookupOperator;
 import org.elasticsearch.xpack.esql.execution.PlanExecutor;
 import org.elasticsearch.xpack.esql.execution.PlanExecutor;
 import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery;
 import org.elasticsearch.xpack.esql.querydsl.query.SingleValueQuery;
@@ -63,13 +64,13 @@ import org.elasticsearch.xpack.esql.session.EsqlIndexResolver;
 import org.elasticsearch.xpack.esql.type.EsqlDataTypeRegistry;
 import org.elasticsearch.xpack.esql.type.EsqlDataTypeRegistry;
 
 
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.List;
 import java.util.List;
 import java.util.Objects;
 import java.util.Objects;
 import java.util.Set;
 import java.util.Set;
 import java.util.function.Predicate;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
-import java.util.stream.Stream;
 
 
 public class EsqlPlugin extends Plugin implements ActionPlugin {
 public class EsqlPlugin extends Plugin implements ActionPlugin {
 
 
@@ -172,26 +173,25 @@ public class EsqlPlugin extends Plugin implements ActionPlugin {
 
 
     @Override
     @Override
     public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
     public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
-        return Stream.concat(
-            List.of(
-                DriverStatus.ENTRY,
-                AbstractPageMappingOperator.Status.ENTRY,
-                AbstractPageMappingToIteratorOperator.Status.ENTRY,
-                AggregationOperator.Status.ENTRY,
-                ExchangeSinkOperator.Status.ENTRY,
-                ExchangeSourceOperator.Status.ENTRY,
-                HashAggregationOperator.Status.ENTRY,
-                LimitOperator.Status.ENTRY,
-                LuceneOperator.Status.ENTRY,
-                TopNOperatorStatus.ENTRY,
-                MvExpandOperator.Status.ENTRY,
-                ValuesSourceReaderOperator.Status.ENTRY,
-                SingleValueQuery.ENTRY,
-                AsyncOperator.Status.ENTRY,
-                EnrichLookupOperator.Status.ENTRY
-            ).stream(),
-            Block.getNamedWriteables().stream()
-        ).toList();
+        List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
+        entries.add(DriverStatus.ENTRY);
+        entries.add(AbstractPageMappingOperator.Status.ENTRY);
+        entries.add(AbstractPageMappingToIteratorOperator.Status.ENTRY);
+        entries.add(AggregationOperator.Status.ENTRY);
+        entries.add(ExchangeSinkOperator.Status.ENTRY);
+        entries.add(ExchangeSourceOperator.Status.ENTRY);
+        entries.add(HashAggregationOperator.Status.ENTRY);
+        entries.add(LimitOperator.Status.ENTRY);
+        entries.add(LuceneOperator.Status.ENTRY);
+        entries.add(TopNOperatorStatus.ENTRY);
+        entries.add(MvExpandOperator.Status.ENTRY);
+        entries.add(ValuesSourceReaderOperator.Status.ENTRY);
+        entries.add(SingleValueQuery.ENTRY);
+        entries.add(AsyncOperator.Status.ENTRY);
+        entries.add(EnrichLookupOperator.Status.ENTRY);
+        entries.addAll(Block.getNamedWriteables());
+        entries.addAll(EsField.getNamedWriteables());
+        return entries;
     }
     }
 
 
     public List<ExecutorBuilder<?>> getExecutorBuilders(Settings settings) {
     public List<ExecutorBuilder<?>> getExecutorBuilders(Settings settings) {

+ 14 - 13
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/SerializationTestUtils.java

@@ -25,6 +25,7 @@ import org.elasticsearch.index.query.WildcardQueryBuilder;
 import org.elasticsearch.test.EqualsHashCodeTestUtils;
 import org.elasticsearch.test.EqualsHashCodeTestUtils;
 import org.elasticsearch.xpack.esql.core.expression.Expression;
 import org.elasticsearch.xpack.esql.core.expression.Expression;
 import org.elasticsearch.xpack.esql.core.plan.logical.LogicalPlan;
 import org.elasticsearch.xpack.esql.core.plan.logical.LogicalPlan;
+import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry;
 import org.elasticsearch.xpack.esql.io.stream.PlanNameRegistry;
 import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
 import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
 import org.elasticsearch.xpack.esql.io.stream.PlanStreamOutput;
 import org.elasticsearch.xpack.esql.io.stream.PlanStreamOutput;
@@ -34,6 +35,7 @@ import org.elasticsearch.xpack.esql.session.EsqlConfiguration;
 
 
 import java.io.IOException;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.io.UncheckedIOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
 public class SerializationTestUtils {
 public class SerializationTestUtils {
@@ -101,18 +103,17 @@ public class SerializationTestUtils {
     }
     }
 
 
     public static NamedWriteableRegistry writableRegistry() {
     public static NamedWriteableRegistry writableRegistry() {
-        return new NamedWriteableRegistry(
-            List.of(
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, TermQueryBuilder.NAME, TermQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, TermsQueryBuilder.NAME, TermsQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, MatchAllQueryBuilder.NAME, MatchAllQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, RangeQueryBuilder.NAME, RangeQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, BoolQueryBuilder.NAME, BoolQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, WildcardQueryBuilder.NAME, WildcardQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, RegexpQueryBuilder.NAME, RegexpQueryBuilder::new),
-                new NamedWriteableRegistry.Entry(QueryBuilder.class, ExistsQueryBuilder.NAME, ExistsQueryBuilder::new),
-                SingleValueQuery.ENTRY
-            )
-        );
+        List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, TermQueryBuilder.NAME, TermQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, TermsQueryBuilder.NAME, TermsQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, MatchAllQueryBuilder.NAME, MatchAllQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, RangeQueryBuilder.NAME, RangeQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, BoolQueryBuilder.NAME, BoolQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, WildcardQueryBuilder.NAME, WildcardQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, RegexpQueryBuilder.NAME, RegexpQueryBuilder::new));
+        entries.add(new NamedWriteableRegistry.Entry(QueryBuilder.class, ExistsQueryBuilder.NAME, ExistsQueryBuilder::new));
+        entries.add(SingleValueQuery.ENTRY);
+        entries.addAll(EsField.getNamedWriteables());
+        return new NamedWriteableRegistry(entries);
     }
     }
 }
 }

+ 0 - 66
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/io/stream/PlanNamedTypesTests.java

@@ -34,7 +34,6 @@ import org.elasticsearch.xpack.esql.core.plan.logical.OrderBy;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.core.type.DataType;
 import org.elasticsearch.xpack.esql.core.type.DataType;
 import org.elasticsearch.xpack.esql.core.type.DataTypes;
 import org.elasticsearch.xpack.esql.core.type.DataTypes;
-import org.elasticsearch.xpack.esql.core.type.DateEsField;
 import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.core.type.EsField;
 import org.elasticsearch.xpack.esql.core.type.InvalidMappedField;
 import org.elasticsearch.xpack.esql.core.type.InvalidMappedField;
 import org.elasticsearch.xpack.esql.core.type.KeywordEsField;
 import org.elasticsearch.xpack.esql.core.type.KeywordEsField;
@@ -266,66 +265,6 @@ public class PlanNamedTypesTests extends ESTestCase {
         Stream.generate(PlanNamedTypesTests::randomFieldAttribute).limit(100).forEach(PlanNamedTypesTests::assertNamedExpression);
         Stream.generate(PlanNamedTypesTests::randomFieldAttribute).limit(100).forEach(PlanNamedTypesTests::assertNamedExpression);
     }
     }
 
 
-    public void testKeywordEsFieldSimple() throws IOException {
-        var orig = new KeywordEsField(
-            "BarKeyField", // name
-            Map.of(), // no properties
-            true, // hasDocValues
-            5, // precision
-            true, // normalized
-            true // alias
-        );
-        BytesStreamOutput bso = new BytesStreamOutput();
-        PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry, null);
-        PlanNamedTypes.writeKeywordEsField(out, orig);
-        var deser = PlanNamedTypes.readKeywordEsField(planStreamInput(bso));
-        EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser);
-    }
-
-    public void testKeywordEsField() {
-        Stream.generate(PlanNamedTypesTests::randomKeywordEsField).limit(100).forEach(PlanNamedTypesTests::assertNamedEsField);
-    }
-
-    public void testTextdEsFieldSimple() throws IOException {
-        var orig = new TextEsField(
-            "BarKeyField", // name
-            Map.of(), // no properties
-            true, // hasDocValues
-            true // alias
-        );
-        BytesStreamOutput bso = new BytesStreamOutput();
-        PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry, null);
-        PlanNamedTypes.writeTextEsField(out, orig);
-        var deser = PlanNamedTypes.readTextEsField(planStreamInput(bso));
-        EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser);
-    }
-
-    public void testTextEsField() {
-        Stream.generate(PlanNamedTypesTests::randomTextEsField).limit(100).forEach(PlanNamedTypesTests::assertNamedEsField);
-    }
-
-    public void testInvalidMappedFieldSimple() throws IOException {
-        var orig = new InvalidMappedField("foo", "bar");
-        BytesStreamOutput bso = new BytesStreamOutput();
-        PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry, null);
-        PlanNamedTypes.writeInvalidMappedField(out, orig);
-        var deser = PlanNamedTypes.readInvalidMappedField(planStreamInput(bso));
-        EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser);
-    }
-
-    public void testInvalidMappedField() {
-        Stream.generate(PlanNamedTypesTests::randomInvalidMappedField).limit(100).forEach(PlanNamedTypesTests::assertNamedEsField);
-    }
-
-    public void testEsDateFieldSimple() throws IOException {
-        var orig = DateEsField.dateEsField("birth_date", Map.of(), false);
-        BytesStreamOutput bso = new BytesStreamOutput();
-        PlanStreamOutput out = new PlanStreamOutput(bso, planNameRegistry, null);
-        PlanNamedTypes.writeDateEsField(out, orig);
-        var deser = PlanNamedTypes.readDateEsField(planStreamInput(bso));
-        EqualsHashCodeTestUtils.checkEqualsAndHashCode(orig, unused -> deser);
-    }
-
     public void testBinComparisonSimple() throws IOException {
     public void testBinComparisonSimple() throws IOException {
         var orig = new Equals(Source.EMPTY, field("foo", DataTypes.DOUBLE), field("bar", DataTypes.DOUBLE));
         var orig = new Equals(Source.EMPTY, field("foo", DataTypes.DOUBLE), field("bar", DataTypes.DOUBLE));
         BytesStreamOutput bso = new BytesStreamOutput();
         BytesStreamOutput bso = new BytesStreamOutput();
@@ -516,11 +455,6 @@ public class PlanNamedTypesTests extends ESTestCase {
         EqualsHashCodeTestUtils.checkEqualsAndHashCode(origObj, unused -> deserObj);
         EqualsHashCodeTestUtils.checkEqualsAndHashCode(origObj, unused -> deserObj);
     }
     }
 
 
-    private static void assertNamedEsField(EsField origObj) {
-        var deserObj = serializeDeserialize(origObj, (o, v) -> o.writeNamed(EsField.class, v), PlanStreamInput::readEsFieldNamed);
-        EqualsHashCodeTestUtils.checkEqualsAndHashCode(origObj, unused -> deserObj);
-    }
-
     static EsIndex randomEsIndex() {
     static EsIndex randomEsIndex() {
         return new EsIndex(
         return new EsIndex(
             randomAlphaOfLength(randomIntBetween(1, 25)),
             randomAlphaOfLength(randomIntBetween(1, 25)),

+ 1 - 0
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/DataNodeRequestTests.java

@@ -59,6 +59,7 @@ public class DataNodeRequestTests extends AbstractWireSerializingTestCase<DataNo
         List<NamedWriteableRegistry.Entry> writeables = new ArrayList<>();
         List<NamedWriteableRegistry.Entry> writeables = new ArrayList<>();
         writeables.addAll(new SearchModule(Settings.EMPTY, List.of()).getNamedWriteables());
         writeables.addAll(new SearchModule(Settings.EMPTY, List.of()).getNamedWriteables());
         writeables.addAll(Block.getNamedWriteables());
         writeables.addAll(Block.getNamedWriteables());
+        writeables.addAll(EsField.getNamedWriteables());
         return new NamedWriteableRegistry(writeables);
         return new NamedWriteableRegistry(writeables);
     }
     }