Skip to content

Commit 45d7bdc

Browse files
authored
Add analysis components and mapping types to the usage API. (#51062)
Knowing about used analysis components and mapping types would be incredibly useful in order to know which ones may be deprecated or should get more love. Some field types also act as a proxy to know about feature usage of some APIs like the `percolator` or `completion` fields types for percolation and the completion suggester, respectively.
1 parent ac6602a commit 45d7bdc

File tree

10 files changed

+729
-1
lines changed

10 files changed

+729
-1
lines changed

docs/reference/rest-api/info.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ Example response:
9191
"available" : true,
9292
"enabled" : true
9393
},
94+
"index" : {
95+
"available" : true,
96+
"enabled" : true
97+
},
9498
"logstash" : {
9599
"available" : true,
96100
"enabled" : true

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
import org.elasticsearch.xpack.core.watcher.transport.actions.put.PutWatchAction;
253253
import org.elasticsearch.xpack.core.watcher.transport.actions.service.WatcherServiceAction;
254254
import org.elasticsearch.xpack.core.watcher.transport.actions.stats.WatcherStatsAction;
255+
import org.elasticsearch.xpack.oss.IndexFeatureSetUsage;
255256

256257
import java.util.ArrayList;
257258
import java.util.Arrays;
@@ -608,7 +609,8 @@ public List<NamedWriteableRegistry.Entry> getNamedWriteables() {
608609
// analytics
609610
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ANALYTICS, AnalyticsFeatureSetUsage::new),
610611
// Enrich
611-
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ENRICH, EnrichFeatureSet.Usage::new)
612+
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.ENRICH, EnrichFeatureSet.Usage::new),
613+
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.INDEX, IndexFeatureSetUsage::new)
612614
).stream(),
613615
MlEvaluationNamedXContentProvider.getNamedWriteables().stream()
614616
).collect(toList());

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public final class XPackField {
5353
public static final String ANALYTICS = "analytics";
5454
/** Name constant for the enrich plugin. */
5555
public static final String ENRICH = "enrich";
56+
/** Name constant for indices. */
57+
public static final String INDEX = "index";
5658

5759
private XPackField() {}
5860

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import org.elasticsearch.xpack.core.ssl.SSLConfigurationReloader;
6969
import org.elasticsearch.xpack.core.ssl.SSLService;
7070
import org.elasticsearch.xpack.core.watcher.WatcherMetaData;
71+
import org.elasticsearch.xpack.oss.IndexFeatureSet;
7172

7273
import java.nio.file.Files;
7374
import java.nio.file.Path;
@@ -245,6 +246,8 @@ public Collection<Module> createGuiceModules() {
245246

246247
if (transportClientMode) {
247248
modules.add(b -> b.bind(XPackLicenseState.class).toProvider(Providers.of(null)));
249+
} else {
250+
modules.add(b -> XPackPlugin.bindFeatureSet(b, IndexFeatureSet.class));
248251
}
249252
return modules;
250253
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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.oss;
8+
9+
import org.elasticsearch.action.ActionListener;
10+
import org.elasticsearch.cluster.ClusterState;
11+
import org.elasticsearch.cluster.metadata.IndexMetaData;
12+
import org.elasticsearch.cluster.metadata.MappingMetaData;
13+
import org.elasticsearch.cluster.service.ClusterService;
14+
import org.elasticsearch.common.inject.Inject;
15+
import org.elasticsearch.common.settings.Settings;
16+
import org.elasticsearch.xpack.core.XPackFeatureSet;
17+
import org.elasticsearch.xpack.core.XPackField;
18+
19+
import java.util.Collection;
20+
import java.util.HashSet;
21+
import java.util.Map;
22+
import java.util.Set;
23+
import java.util.function.Consumer;
24+
25+
public class IndexFeatureSet implements XPackFeatureSet {
26+
27+
private final ClusterService clusterService;
28+
29+
@Inject
30+
public IndexFeatureSet(ClusterService clusterService) {
31+
this.clusterService = clusterService;
32+
}
33+
34+
@Override
35+
public String name() {
36+
return XPackField.INDEX;
37+
}
38+
39+
@Override
40+
public boolean available() {
41+
return true;
42+
}
43+
44+
@Override
45+
public boolean enabled() {
46+
return true;
47+
}
48+
49+
@Override
50+
public Map<String, Object> nativeCodeInfo() {
51+
return null;
52+
}
53+
54+
@Override
55+
public void usage(ActionListener<Usage> listener) {
56+
final Set<String> usedFieldTypes = new HashSet<>();
57+
final Set<String> usedCharFilters = new HashSet<>();
58+
final Set<String> usedTokenizers = new HashSet<>();
59+
final Set<String> usedTokenFilters = new HashSet<>();
60+
final Set<String> usedAnalyzers = new HashSet<>();
61+
final Set<String> usedBuiltInCharFilters = new HashSet<>();
62+
final Set<String> usedBuiltInTokenizers = new HashSet<>();
63+
final Set<String> usedBuiltInTokenFilters = new HashSet<>();
64+
final Set<String> usedBuiltInAnalyzers = new HashSet<>();
65+
66+
ClusterState state = clusterService.state();
67+
if (state != null) {
68+
69+
for (IndexMetaData indexMetaData : state.metaData()) {
70+
MappingMetaData mappingMetaData = indexMetaData.mapping();
71+
if (mappingMetaData != null) {
72+
visitMapping(mappingMetaData.getSourceAsMap(), fieldMapping -> {
73+
Object type = fieldMapping.get("type");
74+
if (type != null) {
75+
usedFieldTypes.add(type.toString());
76+
} else if (fieldMapping.containsKey("properties")) {
77+
usedFieldTypes.add("object");
78+
}
79+
80+
for (String key : new String[] { "analyzer", "search_analyzer", "search_quote_analyzer" }) {
81+
Object analyzer = fieldMapping.get(key);
82+
if (analyzer != null) {
83+
usedBuiltInAnalyzers.add(analyzer.toString());
84+
}
85+
}
86+
});
87+
}
88+
89+
Settings indexSettings = indexMetaData.getSettings();
90+
91+
Map<String, Settings> analyzerSettings = indexSettings.getGroups("index.analysis.analyzer");
92+
usedBuiltInAnalyzers.removeAll(analyzerSettings.keySet());
93+
for (Settings analyzerSetting : analyzerSettings.values()) {
94+
usedAnalyzers.add(analyzerSetting.get("type", "custom"));
95+
usedBuiltInCharFilters.addAll(analyzerSetting.getAsList("char_filter"));
96+
String tokenizer = analyzerSetting.get("tokenizer");
97+
if (tokenizer != null) {
98+
usedBuiltInTokenizers.add(tokenizer);
99+
}
100+
usedBuiltInTokenFilters.addAll(analyzerSetting.getAsList("filter"));
101+
}
102+
103+
Map<String, Settings> charFilterSettings = indexSettings.getGroups("index.analysis.char_filter");
104+
usedBuiltInCharFilters.removeAll(charFilterSettings.keySet());
105+
aggregateAnalysisTypes(charFilterSettings.values(), usedCharFilters);
106+
107+
Map<String, Settings> tokenizerSettings = indexSettings.getGroups("index.analysis.tokenizer");
108+
usedBuiltInTokenizers.removeAll(tokenizerSettings.keySet());
109+
aggregateAnalysisTypes(tokenizerSettings.values(), usedTokenizers);
110+
111+
Map<String, Settings> tokenFilterSettings = indexSettings.getGroups("index.analysis.filter");
112+
usedBuiltInTokenFilters.removeAll(tokenFilterSettings.keySet());
113+
aggregateAnalysisTypes(tokenFilterSettings.values(), usedTokenFilters);
114+
}
115+
}
116+
117+
listener.onResponse(new IndexFeatureSetUsage(usedFieldTypes, usedCharFilters, usedTokenizers, usedTokenFilters,
118+
usedAnalyzers, usedBuiltInCharFilters, usedBuiltInTokenizers, usedBuiltInTokenFilters, usedBuiltInAnalyzers));
119+
}
120+
121+
static void visitMapping(Map<String, ?> mapping, Consumer<Map<String, ?>> fieldMappingConsumer) {
122+
Object properties = mapping.get("properties");
123+
if (properties != null && properties instanceof Map) {
124+
@SuppressWarnings("unchecked")
125+
Map<String, ?> propertiesAsMap = (Map<String, ?>) properties;
126+
for (Object v : propertiesAsMap.values()) {
127+
if (v != null && v instanceof Map) {
128+
129+
@SuppressWarnings("unchecked")
130+
Map<String, ?> fieldMapping = (Map<String, ?>) v;
131+
fieldMappingConsumer.accept(fieldMapping);
132+
visitMapping(fieldMapping, fieldMappingConsumer);
133+
134+
// Multi fields
135+
Object fieldsO = fieldMapping.get("fields");
136+
if (fieldsO != null && fieldsO instanceof Map) {
137+
@SuppressWarnings("unchecked")
138+
Map<String, ?> fields = (Map<String, ?>) fieldsO;
139+
for (Object v2 : fields.values()) {
140+
if (v2 instanceof Map) {
141+
@SuppressWarnings("unchecked")
142+
Map<String, ?> fieldMapping2 = (Map<String, ?>) v2;
143+
fieldMappingConsumer.accept(fieldMapping2);
144+
}
145+
}
146+
}
147+
}
148+
}
149+
}
150+
}
151+
152+
static void aggregateAnalysisTypes(Collection<Settings> analysisComponents, Set<String> usedTypes) {
153+
for (Settings settings : analysisComponents) {
154+
String type = settings.get("type");
155+
if (type != null) {
156+
usedTypes.add(type);
157+
}
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)