Skip to content

Commit 91d37c2

Browse files
committed
Add xpack info and usage endpoints for runtime fields
Relates to elastic#59332
1 parent 5fec253 commit 91d37c2

File tree

7 files changed

+701
-8
lines changed

7 files changed

+701
-8
lines changed

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

+24-7
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,37 @@ public final class XPackField {
5555
public static final String ANALYTICS = "analytics";
5656
/** Name constant for the enrich plugin. */
5757
public static final String ENRICH = "enrich";
58-
/** Name constant for the constant-keyword plugin. */
58+
/**
59+
* Name constant for the constant-keyword plugin.
60+
*/
5961
public static final String CONSTANT_KEYWORD = "constant_keyword";
60-
/** Name constant for the searchable snapshots feature. */
62+
/**
63+
* Name constant for the searchable snapshots feature.
64+
*/
6165
public static final String SEARCHABLE_SNAPSHOTS = "searchable_snapshots";
62-
/** Name constant for the data streams feature. */
66+
/**
67+
* Name constant for the data streams feature.
68+
*/
6369
public static final String DATA_STREAMS = "data_streams";
64-
/** Name constant for the data tiers feature. */
70+
/**
71+
* Name constant for the data tiers feature.
72+
*/
6573
public static final String DATA_TIERS = "data_tiers";
66-
/** Name constant for the aggregate_metric plugin. */
74+
/**
75+
* Name constant for the aggregate_metric plugin.
76+
*/
6777
public static final String AGGREGATE_METRIC = "aggregate_metric";
68-
/** Name constant for the operator privileges feature. */
78+
/**
79+
* Name constant for the runtime fields plugin.
80+
*/
81+
public static final String RUNTIME_FIELDS = "runtime_fields";
82+
/**
83+
* Name constant for the operator privileges feature.
84+
*/
6985
public static final String OPERATOR_PRIVILEGES = "operator_privileges";
7086

71-
private XPackField() {}
87+
private XPackField() {
88+
}
7289

7390
public static String featureSettingPrefix(String featureName) {
7491
return XPackField.SETTINGS_NAME + "." + featureName;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.core.runtimefields;
8+
9+
import org.elasticsearch.Version;
10+
import org.elasticsearch.cluster.metadata.IndexMetadata;
11+
import org.elasticsearch.cluster.metadata.MappingMetadata;
12+
import org.elasticsearch.common.io.stream.StreamInput;
13+
import org.elasticsearch.common.io.stream.StreamOutput;
14+
import org.elasticsearch.common.io.stream.Writeable;
15+
import org.elasticsearch.common.xcontent.ToXContentObject;
16+
import org.elasticsearch.common.xcontent.XContentBuilder;
17+
import org.elasticsearch.xpack.core.XPackFeatureSet;
18+
import org.elasticsearch.xpack.core.XPackField;
19+
20+
import java.io.IOException;
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.Comparator;
24+
import java.util.HashMap;
25+
import java.util.HashSet;
26+
import java.util.List;
27+
import java.util.Map;
28+
import java.util.Objects;
29+
import java.util.Set;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
32+
33+
public class RuntimeFieldsFeatureSetUsage extends XPackFeatureSet.Usage {
34+
35+
public static RuntimeFieldsFeatureSetUsage fromMetadata(Iterable<IndexMetadata> metadata) {
36+
Map<String, RuntimeFieldStats> fieldTypes = new HashMap<>();
37+
for (IndexMetadata indexMetadata : metadata) {
38+
if (indexMetadata.isSystem()) {
39+
// Don't include system indices in statistics about mappings, we care about the user's indices.
40+
continue;
41+
}
42+
Set<String> indexFieldTypes = new HashSet<>();
43+
MappingMetadata mappingMetadata = indexMetadata.mapping();
44+
if (mappingMetadata != null) {
45+
Object runtimeObject = mappingMetadata.getSourceAsMap().get("runtime");
46+
if (runtimeObject instanceof Map == false) {
47+
continue;
48+
}
49+
Map<?, ?> runtimeMappings = (Map<?, ?>) runtimeObject;
50+
for (Object runtimeFieldMappingObject : runtimeMappings.values()) {
51+
if (runtimeFieldMappingObject instanceof Map == false) {
52+
continue;
53+
}
54+
Map<?, ?> runtimeFieldMapping = (Map<?, ?>) runtimeFieldMappingObject;
55+
Object typeObject = runtimeFieldMapping.get("type");
56+
if (typeObject == null) {
57+
continue;
58+
}
59+
String type = typeObject.toString();
60+
RuntimeFieldStats stats = fieldTypes.computeIfAbsent(type, RuntimeFieldStats::new);
61+
stats.count++;
62+
if (indexFieldTypes.add(type)) {
63+
stats.indexCount++;
64+
}
65+
Object scriptObject = runtimeFieldMapping.get("script");
66+
if (scriptObject == null) {
67+
stats.scriptLessCount++;
68+
} else if (scriptObject instanceof Map) {
69+
Map<?, ?> script = (Map<?, ?>) scriptObject;
70+
Object sourceObject = script.get("source");
71+
if (sourceObject != null) {
72+
String scriptSource = sourceObject.toString();
73+
int chars = scriptSource.length();
74+
long lines = scriptSource.split("\\n").length;
75+
int docUsages = countOccurrences(scriptSource, "doc[\\[\\.]");
76+
int sourceUsages = countOccurrences(scriptSource, "params\\._source");
77+
stats.update(chars, lines, sourceUsages, docUsages);
78+
}
79+
Object langObject = script.get("lang");
80+
if (langObject != null) {
81+
stats.scriptLangs.add(langObject.toString());
82+
}
83+
}
84+
}
85+
}
86+
}
87+
List<RuntimeFieldStats> runtimeFieldStats = new ArrayList<>(fieldTypes.values());
88+
runtimeFieldStats.sort(Comparator.comparing(RuntimeFieldStats::type));
89+
return new RuntimeFieldsFeatureSetUsage(Collections.unmodifiableList(runtimeFieldStats));
90+
}
91+
92+
private final List<RuntimeFieldStats> stats;
93+
94+
RuntimeFieldsFeatureSetUsage(List<RuntimeFieldStats> stats) {
95+
super(XPackField.RUNTIME_FIELDS, true, true);
96+
this.stats = stats;
97+
}
98+
99+
public RuntimeFieldsFeatureSetUsage(StreamInput in) throws IOException {
100+
super(in);
101+
this.stats = in.readList(RuntimeFieldStats::new);
102+
}
103+
104+
@Override
105+
public void writeTo(StreamOutput out) throws IOException {
106+
super.writeTo(out);
107+
out.writeList(stats);
108+
}
109+
110+
List<RuntimeFieldStats> getRuntimeFieldStats() {
111+
return stats;
112+
}
113+
114+
@Override
115+
protected void innerXContent(XContentBuilder builder, Params params) throws IOException {
116+
super.innerXContent(builder, params);
117+
builder.startArray("field_types");
118+
for (RuntimeFieldStats stats : stats) {
119+
stats.toXContent(builder, params);
120+
}
121+
builder.endArray();
122+
}
123+
124+
@Override
125+
public Version getMinimalSupportedVersion() {
126+
return Version.V_7_11_0;
127+
}
128+
129+
@Override
130+
public boolean equals(Object o) {
131+
if (this == o) {
132+
return true;
133+
}
134+
if (o == null || getClass() != o.getClass()) {
135+
return false;
136+
}
137+
RuntimeFieldsFeatureSetUsage that = (RuntimeFieldsFeatureSetUsage) o;
138+
return stats.equals(that.stats);
139+
}
140+
141+
@Override
142+
public int hashCode() {
143+
return Objects.hash(stats);
144+
}
145+
146+
private static int countOccurrences(String script, String keyword) {
147+
int occurrences = 0;
148+
Pattern pattern = Pattern.compile(keyword);
149+
Matcher matcher = pattern.matcher(script);
150+
while (matcher.find()) {
151+
occurrences++;
152+
}
153+
return occurrences;
154+
}
155+
156+
static final class RuntimeFieldStats implements Writeable, ToXContentObject {
157+
private final String type;
158+
private int count = 0;
159+
private int indexCount = 0;
160+
private final Set<String> scriptLangs;
161+
private long scriptLessCount = 0;
162+
private long maxLines = 0;
163+
private long totalLines = 0;
164+
private long maxChars = 0;
165+
private long totalChars = 0;
166+
private long maxSourceUsages = 0;
167+
private long totalSourceUsages = 0;
168+
private long maxDocUsages = 0;
169+
private long totalDocUsages = 0;
170+
171+
RuntimeFieldStats(String type) {
172+
this.type = Objects.requireNonNull(type);
173+
this.scriptLangs = new HashSet<>();
174+
}
175+
176+
RuntimeFieldStats(StreamInput in) throws IOException {
177+
this.type = in.readString();
178+
this.count = in.readInt();
179+
this.indexCount = in.readInt();
180+
this.scriptLangs = in.readSet(StreamInput::readString);
181+
this.scriptLessCount = in.readLong();
182+
this.maxLines = in.readLong();
183+
this.totalLines = in.readLong();
184+
this.maxChars = in.readLong();
185+
this.totalChars = in.readLong();
186+
this.maxSourceUsages = in.readLong();
187+
this.totalSourceUsages = in.readLong();
188+
this.maxDocUsages = in.readLong();
189+
this.totalDocUsages = in.readLong();
190+
}
191+
192+
String type() {
193+
return type;
194+
}
195+
196+
@Override
197+
public void writeTo(StreamOutput out) throws IOException {
198+
out.writeString(type);
199+
out.writeInt(count);
200+
out.writeInt(indexCount);
201+
out.writeCollection(scriptLangs, StreamOutput::writeString);
202+
out.writeLong(scriptLessCount);
203+
out.writeLong(maxLines);
204+
out.writeLong(totalLines);
205+
out.writeLong(maxChars);
206+
out.writeLong(totalChars);
207+
out.writeLong(maxSourceUsages);
208+
out.writeLong(totalSourceUsages);
209+
out.writeLong(maxDocUsages);
210+
out.writeLong(totalDocUsages);
211+
}
212+
213+
@Override
214+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
215+
builder.startObject();
216+
builder.field("name", type);
217+
builder.field("count", count);
218+
builder.field("index_count", indexCount);
219+
builder.field("scriptless_count", scriptLessCount);
220+
builder.array("lang", scriptLangs.toArray(new String[0]));
221+
builder.field("lines_max", maxLines);
222+
builder.field("lines_total", totalLines);
223+
builder.field("chars_max", maxChars);
224+
builder.field("chars_total", totalChars);
225+
builder.field("source_max", maxSourceUsages);
226+
builder.field("source_total", totalSourceUsages);
227+
builder.field("doc_max", maxDocUsages);
228+
builder.field("doc_total", totalDocUsages);
229+
builder.endObject();
230+
return builder;
231+
}
232+
233+
void update(int chars, long lines, int sourceUsages, int docUsages) {
234+
this.maxChars = Math.max(this.maxChars, chars);
235+
this.totalChars += chars;
236+
this.maxLines = Math.max(this.maxLines, lines);
237+
this.totalLines += lines;
238+
this.totalSourceUsages += sourceUsages;
239+
this.maxSourceUsages = Math.max(this.maxSourceUsages, sourceUsages);
240+
this.totalDocUsages += docUsages;
241+
this.maxDocUsages = Math.max(this.maxDocUsages, docUsages);
242+
}
243+
244+
@Override
245+
public boolean equals(Object o) {
246+
if (this == o) {
247+
return true;
248+
}
249+
if (o == null || getClass() != o.getClass()) {
250+
return false;
251+
}
252+
RuntimeFieldStats that = (RuntimeFieldStats) o;
253+
return count == that.count &&
254+
indexCount == that.indexCount &&
255+
scriptLessCount == that.scriptLessCount &&
256+
maxLines == that.maxLines &&
257+
totalLines == that.totalLines &&
258+
maxChars == that.maxChars &&
259+
totalChars == that.totalChars &&
260+
maxSourceUsages == that.maxSourceUsages &&
261+
totalSourceUsages == that.totalSourceUsages &&
262+
maxDocUsages == that.maxDocUsages &&
263+
totalDocUsages == that.totalDocUsages &&
264+
type.equals(that.type) &&
265+
scriptLangs.equals(that.scriptLangs);
266+
}
267+
268+
@Override
269+
public int hashCode() {
270+
return Objects.hash(type, count, indexCount, scriptLangs, scriptLessCount, maxLines, totalLines, maxChars, totalChars,
271+
maxSourceUsages, totalSourceUsages, maxDocUsages, totalDocUsages);
272+
}
273+
}
274+
}

0 commit comments

Comments
 (0)