Browse Source

Move runtime fields stats to server (#69487)

Runtime fields usage is currently reported as part of the xpack feature usage API. Now that runtime fields are part of server, their corresponding stats can be moved to be part of the ordinary mapping stats exposed by the cluster stats API.
Luca Cavanna 4 years ago
parent
commit
ffe61fb097
22 changed files with 581 additions and 803 deletions
  1. 69 1
      docs/reference/cluster/stats.asciidoc
  2. 0 4
      docs/reference/rest-api/info.asciidoc
  3. 0 5
      docs/reference/rest-api/usage.asciidoc
  4. 83 0
      rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yml
  5. 82 4
      server/src/main/java/org/elasticsearch/action/admin/cluster/stats/MappingStats.java
  6. 17 0
      server/src/main/java/org/elasticsearch/action/admin/cluster/stats/MappingVisitor.java
  7. 145 0
      server/src/main/java/org/elasticsearch/action/admin/cluster/stats/RuntimeFieldStats.java
  8. 143 11
      server/src/test/java/org/elasticsearch/action/admin/cluster/stats/MappingStatsTests.java
  9. 32 0
      server/src/test/java/org/elasticsearch/action/admin/cluster/stats/MappingVisitorTests.java
  10. 2 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java
  11. 0 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java
  12. 1 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java
  13. 0 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java
  14. 5 259
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/runtimefields/RuntimeFieldsFeatureSetUsage.java
  15. 0 150
      x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/runtimefields/RuntimeFieldsFeatureSetUsageTests.java
  16. 2 1
      x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java
  17. 0 21
      x-pack/plugin/runtime-fields/build.gradle
  18. 0 28
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFields.java
  19. 0 37
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFieldsInfoTransportAction.java
  20. 0 55
      x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFieldsUsageTransportAction.java
  21. 0 2
      x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java
  22. 0 217
      x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/200_runtime_fields_stats.yml

+ 69 - 1
docs/reference/cluster/stats.asciidoc

@@ -492,6 +492,72 @@ Number of fields mapped to the field data type in selected nodes.
 `index_count`::
 (integer)
 Number of indices containing a mapping of the field data type in selected nodes.
+======
+
+`runtime_field_types`::
+(array of objects)
+Contains statistics about <<runtime-mapping-fields, runtime field data types>> used in selected
+nodes.
++
+.Properties of `runtime_field_types` objects
+[%collapsible%open]
+======
+`name`::
+(string)
+Field data type used in selected nodes.
+
+`count`::
+(integer)
+Number of runtime fields mapped to the field data type in selected nodes.
+
+`index_count`::
+(integer)
+Number of indices containing a mapping of the runtime field data type in selected nodes.
+
+`scriptless_count`::
+(integer)
+Number of runtime fields that don't declare a script.
+
+`shadowed_count`::
+(integer)
+Number of runtime fields that shadow an indexed field.
+
+`lang`::
+(array of strings)
+Script languages used for the runtime fields scripts
+
+`lines_max`::
+(integer)
+Maximum number of lines for a single runtime field script
+
+`lines_total`::
+(integer)
+Total number of lines for the scripts that define the current runtime field data type
+
+`chars_max`::
+(integer)
+Maximum number of characters for a single runtime field script
+
+`chars_total`::
+(integer)
+Total number of characters for the scripts that define the current runtime field data type
+
+`source_max`::
+(integer)
+Maximum number of accesses to _source for a single runtime field script
+
+`source_total`::
+(integer)
+Total number of accesses to _source for the scripts that define the current runtime field data type
+
+`doc_max`::
+(integer)
+Maximum number of accesses to doc_values for a single runtime field script
+
+`doc_total`::
+(integer)
+Total number of accesses to doc_values for the scripts that define the current runtime field data type
+
 ======
 =====
 
@@ -1220,7 +1286,8 @@ The API returns the following response:
          "file_sizes": {}
       },
       "mappings": {
-        "field_types": []
+        "field_types": [],
+        "runtime_field_types": []
       },
       "analysis": {
         "char_filter_types": [],
@@ -1363,6 +1430,7 @@ The API returns the following response:
 // TESTRESPONSE[s/"count": \{[^\}]*\}/"count": $body.$_path/]
 // TESTRESPONSE[s/"packaging_types": \[[^\]]*\]/"packaging_types": $body.$_path/]
 // TESTRESPONSE[s/"field_types": \[[^\]]*\]/"field_types": $body.$_path/]
+// TESTRESPONSE[s/"runtime_field_types": \[[^\]]*\]/"runtime_field_types": $body.$_path/]
 // TESTRESPONSE[s/: true|false/: $body.$_path/]
 // TESTRESPONSE[s/: (\-)?[0-9]+/: $body.$_path/]
 // TESTRESPONSE[s/: "[^"]*"/: $body.$_path/]

+ 0 - 4
docs/reference/rest-api/info.asciidoc

@@ -114,10 +114,6 @@ Example response:
          "available": true,
          "enabled": true
       },
-      "runtime_fields": {
-         "available": true,
-         "enabled": true
-      },
       "searchable_snapshots" : {
          "available" : true,
          "enabled" : true

+ 0 - 5
docs/reference/rest-api/usage.asciidoc

@@ -392,11 +392,6 @@ GET /_xpack/usage
   "aggregate_metric" : {
     "available" : true,
     "enabled" : true
-  },
-  "runtime_fields" : {
-    "available" : true,
-    "enabled" : true,
-    "field_types" : []
   }
 }
 ------------------------------------------------------------

+ 83 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/cluster.stats/10_basic.yml

@@ -83,3 +83,86 @@
       cluster.stats: {}
 
   - is_true: nodes.packaging_types
+---
+"get cluster stats without runtime fields":
+  - skip:
+      version: " - 7.99.99"
+      reason:  "cluster stats includes runtime fields from 8.0 on"
+  - do:
+      indices.create:
+        index: sensor
+
+  - do: {cluster.stats: {}}
+  - length: { indices.mappings.field_types: 0 }
+  - length: { indices.mappings.runtime_field_types: 0 }
+
+---
+"Usage stats with script-less runtime fields":
+  - skip:
+      version: " - 7.99.99"
+      reason:  "cluster stats includes runtime fields from 8.0 on"
+  - do:
+      indices.create:
+        index: sensor
+        body:
+          mappings:
+            runtime:
+              message_from_source:
+                type: keyword
+              bad_map:
+                type: double    # shadows the bad_map field in properties
+              message.text:
+                type: keyword   # shadows the message.text subfield in properties
+            properties:
+              message:
+                type: keyword
+                fields:
+                  text:
+                    type: text
+              bad_map:
+                type: long
+
+  - do: {cluster.stats: {}}
+  - length: { indices.mappings.field_types: 3 }
+
+  - match: { indices.mappings.field_types.0.name: keyword }
+  - match: { indices.mappings.field_types.0.count: 1 }
+  - match: { indices.mappings.field_types.0.index_count: 1 }
+  - match: { indices.mappings.field_types.1.name: long }
+  - match: { indices.mappings.field_types.1.count: 1 }
+  - match: { indices.mappings.field_types.1.index_count: 1 }
+  - match: { indices.mappings.field_types.2.name: text }
+  - match: { indices.mappings.field_types.2.count: 1 }
+  - match: { indices.mappings.field_types.2.index_count: 1 }
+
+
+  - length: { indices.mappings.runtime_field_types: 2 }
+
+  - match: { indices.mappings.runtime_field_types.0.name: double }
+  - match: { indices.mappings.runtime_field_types.0.count: 1 }
+  - match: { indices.mappings.runtime_field_types.0.index_count: 1 }
+  - match: { indices.mappings.runtime_field_types.0.scriptless_count: 1 }
+  - match: { indices.mappings.runtime_field_types.0.shadowed_count: 1 }
+  - match: { indices.mappings.runtime_field_types.0.source_max: 0 }
+  - match: { indices.mappings.runtime_field_types.0.source_total: 0 }
+  - match: { indices.mappings.runtime_field_types.0.lines_max:  0 }
+  - match: { indices.mappings.runtime_field_types.0.lines_total: 0 }
+  - match: { indices.mappings.runtime_field_types.0.chars_max:  0 }
+  - match: { indices.mappings.runtime_field_types.0.chars_total: 0 }
+  - match: { indices.mappings.runtime_field_types.0.doc_max: 0 }
+  - match: { indices.mappings.runtime_field_types.0.doc_total: 0 }
+
+  - match: { indices.mappings.runtime_field_types.1.name: keyword }
+  - match: { indices.mappings.runtime_field_types.1.count: 2 }
+  - match: { indices.mappings.runtime_field_types.1.index_count: 1 }
+  - match: { indices.mappings.runtime_field_types.1.scriptless_count: 2 }
+  - match: { indices.mappings.runtime_field_types.1.shadowed_count: 1 }
+  - match: { indices.mappings.runtime_field_types.1.source_max: 0 }
+  - match: { indices.mappings.runtime_field_types.1.source_total: 0 }
+  - match: { indices.mappings.runtime_field_types.1.lines_max:  0 }
+  - match: { indices.mappings.runtime_field_types.1.lines_total: 0 }
+  - match: { indices.mappings.runtime_field_types.1.chars_max:  0 }
+  - match: { indices.mappings.runtime_field_types.1.chars_total: 0 }
+  - match: { indices.mappings.runtime_field_types.1.doc_max: 0 }
+  - match: { indices.mappings.runtime_field_types.1.doc_total: 0 }
+

+ 82 - 4
server/src/main/java/org/elasticsearch/action/admin/cluster/stats/MappingStats.java

@@ -8,6 +8,7 @@
 
 package org.elasticsearch.action.admin.cluster.stats;
 
+import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.MappingMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
@@ -28,7 +29,10 @@ import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Usage statistics about mappings usage.
@@ -40,6 +44,8 @@ public final class MappingStats implements ToXContentFragment, Writeable {
      */
     public static MappingStats of(Metadata metadata, Runnable ensureNotCancelled) {
         Map<String, IndexFeatureStats> fieldTypes = new HashMap<>();
+        Set<String> concreteFieldNames = new HashSet<>();
+        Map<String, RuntimeFieldStats> runtimeFieldTypes = new HashMap<>();
         for (IndexMetadata indexMetadata : metadata) {
             ensureNotCancelled.run();
             if (indexMetadata.isSystem()) {
@@ -48,9 +54,11 @@ public final class MappingStats implements ToXContentFragment, Writeable {
                 continue;
             }
             Set<String> indexFieldTypes = new HashSet<>();
+            Set<String> indexRuntimeFieldTypes = new HashSet<>();
             MappingMetadata mappingMetadata = indexMetadata.mapping();
             if (mappingMetadata != null) {
                 MappingVisitor.visitMapping(mappingMetadata.getSourceAsMap(), (field, fieldMapping) -> {
+                    concreteFieldNames.add(field);
                     String type = null;
                     Object typeO = fieldMapping.get("type");
                     if (typeO != null) {
@@ -67,26 +75,83 @@ public final class MappingStats implements ToXContentFragment, Writeable {
                         }
                     }
                 });
+
+                MappingVisitor.visitRuntimeMapping(mappingMetadata.getSourceAsMap(), (field, fieldMapping) -> {
+                    Object typeObject = fieldMapping.get("type");
+                    if (typeObject == null) {
+                        return;
+                    }
+                    String type = typeObject.toString();
+                    RuntimeFieldStats stats = runtimeFieldTypes.computeIfAbsent(type, RuntimeFieldStats::new);
+                    stats.count++;
+                    if (indexRuntimeFieldTypes.add(type)) {
+                        stats.indexCount++;
+                    }
+                    if (concreteFieldNames.contains(field)) {
+                        stats.shadowedCount++;
+                    }
+                    Object scriptObject = fieldMapping.get("script");
+                    if (scriptObject == null) {
+                        stats.scriptLessCount++;
+                    } else if (scriptObject instanceof Map) {
+                        Map<?, ?> script = (Map<?, ?>) scriptObject;
+                        Object sourceObject = script.get("source");
+                        if (sourceObject != null) {
+                            String scriptSource = sourceObject.toString();
+                            int chars = scriptSource.length();
+                            long lines = scriptSource.lines().count();
+                            int docUsages = countOccurrences(scriptSource, "doc[\\[\\.]");
+                            int sourceUsages = countOccurrences(scriptSource, "params\\._source");
+                            stats.update(chars, lines, sourceUsages, docUsages);
+                        }
+                        Object langObject = script.get("lang");
+                        if (langObject != null) {
+                            stats.scriptLangs.add(langObject.toString());
+                        }
+                    }
+                });
             }
         }
-        return new MappingStats(fieldTypes.values());
+        return new MappingStats(fieldTypes.values(), runtimeFieldTypes.values());
+    }
+
+    private static int countOccurrences(String script, String keyword) {
+        int occurrences = 0;
+        Pattern pattern = Pattern.compile(keyword);
+        Matcher matcher = pattern.matcher(script);
+        while (matcher.find()) {
+            occurrences++;
+        }
+        return occurrences;
     }
 
     private final Set<IndexFeatureStats> fieldTypeStats;
+    private final Set<RuntimeFieldStats> runtimeFieldTypeStats;
 
-    MappingStats(Collection<IndexFeatureStats> fieldTypeStats) {
+    MappingStats(Collection<IndexFeatureStats> fieldTypeStats, Collection<RuntimeFieldStats> runtimeFieldTypeStats) {
         List<IndexFeatureStats> stats = new ArrayList<>(fieldTypeStats);
         stats.sort(Comparator.comparing(IndexFeatureStats::getName));
         this.fieldTypeStats = Collections.unmodifiableSet(new LinkedHashSet<IndexFeatureStats>(stats));
+        List<RuntimeFieldStats> runtimeStats = new ArrayList<>(runtimeFieldTypeStats);
+        runtimeStats.sort(Comparator.comparing(RuntimeFieldStats::type));
+        this.runtimeFieldTypeStats = Collections.unmodifiableSet(new LinkedHashSet<>(runtimeStats));
     }
 
     MappingStats(StreamInput in) throws IOException {
         fieldTypeStats = Collections.unmodifiableSet(new LinkedHashSet<>(in.readList(IndexFeatureStats::new)));
+        if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
+            runtimeFieldTypeStats = Collections.unmodifiableSet(new LinkedHashSet<>(in.readList(RuntimeFieldStats::new)));
+        } else {
+            runtimeFieldTypeStats = Collections.emptySet();
+        }
     }
 
     @Override
     public void writeTo(StreamOutput out) throws IOException {
         out.writeCollection(fieldTypeStats);
+        if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
+            out.writeCollection(runtimeFieldTypeStats);
+        }
     }
 
     /**
@@ -96,6 +161,13 @@ public final class MappingStats implements ToXContentFragment, Writeable {
         return fieldTypeStats;
     }
 
+    /**
+     * Return stats about runtime field types.
+     */
+    public Set<RuntimeFieldStats> getRuntimeFieldTypeStats() {
+        return runtimeFieldTypeStats;
+    }
+
     @Override
     public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
         builder.startObject("mappings");
@@ -104,6 +176,11 @@ public final class MappingStats implements ToXContentFragment, Writeable {
             st.toXContent(builder, params);
         }
         builder.endArray();
+        builder.startArray("runtime_field_types");
+        for (RuntimeFieldStats st : runtimeFieldTypeStats) {
+            st.toXContent(builder, params);
+        }
+        builder.endArray();
         builder.endObject();
         return builder;
     }
@@ -119,11 +196,12 @@ public final class MappingStats implements ToXContentFragment, Writeable {
             return false;
         }
         MappingStats that = (MappingStats) o;
-        return fieldTypeStats.equals(that.fieldTypeStats);
+        return fieldTypeStats.equals(that.fieldTypeStats) &&
+            runtimeFieldTypeStats.equals(that.runtimeFieldTypeStats);
     }
 
     @Override
     public int hashCode() {
-        return fieldTypeStats.hashCode();
+        return Objects.hash(fieldTypeStats, runtimeFieldTypeStats);
     }
 }

+ 17 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/stats/MappingVisitor.java

@@ -52,4 +52,21 @@ public final class MappingVisitor {
         }
     }
 
+    public static void visitRuntimeMapping(Map<String, ?> mapping, BiConsumer<String, Map<String, ?>> runtimeFieldMappingConsumer) {
+        Object runtimeObject = mapping.get("runtime");
+        if (runtimeObject instanceof Map == false) {
+            return;
+        }
+        @SuppressWarnings("unchecked")
+        Map<String, ?> runtimeMappings = (Map<String, ?>) runtimeObject;
+        for (String runtimeFieldName : runtimeMappings.keySet()) {
+            Object runtimeFieldMappingObject = runtimeMappings.get(runtimeFieldName);
+            if (runtimeFieldMappingObject instanceof Map == false) {
+                continue;
+            }
+            @SuppressWarnings("unchecked")
+            Map<String, ?> runtimeFieldMapping = (Map<String, ?>) runtimeFieldMappingObject;
+            runtimeFieldMappingConsumer.accept(runtimeFieldName, runtimeFieldMapping);
+        }
+    }
 }

+ 145 - 0
server/src/main/java/org/elasticsearch/action/admin/cluster/stats/RuntimeFieldStats.java

@@ -0,0 +1,145 @@
+/*
+ * 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 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.action.admin.cluster.stats;
+
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.io.stream.Writeable;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+public final class RuntimeFieldStats implements Writeable, ToXContentObject {
+    private final String type;
+    int count = 0;
+    int indexCount = 0;
+    final Set<String> scriptLangs;
+    long scriptLessCount = 0;
+    long shadowedCount = 0;
+    private long maxLines = 0;
+    private long totalLines = 0;
+    private long maxChars = 0;
+    private long totalChars = 0;
+    private long maxSourceUsages = 0;
+    private long totalSourceUsages = 0;
+    private long maxDocUsages = 0;
+    private long totalDocUsages = 0;
+
+    RuntimeFieldStats(String type) {
+        this.type = Objects.requireNonNull(type);
+        this.scriptLangs = new HashSet<>();
+    }
+
+    public RuntimeFieldStats(StreamInput in) throws IOException {
+        this.type = in.readString();
+        this.count = in.readInt();
+        this.indexCount = in.readInt();
+        this.scriptLangs = in.readSet(StreamInput::readString);
+        this.scriptLessCount = in.readLong();
+        this.shadowedCount = in.readLong();
+        this.maxLines = in.readLong();
+        this.totalLines = in.readLong();
+        this.maxChars = in.readLong();
+        this.totalChars = in.readLong();
+        this.maxSourceUsages = in.readLong();
+        this.totalSourceUsages = in.readLong();
+        this.maxDocUsages = in.readLong();
+        this.totalDocUsages = in.readLong();
+    }
+
+    String type() {
+        return type;
+    }
+
+    @Override
+    public void writeTo(StreamOutput out) throws IOException {
+        out.writeString(type);
+        out.writeInt(count);
+        out.writeInt(indexCount);
+        out.writeCollection(scriptLangs, StreamOutput::writeString);
+        out.writeLong(scriptLessCount);
+        out.writeLong(shadowedCount);
+        out.writeLong(maxLines);
+        out.writeLong(totalLines);
+        out.writeLong(maxChars);
+        out.writeLong(totalChars);
+        out.writeLong(maxSourceUsages);
+        out.writeLong(totalSourceUsages);
+        out.writeLong(maxDocUsages);
+        out.writeLong(totalDocUsages);
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field("name", type);
+        builder.field("count", count);
+        builder.field("index_count", indexCount);
+        builder.field("scriptless_count", scriptLessCount);
+        builder.field("shadowed_count", shadowedCount);
+        builder.array("lang", scriptLangs.toArray(new String[0]));
+        builder.field("lines_max", maxLines);
+        builder.field("lines_total", totalLines);
+        builder.field("chars_max", maxChars);
+        builder.field("chars_total", totalChars);
+        builder.field("source_max", maxSourceUsages);
+        builder.field("source_total", totalSourceUsages);
+        builder.field("doc_max", maxDocUsages);
+        builder.field("doc_total", totalDocUsages);
+        builder.endObject();
+        return builder;
+    }
+
+    void update(int chars, long lines, int sourceUsages, int docUsages) {
+        this.maxChars = Math.max(this.maxChars, chars);
+        this.totalChars += chars;
+        this.maxLines = Math.max(this.maxLines, lines);
+        this.totalLines += lines;
+        this.totalSourceUsages += sourceUsages;
+        this.maxSourceUsages = Math.max(this.maxSourceUsages, sourceUsages);
+        this.totalDocUsages += docUsages;
+        this.maxDocUsages = Math.max(this.maxDocUsages, docUsages);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        RuntimeFieldStats that = (RuntimeFieldStats) o;
+        return count == that.count &&
+            indexCount == that.indexCount &&
+            scriptLessCount == that.scriptLessCount &&
+            shadowedCount == that.shadowedCount &&
+            maxLines == that.maxLines &&
+            totalLines == that.totalLines &&
+            maxChars == that.maxChars &&
+            totalChars == that.totalChars &&
+            maxSourceUsages == that.maxSourceUsages &&
+            totalSourceUsages == that.totalSourceUsages &&
+            maxDocUsages == that.maxDocUsages &&
+            totalDocUsages == that.totalDocUsages &&
+            type.equals(that.type) &&
+            scriptLangs.equals(that.scriptLangs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, count, indexCount, scriptLangs, scriptLessCount, shadowedCount,
+            maxLines, totalLines, maxChars, totalChars,
+            maxSourceUsages, totalSourceUsages, maxDocUsages, totalDocUsages);
+    }
+}

+ 143 - 11
server/src/test/java/org/elasticsearch/action/admin/cluster/stats/MappingStatsTests.java

@@ -11,8 +11,10 @@ package org.elasticsearch.action.admin.cluster.stats;
 import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetadata;
 import org.elasticsearch.cluster.metadata.Metadata;
+import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.io.stream.Writeable.Reader;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.script.Script;
 import org.elasticsearch.tasks.TaskCancelledException;
 import org.elasticsearch.test.AbstractWireSerializingTestCase;
 
@@ -24,6 +26,109 @@ import java.util.List;
 
 public class MappingStatsTests extends AbstractWireSerializingTestCase<MappingStats> {
 
+    public void testToXContent() {
+        Settings settings = Settings.builder()
+            .put("index.number_of_replicas", 0)
+            .put("index.number_of_shards", 1)
+            .put("index.version.created", Version.CURRENT)
+            .build();
+        Script script1 = new Script("doc['field'] + doc.field + params._source.field");
+        Script script2 = new Script("doc['field']");
+        Script script3 = new Script("params._source.field + params._source.field \n + params._source.field");
+        Script script4 = new Script("params._source.field");
+        String mapping = "{" +
+            "  \"runtime\" : {" +
+            "    \"keyword1\": {" +
+            "      \"type\": \"keyword\"," +
+            "      \"script\": " + Strings.toString(script1) +
+            "    }," +
+            "    \"keyword2\": {" +
+            "      \"type\": \"keyword\"" +
+            "    }," +
+            "    \"object.keyword3\": {" +
+            "      \"type\": \"keyword\"," +
+            "      \"script\": " + Strings.toString(script2) +
+            "    }," +
+            "    \"long\": {" +
+            "      \"type\": \"long\"," +
+            "      \"script\": " + Strings.toString(script3) +
+            "    }," +
+            "    \"long2\": {" +
+            "      \"type\": \"long\"," +
+            "      \"script\": " + Strings.toString(script4) +
+            "    }" +
+            "  }," +
+            "  \"properties\":{" +
+            "    \"object\":{" +
+            "      \"type\":\"object\"," +
+            "      \"properties\":{" +
+            "         \"keyword3\":{" +
+            "           \"type\": \"keyword\"" +
+            "         }" +
+            "      }" +
+            "    }" +
+            "  }" +
+            "}";
+        IndexMetadata meta = IndexMetadata.builder("index").settings(settings).putMapping(mapping).build();
+        IndexMetadata meta2 = IndexMetadata.builder("index2").settings(settings).putMapping(mapping).build();
+        Metadata metadata = Metadata.builder().put(meta, false).put(meta2, false).build();
+        MappingStats mappingStats = MappingStats.of(metadata, () -> {});
+        assertEquals("{\n" +
+            "  \"mappings\" : {\n" +
+            "    \"field_types\" : [\n" +
+            "      {\n" +
+            "        \"name\" : \"keyword\",\n" +
+            "        \"count\" : 2,\n" +
+            "        \"index_count\" : 2\n" +
+            "      },\n" +
+            "      {\n" +
+            "        \"name\" : \"object\",\n" +
+            "        \"count\" : 2,\n" +
+            "        \"index_count\" : 2\n" +
+            "      }\n" +
+            "    ],\n" +
+            "    \"runtime_field_types\" : [\n" +
+            "      {\n" +
+            "        \"name\" : \"keyword\",\n" +
+            "        \"count\" : 6,\n" +
+            "        \"index_count\" : 2,\n" +
+            "        \"scriptless_count\" : 2,\n" +
+            "        \"shadowed_count\" : 2,\n" +
+            "        \"lang\" : [\n" +
+            "          \"painless\"\n" +
+            "        ],\n" +
+            "        \"lines_max\" : 1,\n" +
+            "        \"lines_total\" : 4,\n" +
+            "        \"chars_max\" : 47,\n" +
+            "        \"chars_total\" : 118,\n" +
+            "        \"source_max\" : 1,\n" +
+            "        \"source_total\" : 2,\n" +
+            "        \"doc_max\" : 2,\n" +
+            "        \"doc_total\" : 6\n" +
+            "      },\n" +
+            "      {\n" +
+            "        \"name\" : \"long\",\n" +
+            "        \"count\" : 4,\n" +
+            "        \"index_count\" : 2,\n" +
+            "        \"scriptless_count\" : 0,\n" +
+            "        \"shadowed_count\" : 0,\n" +
+            "        \"lang\" : [\n" +
+            "          \"painless\"\n" +
+            "        ],\n" +
+            "        \"lines_max\" : 2,\n" +
+            "        \"lines_total\" : 6,\n" +
+            "        \"chars_max\" : 68,\n" +
+            "        \"chars_total\" : 176,\n" +
+            "        \"source_max\" : 3,\n" +
+            "        \"source_total\" : 8,\n" +
+            "        \"doc_max\" : 0,\n" +
+            "        \"doc_total\" : 0\n" +
+            "      }\n" +
+            "    ]\n" +
+            "  }\n" +
+            "}", Strings.toString(mappingStats, true, true));
+    }
+
     @Override
     protected Reader<MappingStats> instanceReader() {
         return MappingStats::new;
@@ -32,6 +137,7 @@ public class MappingStatsTests extends AbstractWireSerializingTestCase<MappingSt
     @Override
     protected MappingStats createTestInstance() {
         Collection<IndexFeatureStats> stats = new ArrayList<>();
+        Collection<RuntimeFieldStats> runtimeFieldStats = new ArrayList<>();
         if (randomBoolean()) {
             IndexFeatureStats s = new IndexFeatureStats("keyword");
             s.count = 10;
@@ -44,23 +150,49 @@ public class MappingStatsTests extends AbstractWireSerializingTestCase<MappingSt
             s.indexCount = 3;
             stats.add(s);
         }
-        return new MappingStats(stats);
+        if (randomBoolean()) {
+            runtimeFieldStats.add(randomRuntimeFieldStats("keyword"));
+        }
+        if (randomBoolean()) {
+            runtimeFieldStats.add(randomRuntimeFieldStats("long"));
+        }
+        return new MappingStats(stats, runtimeFieldStats);
+    }
+
+    private static RuntimeFieldStats randomRuntimeFieldStats(String type) {
+        RuntimeFieldStats stats = new RuntimeFieldStats(type);
+        if (randomBoolean()) {
+            stats.update(randomIntBetween(1, 100), randomLongBetween(100, 1000), randomIntBetween(1, 10), randomIntBetween(1, 10));
+        }
+        return stats;
     }
 
     @Override
     protected MappingStats mutateInstance(MappingStats instance) throws IOException {
         List<IndexFeatureStats> fieldTypes = new ArrayList<>(instance.getFieldTypeStats());
-        boolean remove = fieldTypes.size() > 0 && randomBoolean();
-        if (remove) {
-            fieldTypes.remove(randomInt(fieldTypes.size() - 1));
-        }
-        if (remove == false || randomBoolean()) {
-            IndexFeatureStats s = new IndexFeatureStats("float");
-            s.count = 13;
-            s.indexCount = 2;
-            fieldTypes.add(s);
+        List<RuntimeFieldStats> runtimeFieldTypes = new ArrayList<>(instance.getRuntimeFieldTypeStats());
+        if (randomBoolean()) {
+            boolean remove = fieldTypes.size() > 0 && randomBoolean();
+            if (remove) {
+                fieldTypes.remove(randomInt(fieldTypes.size() - 1));
+            }
+            if (remove == false || randomBoolean()) {
+                IndexFeatureStats s = new IndexFeatureStats("float");
+                s.count = 13;
+                s.indexCount = 2;
+                fieldTypes.add(s);
+            }
+        } else {
+            boolean remove = runtimeFieldTypes.size() > 0 && randomBoolean();
+            if (remove) {
+                runtimeFieldTypes.remove(randomInt(runtimeFieldTypes.size() - 1));
+            }
+            if (remove == false || randomBoolean()) {
+                runtimeFieldTypes.add(randomRuntimeFieldStats("double"));
+            }
         }
-        return new MappingStats(fieldTypes);
+
+        return new MappingStats(fieldTypes, runtimeFieldTypes);
     }
 
     public void testAccountsRegularIndices() {

+ 32 - 0
server/src/test/java/org/elasticsearch/action/admin/cluster/stats/MappingVisitorTests.java

@@ -104,4 +104,36 @@ public class MappingVisitorTests extends ESTestCase {
         collectTypes(mapping, fields);
         assertEquals(new HashSet<>(Arrays.asList("keyword", "object")), fields);
     }
+
+    public void testCountRuntimeFields() {
+        Map<String, Object> mapping = new HashMap<>();
+        Set<String> fields = new HashSet<>();
+        collectRuntimeTypes(mapping, fields);
+        assertEquals(Collections.emptySet(), fields);
+
+        Map<String, Object> properties = new HashMap<>();
+        mapping.put("runtime", properties);
+
+        Map<String, Object> keywordField = new HashMap<>();
+        keywordField.put("type", "keyword");
+        properties.put("foo", keywordField);
+        collectRuntimeTypes(mapping, fields);
+        assertEquals(Collections.singleton("keyword"), fields);
+
+        Map<String, Object> runtimeField = new HashMap<>();
+        runtimeField.put("type", "long");
+        properties.put("bar", runtimeField);
+        fields = new HashSet<>();
+        collectRuntimeTypes(mapping, fields);
+        assertEquals(new HashSet<>(Arrays.asList("keyword", "long")), fields);
+
+        properties.put("baz", runtimeField);
+        fields = new HashSet<>();
+        collectRuntimeTypes(mapping, fields);
+        assertEquals(new HashSet<>(Arrays.asList("keyword", "long")), fields);
+    }
+
+    private static void collectRuntimeTypes(Map<String, ?> mapping, Set<String> types) {
+        MappingVisitor.visitRuntimeMapping(mapping, (f, m) -> types.add(m.get("type").toString()));
+    }
 }

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackClientPlugin.java

@@ -92,7 +92,6 @@ import org.elasticsearch.xpack.core.ml.action.DeleteTrainedModelAction;
 import org.elasticsearch.xpack.core.ml.action.EvaluateDataFrameAction;
 import org.elasticsearch.xpack.core.ml.action.ExplainDataFrameAnalyticsAction;
 import org.elasticsearch.xpack.core.ml.action.FinalizeJobExecutionAction;
-import org.elasticsearch.xpack.core.rollup.action.RollupIndexerAction;
 import org.elasticsearch.xpack.core.ml.action.FlushJobAction;
 import org.elasticsearch.xpack.core.ml.action.ForecastJobAction;
 import org.elasticsearch.xpack.core.ml.action.GetBucketsAction;
@@ -153,6 +152,7 @@ import org.elasticsearch.xpack.core.rollup.action.GetRollupCapsAction;
 import org.elasticsearch.xpack.core.rollup.action.GetRollupJobsAction;
 import org.elasticsearch.xpack.core.rollup.action.PutRollupJobAction;
 import org.elasticsearch.xpack.core.rollup.action.RollupAction;
+import org.elasticsearch.xpack.core.rollup.action.RollupIndexerAction;
 import org.elasticsearch.xpack.core.rollup.action.RollupSearchAction;
 import org.elasticsearch.xpack.core.rollup.action.StartRollupJobAction;
 import org.elasticsearch.xpack.core.rollup.action.StopRollupJobAction;
@@ -536,7 +536,7 @@ public class XPackClientPlugin extends Plugin implements ActionPlugin, NetworkPl
             new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.DATA_STREAMS, DataStreamFeatureSetUsage::new),
             // Data Tiers
             new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.DATA_TIERS, DataTiersFeatureSetUsage::new),
-            new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.RUNTIME_FIELDS, RuntimeFieldsFeatureSetUsage::new)
+            new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, "runtime_fields", RuntimeFieldsFeatureSetUsage::new)
         ));
 
         if (RollupV2.isEnabled()) {

+ 0 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/XPackField.java

@@ -68,8 +68,6 @@ public final class XPackField {
     public static final String DATA_TIERS = "data_tiers";
     /** Name constant for the aggregate_metric plugin. */
     public static final String AGGREGATE_METRIC = "aggregate_metric";
-    /** Name constant for the runtime fields plugin. */
-    public static final String RUNTIME_FIELDS = "runtime_fields";
     /** Name constant for the operator privileges feature. */
     public static final String OPERATOR_PRIVILEGES = "operator_privileges";
 

+ 1 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackInfoFeatureAction.java

@@ -48,7 +48,6 @@ public class XPackInfoFeatureAction extends ActionType<XPackInfoFeatureResponse>
     public static final XPackInfoFeatureAction DATA_STREAMS = new XPackInfoFeatureAction(XPackField.DATA_STREAMS);
     public static final XPackInfoFeatureAction DATA_TIERS = new XPackInfoFeatureAction(XPackField.DATA_TIERS);
     public static final XPackInfoFeatureAction AGGREGATE_METRIC = new XPackInfoFeatureAction(XPackField.AGGREGATE_METRIC);
-    public static final XPackInfoFeatureAction RUNTIME_FIELDS = new XPackInfoFeatureAction(XPackField.RUNTIME_FIELDS);
 
     public static final List<XPackInfoFeatureAction> ALL;
     static {
@@ -56,7 +55,7 @@ public class XPackInfoFeatureAction extends ActionType<XPackInfoFeatureResponse>
         actions.addAll(Arrays.asList(
             SECURITY, MONITORING, WATCHER, GRAPH, MACHINE_LEARNING, LOGSTASH, EQL, SQL, ROLLUP, INDEX_LIFECYCLE, SNAPSHOT_LIFECYCLE, CCR,
             TRANSFORM, VECTORS, VOTING_ONLY, FROZEN_INDICES, SPATIAL, ANALYTICS, ENRICH, DATA_STREAMS, SEARCHABLE_SNAPSHOTS, DATA_TIERS,
-            AGGREGATE_METRIC, RUNTIME_FIELDS
+            AGGREGATE_METRIC
         ));
         ALL = Collections.unmodifiableList(actions);
     }

+ 0 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/action/XPackUsageFeatureAction.java

@@ -45,7 +45,6 @@ public class XPackUsageFeatureAction extends ActionType<XPackUsageFeatureRespons
     public static final XPackUsageFeatureAction DATA_STREAMS = new XPackUsageFeatureAction(XPackField.DATA_STREAMS);
     public static final XPackUsageFeatureAction DATA_TIERS = new XPackUsageFeatureAction(XPackField.DATA_TIERS);
     public static final XPackUsageFeatureAction AGGREGATE_METRIC = new XPackUsageFeatureAction(XPackField.AGGREGATE_METRIC);
-    public static final XPackUsageFeatureAction RUNTIME_FIELDS = new XPackUsageFeatureAction(XPackField.RUNTIME_FIELDS);
 
     static final List<XPackUsageFeatureAction> ALL = List.of(
         AGGREGATE_METRIC,
@@ -61,7 +60,6 @@ public class XPackUsageFeatureAction extends ActionType<XPackUsageFeatureRespons
         MACHINE_LEARNING,
         MONITORING,
         ROLLUP,
-        RUNTIME_FIELDS,
         SEARCHABLE_SNAPSHOTS,
         SECURITY,
         SNAPSHOT_LIFECYCLE,

+ 5 - 259
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/runtimefields/RuntimeFieldsFeatureSetUsage.java

@@ -8,283 +8,29 @@
 package org.elasticsearch.xpack.core.runtimefields;
 
 import org.elasticsearch.Version;
-import org.elasticsearch.action.admin.cluster.stats.MappingVisitor;
-import org.elasticsearch.cluster.metadata.IndexMetadata;
-import org.elasticsearch.cluster.metadata.MappingMetadata;
+import org.elasticsearch.action.admin.cluster.stats.RuntimeFieldStats;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
-import org.elasticsearch.common.io.stream.Writeable;
-import org.elasticsearch.common.xcontent.ToXContentObject;
-import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.xpack.core.XPackFeatureSet;
-import org.elasticsearch.xpack.core.XPackField;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class RuntimeFieldsFeatureSetUsage extends XPackFeatureSet.Usage {
 
-    public static RuntimeFieldsFeatureSetUsage fromMetadata(Iterable<IndexMetadata> metadata) {
-        Map<String, RuntimeFieldStats> fieldTypes = new HashMap<>();
-        for (IndexMetadata indexMetadata : metadata) {
-            if (indexMetadata.isSystem()) {
-                // Don't include system indices in statistics about mappings, we care about the user's indices.
-                continue;
-            }
-            Set<String> indexFieldTypes = new HashSet<>();
-            Set<String> concreteFieldNames = new HashSet<>();
-            MappingMetadata mappingMetadata = indexMetadata.mapping();
-            if (mappingMetadata != null) {
-                Map<String, Object> sourceAsMap = mappingMetadata.getSourceAsMap();
-                Object runtimeObject = sourceAsMap.get("runtime");
-                if (runtimeObject instanceof Map == false) {
-                    continue;
-                }
-                @SuppressWarnings("unchecked")
-                Map<String, ?> runtimeMappings = (Map<String, ?>) runtimeObject;
-                MappingVisitor.visitMapping(sourceAsMap, (f, m) -> concreteFieldNames.add(f));
-                for (String runtimeFieldName : runtimeMappings.keySet()) {
-                    Object runtimeFieldMappingObject = runtimeMappings.get(runtimeFieldName);
-                    if (runtimeFieldMappingObject instanceof Map == false) {
-                        continue;
-                    }
-                    Map<?, ?> runtimeFieldMapping = (Map<?, ?>) runtimeFieldMappingObject;
-                    Object typeObject = runtimeFieldMapping.get("type");
-                    if (typeObject == null) {
-                        continue;
-                    }
-                    String type = typeObject.toString();
-                    RuntimeFieldStats stats = fieldTypes.computeIfAbsent(type, RuntimeFieldStats::new);
-                    stats.count++;
-                    if (indexFieldTypes.add(type)) {
-                        stats.indexCount++;
-                    }
-                    if (concreteFieldNames.contains(runtimeFieldName)) {
-                        stats.shadowedCount++;
-                    }
-                    Object scriptObject = runtimeFieldMapping.get("script");
-                    if (scriptObject == null) {
-                        stats.scriptLessCount++;
-                    } else if (scriptObject instanceof Map) {
-                        Map<?, ?> script = (Map<?, ?>) scriptObject;
-                        Object sourceObject = script.get("source");
-                        if (sourceObject != null) {
-                            String scriptSource = sourceObject.toString();
-                            int chars = scriptSource.length();
-                            long lines = scriptSource.lines().count();
-                            int docUsages = countOccurrences(scriptSource, "doc[\\[\\.]");
-                            int sourceUsages = countOccurrences(scriptSource, "params\\._source");
-                            stats.update(chars, lines, sourceUsages, docUsages);
-                        }
-                        Object langObject = script.get("lang");
-                        if (langObject != null) {
-                            stats.scriptLangs.add(langObject.toString());
-                        }
-                    }
-                }
-            }
-        }
-        List<RuntimeFieldStats> runtimeFieldStats = new ArrayList<>(fieldTypes.values());
-        runtimeFieldStats.sort(Comparator.comparing(RuntimeFieldStats::type));
-        return new RuntimeFieldsFeatureSetUsage(Collections.unmodifiableList(runtimeFieldStats));
-    }
-
-    private final List<RuntimeFieldStats> stats;
-
-    RuntimeFieldsFeatureSetUsage(List<RuntimeFieldStats> stats) {
-        super(XPackField.RUNTIME_FIELDS, true, true);
-        this.stats = stats;
-    }
-
-    public RuntimeFieldsFeatureSetUsage(StreamInput in) throws IOException {
-        super(in);
-        this.stats = in.readList(RuntimeFieldStats::new);
+    public RuntimeFieldsFeatureSetUsage(StreamInput input) throws IOException {
+        super(input);
+        input.readList(RuntimeFieldStats::new);
     }
 
     @Override
     public void writeTo(StreamOutput out) throws IOException {
         super.writeTo(out);
-        out.writeList(stats);
-    }
-
-    List<RuntimeFieldStats> getRuntimeFieldStats() {
-        return stats;
-    }
-
-    @Override
-    protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
-        super.innerXContent(builder, params);
-        builder.startArray("field_types");
-        for (RuntimeFieldStats stats : stats) {
-            stats.toXContent(builder, params);
-        }
-        builder.endArray();
+        out.writeList(Collections.emptyList());
     }
 
     @Override
     public Version getMinimalSupportedVersion() {
         return Version.V_7_11_0;
     }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        RuntimeFieldsFeatureSetUsage that = (RuntimeFieldsFeatureSetUsage) o;
-        return stats.equals(that.stats);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(stats);
-    }
-
-    private static int countOccurrences(String script, String keyword) {
-        int occurrences = 0;
-        Pattern pattern = Pattern.compile(keyword);
-        Matcher matcher = pattern.matcher(script);
-        while (matcher.find()) {
-            occurrences++;
-        }
-        return occurrences;
-    }
-
-    static final class RuntimeFieldStats implements Writeable, ToXContentObject {
-        private final String type;
-        private int count = 0;
-        private int indexCount = 0;
-        private final Set<String> scriptLangs;
-        private long scriptLessCount = 0;
-        private long shadowedCount = 0;
-        private long maxLines = 0;
-        private long totalLines = 0;
-        private long maxChars = 0;
-        private long totalChars = 0;
-        private long maxSourceUsages = 0;
-        private long totalSourceUsages = 0;
-        private long maxDocUsages = 0;
-        private long totalDocUsages = 0;
-
-        RuntimeFieldStats(String type) {
-            this.type = Objects.requireNonNull(type);
-            this.scriptLangs = new HashSet<>();
-        }
-
-        RuntimeFieldStats(StreamInput in) throws IOException {
-            this.type = in.readString();
-            this.count = in.readInt();
-            this.indexCount = in.readInt();
-            this.scriptLangs = in.readSet(StreamInput::readString);
-            this.scriptLessCount = in.readLong();
-            this.shadowedCount = in.readLong();
-            this.maxLines = in.readLong();
-            this.totalLines = in.readLong();
-            this.maxChars = in.readLong();
-            this.totalChars = in.readLong();
-            this.maxSourceUsages = in.readLong();
-            this.totalSourceUsages = in.readLong();
-            this.maxDocUsages = in.readLong();
-            this.totalDocUsages = in.readLong();
-        }
-
-        String type() {
-            return type;
-        }
-
-        @Override
-        public void writeTo(StreamOutput out) throws IOException {
-            out.writeString(type);
-            out.writeInt(count);
-            out.writeInt(indexCount);
-            out.writeCollection(scriptLangs, StreamOutput::writeString);
-            out.writeLong(scriptLessCount);
-            out.writeLong(shadowedCount);
-            out.writeLong(maxLines);
-            out.writeLong(totalLines);
-            out.writeLong(maxChars);
-            out.writeLong(totalChars);
-            out.writeLong(maxSourceUsages);
-            out.writeLong(totalSourceUsages);
-            out.writeLong(maxDocUsages);
-            out.writeLong(totalDocUsages);
-        }
-
-        @Override
-        public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
-            builder.startObject();
-            builder.field("name", type);
-            builder.field("count", count);
-            builder.field("index_count", indexCount);
-            builder.field("scriptless_count", scriptLessCount);
-            builder.field("shadowed_count", shadowedCount);
-            builder.array("lang", scriptLangs.toArray(new String[0]));
-            builder.field("lines_max", maxLines);
-            builder.field("lines_total", totalLines);
-            builder.field("chars_max", maxChars);
-            builder.field("chars_total", totalChars);
-            builder.field("source_max", maxSourceUsages);
-            builder.field("source_total", totalSourceUsages);
-            builder.field("doc_max", maxDocUsages);
-            builder.field("doc_total", totalDocUsages);
-            builder.endObject();
-            return builder;
-        }
-
-        void update(int chars, long lines, int sourceUsages, int docUsages) {
-            this.maxChars = Math.max(this.maxChars, chars);
-            this.totalChars += chars;
-            this.maxLines = Math.max(this.maxLines, lines);
-            this.totalLines += lines;
-            this.totalSourceUsages += sourceUsages;
-            this.maxSourceUsages = Math.max(this.maxSourceUsages, sourceUsages);
-            this.totalDocUsages += docUsages;
-            this.maxDocUsages = Math.max(this.maxDocUsages, docUsages);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            RuntimeFieldStats that = (RuntimeFieldStats) o;
-            return count == that.count &&
-                indexCount == that.indexCount &&
-                scriptLessCount == that.scriptLessCount &&
-                shadowedCount == that.shadowedCount &&
-                maxLines == that.maxLines &&
-                totalLines == that.totalLines &&
-                maxChars == that.maxChars &&
-                totalChars == that.totalChars &&
-                maxSourceUsages == that.maxSourceUsages &&
-                totalSourceUsages == that.totalSourceUsages &&
-                maxDocUsages == that.maxDocUsages &&
-                totalDocUsages == that.totalDocUsages &&
-                type.equals(that.type) &&
-                scriptLangs.equals(that.scriptLangs);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(type, count, indexCount, scriptLangs, scriptLessCount, shadowedCount,
-                maxLines, totalLines, maxChars, totalChars,
-                maxSourceUsages, totalSourceUsages, maxDocUsages, totalDocUsages);
-        }
-    }
 }

+ 0 - 150
x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/runtimefields/RuntimeFieldsFeatureSetUsageTests.java

@@ -1,150 +0,0 @@
-/*
- * 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.core.runtimefields;
-
-import org.elasticsearch.Version;
-import org.elasticsearch.cluster.metadata.IndexMetadata;
-import org.elasticsearch.common.Strings;
-import org.elasticsearch.common.io.stream.Writeable;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.script.Script;
-import org.elasticsearch.test.AbstractWireSerializingTestCase;
-import org.elasticsearch.xpack.core.runtimefields.RuntimeFieldsFeatureSetUsage.RuntimeFieldStats;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class RuntimeFieldsFeatureSetUsageTests extends AbstractWireSerializingTestCase<RuntimeFieldsFeatureSetUsage> {
-
-    public void testToXContent() {
-        Settings settings = Settings.builder()
-            .put("index.number_of_replicas", 0)
-            .put("index.number_of_shards", 1)
-            .put("index.version.created", Version.CURRENT)
-            .build();
-        Script script1 = new Script("doc['field'] + doc.field + params._source.field");
-        Script script2 = new Script("doc['field']");
-        Script script3 = new Script("params._source.field + params._source.field \n + params._source.field");
-        Script script4 = new Script("params._source.field");
-        IndexMetadata meta = IndexMetadata.builder("index").settings(settings)
-            .putMapping("{" +
-                "  \"runtime\" : {" +
-                "    \"keyword1\": {" +
-                "      \"type\": \"keyword\"," +
-                "      \"script\": " + Strings.toString(script1) +
-                "    }," +
-                "    \"keyword2\": {" +
-                "      \"type\": \"keyword\"" +
-                "    }," +
-                "    \"object.keyword3\": {" +
-                "      \"type\": \"keyword\"," +
-                "      \"script\": " + Strings.toString(script2) +
-                "    }," +
-                "    \"long\": {" +
-                "      \"type\": \"long\"," +
-                "      \"script\": " + Strings.toString(script3) +
-                "    }," +
-                "    \"long2\": {" +
-                "      \"type\": \"long\"," +
-                "      \"script\": " + Strings.toString(script4) +
-                "    }" +
-                "  }," +
-                "  \"properties\":{" +
-                "    \"object\":{" +
-                "      \"type\":\"object\"," +
-                "      \"properties\":{" +
-                "         \"keyword3\":{" +
-                "           \"type\": \"keyword\"" +
-                "         }" +
-                "      }" +
-                "    }" +
-                "  }" +
-                "}")
-            .build();
-
-        RuntimeFieldsFeatureSetUsage featureSetUsage = RuntimeFieldsFeatureSetUsage.fromMetadata(List.of(meta, meta));
-        assertEquals("{\n" +
-            "  \"available\" : true,\n" +
-            "  \"enabled\" : true,\n" +
-            "  \"field_types\" : [\n" +
-            "    {\n" +
-            "      \"name\" : \"keyword\",\n" +
-            "      \"count\" : 6,\n" +
-            "      \"index_count\" : 2,\n" +
-            "      \"scriptless_count\" : 2,\n" +
-            "      \"shadowed_count\" : 2,\n" +
-            "      \"lang\" : [\n" +
-            "        \"painless\"\n" +
-            "      ],\n" +
-            "      \"lines_max\" : 1,\n" +
-            "      \"lines_total\" : 4,\n" +
-            "      \"chars_max\" : 47,\n" +
-            "      \"chars_total\" : 118,\n" +
-            "      \"source_max\" : 1,\n" +
-            "      \"source_total\" : 2,\n" +
-            "      \"doc_max\" : 2,\n" +
-            "      \"doc_total\" : 6\n" +
-            "    },\n" +
-            "    {\n" +
-            "      \"name\" : \"long\",\n" +
-            "      \"count\" : 4,\n" +
-            "      \"index_count\" : 2,\n" +
-            "      \"scriptless_count\" : 0,\n" +
-            "      \"shadowed_count\" : 0,\n" +
-            "      \"lang\" : [\n" +
-            "        \"painless\"\n" +
-            "      ],\n" +
-            "      \"lines_max\" : 2,\n" +
-            "      \"lines_total\" : 6,\n" +
-            "      \"chars_max\" : 68,\n" +
-            "      \"chars_total\" : 176,\n" +
-            "      \"source_max\" : 3,\n" +
-            "      \"source_total\" : 8,\n" +
-            "      \"doc_max\" : 0,\n" +
-            "      \"doc_total\" : 0\n" +
-            "    }\n" +
-            "  ]\n" +
-            "}", Strings.toString(featureSetUsage, true, true));
-    }
-
-    @Override
-    protected RuntimeFieldsFeatureSetUsage createTestInstance() {
-        int numItems = randomIntBetween(0, 10);
-        List<RuntimeFieldStats> stats = new ArrayList<>(numItems);
-        for (int i = 0; i < numItems; i++) {
-            stats.add(randomRuntimeFieldStats("type" + i));
-        }
-        return new RuntimeFieldsFeatureSetUsage(stats);
-    }
-
-    private static RuntimeFieldStats randomRuntimeFieldStats(String type) {
-        RuntimeFieldStats stats = new RuntimeFieldStats(type);
-        if (randomBoolean()) {
-            stats.update(randomIntBetween(1, 100), randomLongBetween(100, 1000), randomIntBetween(1, 10), randomIntBetween(1, 10));
-        }
-        return stats;
-    }
-
-    @Override
-    protected RuntimeFieldsFeatureSetUsage mutateInstance(RuntimeFieldsFeatureSetUsage instance) throws IOException {
-        List<RuntimeFieldStats> runtimeFieldStats = instance.getRuntimeFieldStats();
-        if (runtimeFieldStats.size() == 0) {
-            return new RuntimeFieldsFeatureSetUsage(Collections.singletonList(randomRuntimeFieldStats("type")));
-        }
-        List<RuntimeFieldStats> mutated = new ArrayList<>(runtimeFieldStats);
-        mutated.remove(randomIntBetween(0, mutated.size() - 1));
-        return new RuntimeFieldsFeatureSetUsage(mutated);
-    }
-
-    @Override
-    protected Writeable.Reader<RuntimeFieldsFeatureSetUsage> instanceReader() {
-        return RuntimeFieldsFeatureSetUsage::new;
-    }
-}

+ 2 - 1
x-pack/plugin/monitoring/src/test/java/org/elasticsearch/xpack/monitoring/collector/cluster/ClusterStatsMonitoringDocTests.java

@@ -456,7 +456,8 @@ public class ClusterStatsMonitoringDocTests extends BaseMonitoringDocTestCase<Cl
                 + "        \"file_sizes\": {}"
                 + "      },"
                 + "      \"mappings\":{"
-                + "        \"field_types\":[]"
+                + "        \"field_types\":[],"
+                + "        \"runtime_field_types\":[]"
                 + "      },"
                 + "      \"analysis\":{"
                 + "        \"char_filter_types\":[],"

+ 0 - 21
x-pack/plugin/runtime-fields/build.gradle

@@ -1,21 +0,0 @@
-apply plugin: 'elasticsearch.esplugin'
-
-esplugin {
-  name 'x-pack-runtime-fields'
-  description 'A module which adds support for runtime fields'
-  classname 'org.elasticsearch.xpack.runtimefields.RuntimeFields'
-  extendedPlugins = ['x-pack-core']
-}
-archivesBaseName = 'x-pack-runtime-fields'
-
-dependencies {
-  compileOnly project(":server")
-  compileOnly project(path: xpackModule('core'))
-}
-
-tasks.named("dependencyLicenses").configure {
-  ignoreSha 'x-pack-core'
-}
-
-//this plugin is here only for the painless extension, there are no unit tests
-tasks.named("testingConventions").configure { enabled = false }

+ 0 - 28
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFields.java

@@ -1,28 +0,0 @@
-/*
- * 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.runtimefields;
-
-import org.elasticsearch.action.ActionRequest;
-import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.plugins.ActionPlugin;
-import org.elasticsearch.plugins.Plugin;
-import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction;
-import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction;
-
-import java.util.List;
-
-public final class RuntimeFields extends Plugin implements ActionPlugin {
-
-    @Override
-    public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
-        return List.of(
-            new ActionPlugin.ActionHandler<>(XPackUsageFeatureAction.RUNTIME_FIELDS, RuntimeFieldsUsageTransportAction.class),
-            new ActionPlugin.ActionHandler<>(XPackInfoFeatureAction.RUNTIME_FIELDS, RuntimeFieldsInfoTransportAction.class)
-        );
-    }
-}

+ 0 - 37
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFieldsInfoTransportAction.java

@@ -1,37 +0,0 @@
-/*
- * 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.runtimefields;
-
-import org.elasticsearch.action.support.ActionFilters;
-import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.core.XPackField;
-import org.elasticsearch.xpack.core.action.XPackInfoFeatureAction;
-import org.elasticsearch.xpack.core.action.XPackInfoFeatureTransportAction;
-
-public class RuntimeFieldsInfoTransportAction extends XPackInfoFeatureTransportAction {
-    @Inject
-    public RuntimeFieldsInfoTransportAction(TransportService transportService, ActionFilters actionFilters) {
-        super(XPackInfoFeatureAction.RUNTIME_FIELDS.name(), transportService, actionFilters);
-    }
-
-    @Override
-    protected String name() {
-        return XPackField.RUNTIME_FIELDS;
-    }
-
-    @Override
-    protected boolean available() {
-        return true;
-    }
-
-    @Override
-    protected boolean enabled() {
-        return true;
-    }
-}

+ 0 - 55
x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/RuntimeFieldsUsageTransportAction.java

@@ -1,55 +0,0 @@
-/*
- * 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.runtimefields;
-
-import org.elasticsearch.action.ActionListener;
-import org.elasticsearch.action.support.ActionFilters;
-import org.elasticsearch.cluster.ClusterState;
-import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
-import org.elasticsearch.cluster.service.ClusterService;
-import org.elasticsearch.common.inject.Inject;
-import org.elasticsearch.protocol.xpack.XPackUsageRequest;
-import org.elasticsearch.tasks.Task;
-import org.elasticsearch.threadpool.ThreadPool;
-import org.elasticsearch.transport.TransportService;
-import org.elasticsearch.xpack.core.action.XPackUsageFeatureAction;
-import org.elasticsearch.xpack.core.action.XPackUsageFeatureResponse;
-import org.elasticsearch.xpack.core.action.XPackUsageFeatureTransportAction;
-import org.elasticsearch.xpack.core.runtimefields.RuntimeFieldsFeatureSetUsage;
-
-public final class RuntimeFieldsUsageTransportAction extends XPackUsageFeatureTransportAction {
-
-    @Inject
-    public RuntimeFieldsUsageTransportAction(
-        TransportService transportService,
-        ClusterService clusterService,
-        ThreadPool threadPool,
-        ActionFilters actionFilters,
-        IndexNameExpressionResolver indexNameExpressionResolver
-    ) {
-        super(
-            XPackUsageFeatureAction.RUNTIME_FIELDS.name(),
-            transportService,
-            clusterService,
-            threadPool,
-            actionFilters,
-            indexNameExpressionResolver
-        );
-    }
-
-    @Override
-    protected void masterOperation(
-        Task task,
-        XPackUsageRequest request,
-        ClusterState state,
-        ActionListener<XPackUsageFeatureResponse> listener
-    ) {
-        RuntimeFieldsFeatureSetUsage runtimeFieldsFeatureSetUsage = RuntimeFieldsFeatureSetUsage.fromMetadata(state.metadata());
-        listener.onResponse(new XPackUsageFeatureResponse(runtimeFieldsFeatureSetUsage));
-    }
-}

+ 0 - 2
x-pack/plugin/security/qa/operator-privileges-tests/src/javaRestTest/java/org/elasticsearch/xpack/security/operator/Constants.java

@@ -254,7 +254,6 @@ public class Constants {
         "cluster:monitor/xpack/info/ml",
         "cluster:monitor/xpack/info/monitoring",
         "cluster:monitor/xpack/info/rollup",
-        "cluster:monitor/xpack/info/runtime_fields",
         "cluster:monitor/xpack/info/searchable_snapshots",
         "cluster:monitor/xpack/info/security",
         "cluster:monitor/xpack/info/slm",
@@ -307,7 +306,6 @@ public class Constants {
         "cluster:monitor/xpack/usage/ml",
         "cluster:monitor/xpack/usage/monitoring",
         "cluster:monitor/xpack/usage/rollup",
-        "cluster:monitor/xpack/usage/runtime_fields",
         "cluster:monitor/xpack/usage/searchable_snapshots",
         "cluster:monitor/xpack/usage/security",
         "cluster:monitor/xpack/usage/slm",

+ 0 - 217
x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/200_runtime_fields_stats.yml

@@ -1,217 +0,0 @@
----
-"Usage stats without runtime fields":
-  - do:
-      indices.create:
-        index: sensor
-
-  - do: {xpack.info: {}}
-  - match: { features.runtime_fields.available: true }
-  - match: { features.runtime_fields.enabled: true }
-
-  - do: {xpack.usage: {}}
-  - match: { runtime_fields.available: true }
-  - match: { runtime_fields.enabled: true }
-  - length: { runtime_fields.field_types: 0 }
-
----
-"Usage stats with runtime fields":
-  - do:
-      indices.create:
-        index: sensor
-        body:
-          mappings:
-            runtime:
-              message_from_source:
-                type: keyword
-              day_of_week:
-                type: keyword
-                script: |
-                  emit(doc['timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT));
-              # Test fetching from _source
-              day_of_week_from_source:
-                type: keyword
-                script: |
-                  Instant instant = Instant.ofEpochMilli(params._source.timestamp);
-                  ZonedDateTime dt = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
-                  emit(dt.dayOfWeek.getDisplayName(TextStyle.FULL, Locale.ROOT));
-              millis_ago:
-                type: date
-                script:
-                  source: |
-                    for (def dt : doc['timestamp']) {
-                      emit(System.currentTimeMillis() - dt.toInstant().toEpochMilli());
-                    }
-              tomorrow:
-                type: date
-                script:
-                  source: |
-                    for (def dt : doc['timestamp']) {
-                      emit(dt.plus(params.days, ChronoUnit.DAYS).toEpochMilli());
-                    }
-                  params:
-                    days: 1
-              voltage_times_ten:
-                type: long
-                script:
-                  source: |
-                    for (double v : doc['voltage']) {
-                      emit((long)(v * params.multiplier));
-                    }
-                  params:
-                    multiplier: 10
-              voltage_percent_from_source:
-                type: double
-                script:
-                  source: |
-                    emit(params._source.voltage / params.max);
-                  params:
-                    max: 5.8
-              over_v:
-                type: boolean
-                script:
-                  source: |
-                    for (def v : doc['voltage']) {
-                      emit(v >= params.min_v);
-                    }
-                  params:
-                    min_v: 5.0
-              ip:
-                type: ip
-                script:
-                  source: |
-                    Matcher m = /([^ ]+) .+/.matcher(doc["message"].value);
-                    if (m.matches()) {
-                      emit(m.group(1));
-                    }
-              bad_map:
-                type: double    # shadows the bad_map field in properties
-              message.text:
-                type: keyword   # shadows the message.text subfield in properties
-              location_from_source:
-                type: geo_point
-                script:
-                  source: |
-                    emit(params._source.location.lat, params._source.location.lon);
-            properties:
-              timestamp:
-                type: date
-              message:
-                type: keyword
-                fields:
-                  text:
-                    type: text
-              voltage:
-                type: double
-              location:
-                type: geo_point
-              bad_map:
-                type: long
-
-  - do: {xpack.info: {}}
-  - match: { features.runtime_fields.available: true }
-  - match: { features.runtime_fields.enabled: true }
-
-  - do: {xpack.usage: {}}
-  - match: { runtime_fields.available: true }
-  - match: { runtime_fields.enabled: true }
-  - length: { runtime_fields.field_types: 7 }
-  - match: { runtime_fields.field_types.0.name: boolean }
-  - match: { runtime_fields.field_types.0.lang: [painless] }
-  - match: { runtime_fields.field_types.0.count: 1 }
-  - match: { runtime_fields.field_types.0.index_count: 1 }
-  - match: { runtime_fields.field_types.0.scriptless_count: 0 }
-  - match: { runtime_fields.field_types.0.source_max: 0 }
-  - match: { runtime_fields.field_types.0.source_total: 0 }
-  - match: { runtime_fields.field_types.0.lines_max:  3 }
-  - match: { runtime_fields.field_types.0.lines_total: 3 }
-  - is_true: runtime_fields.field_types.0.chars_max
-  - is_true: runtime_fields.field_types.0.chars_total
-  - match: { runtime_fields.field_types.0.doc_max: 1 }
-  - match: { runtime_fields.field_types.0.doc_total: 1 }
-
-  - match: { runtime_fields.field_types.1.name: date }
-  - match: { runtime_fields.field_types.1.lang: [painless] }
-  - match: { runtime_fields.field_types.1.count: 2 }
-  - match: { runtime_fields.field_types.1.index_count: 1 }
-  - match: { runtime_fields.field_types.1.scriptless_count: 0 }
-  - match: { runtime_fields.field_types.1.shadowed_count: 0 }
-  - match: { runtime_fields.field_types.1.source_max: 0 }
-  - match: { runtime_fields.field_types.1.source_total: 0 }
-  - match: { runtime_fields.field_types.1.lines_max:  3 }
-  - match: { runtime_fields.field_types.1.lines_total: 6 }
-  - is_true: runtime_fields.field_types.1.chars_max
-  - is_true: runtime_fields.field_types.1.chars_total
-  - match: { runtime_fields.field_types.1.doc_max: 1 }
-  - match: { runtime_fields.field_types.1.doc_total: 2 }
-
-  - match: { runtime_fields.field_types.2.name: double }
-  - match: { runtime_fields.field_types.2.lang: [painless] }
-  - match: { runtime_fields.field_types.2.count: 2 }
-  - match: { runtime_fields.field_types.2.index_count: 1 }
-  - match: { runtime_fields.field_types.2.scriptless_count: 1 }
-  - match: { runtime_fields.field_types.2.shadowed_count: 1 }
-  - match: { runtime_fields.field_types.2.source_max: 1 }
-  - match: { runtime_fields.field_types.2.source_total: 1 }
-  - match: { runtime_fields.field_types.2.lines_max:  1 }
-  - match: { runtime_fields.field_types.2.lines_total: 1 }
-  - is_true: runtime_fields.field_types.2.chars_max
-  - is_true: runtime_fields.field_types.2.chars_total
-  - match: { runtime_fields.field_types.2.doc_max: 0 }
-  - match: { runtime_fields.field_types.2.doc_total: 0 }
-
-  - match: { runtime_fields.field_types.3.name: geo_point }
-  - match: { runtime_fields.field_types.3.lang: [painless] }
-  - match: { runtime_fields.field_types.3.count: 1 }
-  - match: { runtime_fields.field_types.3.index_count: 1 }
-  - match: { runtime_fields.field_types.3.scriptless_count: 0 }
-  - match: { runtime_fields.field_types.3.source_max: 2 }
-  - match: { runtime_fields.field_types.3.source_total: 2 }
-  - match: { runtime_fields.field_types.3.lines_max:  1 }
-  - match: { runtime_fields.field_types.3.lines_total: 1 }
-  - is_true: runtime_fields.field_types.3.chars_max
-  - is_true: runtime_fields.field_types.3.chars_total
-  - match: { runtime_fields.field_types.3.doc_max: 0 }
-  - match: { runtime_fields.field_types.3.doc_total: 0 }
-
-  - match: { runtime_fields.field_types.4.name: ip }
-  - match: { runtime_fields.field_types.4.lang: [painless] }
-  - match: { runtime_fields.field_types.4.count: 1 }
-  - match: { runtime_fields.field_types.4.index_count: 1 }
-  - match: { runtime_fields.field_types.4.scriptless_count: 0 }
-  - match: { runtime_fields.field_types.4.source_max: 0 }
-  - match: { runtime_fields.field_types.4.source_total: 0 }
-  - match: { runtime_fields.field_types.4.lines_max:  4 }
-  - match: { runtime_fields.field_types.4.lines_total: 4 }
-  - is_true: runtime_fields.field_types.4.chars_max
-  - is_true: runtime_fields.field_types.4.chars_total
-  - match: { runtime_fields.field_types.4.doc_max: 1 }
-  - match: { runtime_fields.field_types.4.doc_total: 1 }
-
-  - match: { runtime_fields.field_types.5.name: keyword }
-  - match: { runtime_fields.field_types.5.lang: [painless] }
-  - match: { runtime_fields.field_types.5.count: 4 }
-  - match: { runtime_fields.field_types.5.index_count: 1 }
-  - match: { runtime_fields.field_types.5.scriptless_count: 2 }
-  - match: { runtime_fields.field_types.5.shadowed_count: 1 }
-  - match: { runtime_fields.field_types.5.source_max: 1 }
-  - match: { runtime_fields.field_types.5.source_total: 1 }
-  - match: { runtime_fields.field_types.5.lines_max:  3 }
-  - match: { runtime_fields.field_types.5.lines_total: 4 }
-  - is_true: runtime_fields.field_types.5.chars_max
-  - is_true: runtime_fields.field_types.5.chars_total
-  - match: { runtime_fields.field_types.5.doc_max: 1 }
-  - match: { runtime_fields.field_types.5.doc_total: 1 }
-
-  - match: { runtime_fields.field_types.6.name: long }
-  - match: { runtime_fields.field_types.6.lang: [painless] }
-  - match: { runtime_fields.field_types.6.count: 1 }
-  - match: { runtime_fields.field_types.6.index_count: 1 }
-  - match: { runtime_fields.field_types.6.scriptless_count: 0 }
-  - match: { runtime_fields.field_types.6.source_max: 0 }
-  - match: { runtime_fields.field_types.6.source_total: 0 }
-  - match: { runtime_fields.field_types.6.lines_max:  3 }
-  - match: { runtime_fields.field_types.6.lines_total: 3 }
-  - is_true: runtime_fields.field_types.6.chars_max
-  - is_true: runtime_fields.field_types.6.chars_total
-  - match: { runtime_fields.field_types.6.doc_max: 1 }
-  - match: { runtime_fields.field_types.6.doc_total: 1 }