Skip to content

Commit 390fa65

Browse files
authored
Scripted keyword field type: update family type and test field caps output (#59672)
Relates to #59332
1 parent bd1b2ae commit 390fa65

File tree

3 files changed

+104
-15
lines changed

3 files changed

+104
-15
lines changed

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/RuntimeKeywordMappedFieldType.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.common.unit.Fuzziness;
1717
import org.elasticsearch.common.xcontent.ToXContent.Params;
1818
import org.elasticsearch.common.xcontent.XContentBuilder;
19+
import org.elasticsearch.index.mapper.KeywordFieldMapper;
1920
import org.elasticsearch.index.mapper.MappedFieldType;
2021
import org.elasticsearch.index.mapper.TextSearchInfo;
2122
import org.elasticsearch.index.query.QueryShardContext;
@@ -64,11 +65,24 @@ public Object valueForDisplay(Object value) {
6465

6566
@Override
6667
public String typeName() {
67-
// TODO not sure what we should return here: the runtime type or the field type?
68-
// why is the same string returned from three different methods?
6968
return ScriptFieldMapper.CONTENT_TYPE;
7069
}
7170

71+
@Override
72+
public String familyTypeName() {
73+
return KeywordFieldMapper.CONTENT_TYPE;
74+
}
75+
76+
@Override
77+
public boolean isSearchable() {
78+
return true;
79+
}
80+
81+
@Override
82+
public boolean isAggregatable() {
83+
return true;
84+
}
85+
7286
@Override
7387
public ScriptBinaryFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) {
7488
// TODO once we get SearchLookup as an argument, we can already call scriptFactory.newFactory here and pass through the result

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptFieldMapper.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import org.elasticsearch.common.xcontent.XContentBuilder;
1010
import org.elasticsearch.index.mapper.FieldMapper;
11+
import org.elasticsearch.index.mapper.KeywordFieldMapper;
1112
import org.elasticsearch.index.mapper.MappedFieldType;
1213
import org.elasticsearch.index.mapper.Mapper;
1314
import org.elasticsearch.index.mapper.MapperParsingException;
@@ -21,6 +22,7 @@
2122
import java.io.IOException;
2223
import java.util.List;
2324
import java.util.Map;
25+
import java.util.function.BiFunction;
2426

2527
public final class ScriptFieldMapper extends ParametrizedFieldMapper {
2628

@@ -69,6 +71,22 @@ protected String contentType() {
6971

7072
public static class Builder extends ParametrizedFieldMapper.Builder {
7173

74+
static final Map<String, BiFunction<Builder, BuilderContext, MappedFieldType>> FIELD_TYPE_RESOLVER = Map.of(
75+
KeywordFieldMapper.CONTENT_TYPE,
76+
(builder, context) -> {
77+
StringScriptFieldScript.Factory factory = builder.scriptCompiler.compile(
78+
builder.script.getValue(),
79+
StringScriptFieldScript.CONTEXT
80+
);
81+
return new RuntimeKeywordMappedFieldType(
82+
builder.buildFullName(context),
83+
builder.script.getValue(),
84+
factory,
85+
builder.meta.getValue()
86+
);
87+
}
88+
);
89+
7290
private static ScriptFieldMapper toType(FieldMapper in) {
7391
return (ScriptFieldMapper) in;
7492
}
@@ -112,13 +130,13 @@ protected List<Parameter<?>> getParameters() {
112130

113131
@Override
114132
public ScriptFieldMapper build(BuilderContext context) {
115-
MappedFieldType mappedFieldType;
116-
if (runtimeType.getValue().equals("keyword")) {
117-
StringScriptFieldScript.Factory factory = scriptCompiler.compile(script.getValue(), StringScriptFieldScript.CONTEXT);
118-
mappedFieldType = new RuntimeKeywordMappedFieldType(buildFullName(context), script.getValue(), factory, meta.getValue());
119-
} else {
133+
BiFunction<Builder, BuilderContext, MappedFieldType> fieldTypeResolver = Builder.FIELD_TYPE_RESOLVER.get(
134+
runtimeType.getValue()
135+
);
136+
if (fieldTypeResolver == null) {
120137
throw new IllegalArgumentException("runtime_type [" + runtimeType.getValue() + "] not supported");
121138
}
139+
MappedFieldType mappedFieldType = fieldTypeResolver.apply(this, context);
122140
// TODO copy to and multi_fields should not be supported, parametrized field mapper needs to be adapted
123141
return new ScriptFieldMapper(
124142
name,

x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptFieldMapperTests.java

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66

77
package org.elasticsearch.xpack.runtimefields.mapper;
88

9+
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
10+
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
911
import org.elasticsearch.common.Strings;
1012
import org.elasticsearch.common.settings.Settings;
1113
import org.elasticsearch.common.xcontent.XContentBuilder;
1214
import org.elasticsearch.common.xcontent.XContentFactory;
1315
import org.elasticsearch.index.mapper.FieldMapper;
16+
import org.elasticsearch.index.mapper.KeywordFieldMapper;
1417
import org.elasticsearch.index.mapper.MapperParsingException;
1518
import org.elasticsearch.index.mapper.MapperService;
1619
import org.elasticsearch.plugins.Plugin;
@@ -22,15 +25,22 @@
2225
import org.elasticsearch.xpack.runtimefields.RuntimeFields;
2326
import org.elasticsearch.xpack.runtimefields.StringScriptFieldScript;
2427

28+
import java.util.Arrays;
2529
import java.util.Collection;
2630
import java.util.Map;
2731
import java.util.Set;
2832

33+
import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
2934
import static org.hamcrest.Matchers.instanceOf;
3035

3136
public class ScriptFieldMapperTests extends ESSingleNodeTestCase {
3237

33-
private static final String[] SUPPORTED_RUNTIME_TYPES = new String[] { "keyword" };
38+
private final String[] runtimeTypes;
39+
40+
public ScriptFieldMapperTests() {
41+
this.runtimeTypes = ScriptFieldMapper.Builder.FIELD_TYPE_RESOLVER.keySet().toArray(new String[0]);
42+
Arrays.sort(runtimeTypes);
43+
}
3444

3545
@Override
3646
protected Collection<Class<? extends Plugin>> getPlugins() {
@@ -45,7 +55,7 @@ public void testRuntimeTypeIsRequired() throws Exception {
4555
.startObject("properties")
4656
.startObject("my_field")
4757
.field("type", "script")
48-
.field("script", "value('test')")
58+
.field("script", "keyword('test')")
4959
.endObject()
5060
.endObject()
5161
.endObject()
@@ -63,7 +73,7 @@ public void testScriptIsRequired() throws Exception {
6373
.startObject("properties")
6474
.startObject("my_field")
6575
.field("type", "script")
66-
.field("runtime_type", randomFrom(SUPPORTED_RUNTIME_TYPES))
76+
.field("runtime_type", randomFrom(runtimeTypes))
6777
.endObject()
6878
.endObject()
6979
.endObject()
@@ -80,7 +90,7 @@ public void testStoredScriptsAreNotSupported() throws Exception {
8090
.startObject("properties")
8191
.startObject("my_field")
8292
.field("type", "script")
83-
.field("runtime_type", randomFrom(SUPPORTED_RUNTIME_TYPES))
93+
.field("runtime_type", randomFrom(runtimeTypes))
8494
.startObject("script")
8595
.field("id", "test")
8696
.endObject()
@@ -104,7 +114,7 @@ public void testUnsupportedRuntimeType() throws Exception {
104114
.field("type", "script")
105115
.field("runtime_type", "unsupported")
106116
.startObject("script")
107-
.field("source", "value('test')")
117+
.field("source", "keyword('test')")
108118
.field("lang", "test")
109119
.endObject()
110120
.endObject()
@@ -116,16 +126,63 @@ public void testUnsupportedRuntimeType() throws Exception {
116126
assertEquals("Failed to parse mapping: runtime_type [unsupported] not supported", exc.getMessage());
117127
}
118128

129+
public void testFieldCaps() throws Exception {
130+
for (String runtimeType : runtimeTypes) {
131+
{
132+
XContentBuilder mapping = XContentFactory.jsonBuilder()
133+
.startObject()
134+
.startObject("_doc")
135+
.startObject("properties")
136+
.startObject("field")
137+
.field("type", "script")
138+
.field("runtime_type", runtimeType)
139+
.startObject("script")
140+
.field("source", runtimeType + "('test')")
141+
.field("lang", "test")
142+
.endObject()
143+
.endObject()
144+
.endObject()
145+
.endObject()
146+
.endObject();
147+
createIndex("test_script", Settings.EMPTY, mapping);
148+
}
149+
{
150+
XContentBuilder mapping = XContentFactory.jsonBuilder()
151+
.startObject()
152+
.startObject("_doc")
153+
.startObject("properties")
154+
.startObject("field")
155+
.field("type", runtimeType)
156+
.endObject()
157+
.endObject()
158+
.endObject()
159+
.endObject();
160+
createIndex("test_concrete", Settings.EMPTY, mapping);
161+
}
162+
FieldCapabilitiesResponse response = client().prepareFieldCaps("test_*").setFields("field").get();
163+
assertThat(response.getIndices(), arrayContainingInAnyOrder("test_script", "test_concrete"));
164+
Map<String, FieldCapabilities> field = response.getField("field");
165+
assertEquals(1, field.size());
166+
FieldCapabilities fieldCapabilities = field.get(KeywordFieldMapper.CONTENT_TYPE);
167+
assertTrue(fieldCapabilities.isSearchable());
168+
assertTrue(fieldCapabilities.isAggregatable());
169+
assertEquals(runtimeType, fieldCapabilities.getType());
170+
assertNull(fieldCapabilities.nonAggregatableIndices());
171+
assertNull(fieldCapabilities.nonSearchableIndices());
172+
assertEquals("field", fieldCapabilities.getName());
173+
}
174+
}
175+
119176
public void testDefaultMapping() throws Exception {
120177
XContentBuilder mapping = XContentFactory.jsonBuilder()
121178
.startObject()
122179
.startObject("_doc")
123180
.startObject("properties")
124181
.startObject("field")
125182
.field("type", "script")
126-
.field("runtime_type", randomFrom(SUPPORTED_RUNTIME_TYPES))
183+
.field("runtime_type", randomFrom(runtimeTypes))
127184
.startObject("script")
128-
.field("source", "value('test')")
185+
.field("source", "keyword('test')")
129186
.field("lang", "test")
130187
.endObject()
131188
.endObject()
@@ -155,7 +212,7 @@ public <FactoryType> FactoryType compile(
155212
ScriptContext<FactoryType> context,
156213
Map<String, String> paramsMap
157214
) {
158-
if ("value('test')".equals(code)) {
215+
if ("keyword('test')".equals(code)) {
159216
StringScriptFieldScript.Factory factory = (params, lookup) -> ctx -> new StringScriptFieldScript(
160217
params,
161218
lookup,

0 commit comments

Comments
 (0)