Skip to content

Commit 082e721

Browse files
authored
Use fallback synthetic source for copy_to and doc_values: false cases (#112294)
1 parent 69ac8f8 commit 082e721

File tree

73 files changed

+963
-1086
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+963
-1086
lines changed

docs/changelog/112294.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
pr: 112294
2+
summary: "Use fallback synthetic source for `copy_to` and doc_values: false cases"
3+
area: Mapping
4+
type: enhancement
5+
issues:
6+
- 110753
7+
- 110038
8+
- 109546

modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/logsdb/LogsIndexModeCustomSettingsIT.java

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -283,35 +283,6 @@ public void testOverrideIgnoreDynamicBeyondLimit() throws IOException {
283283
assertThat(ignoreDynamicBeyondLimitIndexSetting, equalTo("false"));
284284
}
285285

286-
public void testAddNonCompatibleMapping() throws IOException {
287-
var nonCompatibleMappingAdditionTemplate = """
288-
{
289-
"template": {
290-
"mappings": {
291-
"properties": {
292-
"bomb": {
293-
"type": "ip",
294-
"doc_values": false
295-
}
296-
}
297-
}
298-
}
299-
}""";
300-
301-
Exception e = assertThrows(
302-
ResponseException.class,
303-
() -> putComponentTemplate(client, "logs@custom", nonCompatibleMappingAdditionTemplate)
304-
);
305-
assertThat(
306-
e.getMessage(),
307-
containsString("updating component template [logs@custom] results in invalid composable template [logs]")
308-
);
309-
assertThat(
310-
e.getMessage(),
311-
containsString("field [bomb] of type [ip] doesn't support synthetic source because it doesn't have doc values")
312-
);
313-
}
314-
315286
private static Map<String, Object> getMapping(final RestClient client, final String indexName) throws IOException {
316287
final Request request = new Request("GET", "/" + indexName + "/_mapping");
317288

modules/data-streams/src/javaRestTest/java/org/elasticsearch/datastreams/logsdb/qa/StandardVersusLogsIndexModeRandomDataChallengeRestIT.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import org.elasticsearch.logsdb.datageneration.DataGenerator;
1818
import org.elasticsearch.logsdb.datageneration.DataGeneratorSpecification;
1919
import org.elasticsearch.logsdb.datageneration.FieldDataGenerator;
20-
import org.elasticsearch.logsdb.datageneration.FieldType;
2120
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceHandler;
2221
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceRequest;
2322
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceResponse;
@@ -78,7 +77,18 @@ public DataSourceResponse.ObjectMappingParametersGenerator handle(DataSourceRequ
7877
}))
7978
.withPredefinedFields(
8079
List.of(
81-
new PredefinedField.WithType("host.name", FieldType.KEYWORD),
80+
// Customized because it always needs doc_values for aggregations.
81+
new PredefinedField.WithGenerator("host.name", new FieldDataGenerator() {
82+
@Override
83+
public CheckedConsumer<XContentBuilder, IOException> mappingWriter() {
84+
return b -> b.startObject().field("type", "keyword").endObject();
85+
}
86+
87+
@Override
88+
public CheckedConsumer<XContentBuilder, IOException> fieldValueGenerator() {
89+
return b -> b.value(randomAlphaOfLength(5));
90+
}
91+
}),
8292
// Needed for terms query
8393
new PredefinedField.WithGenerator("method", new FieldDataGenerator() {
8494
@Override

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import org.elasticsearch.index.mapper.DocumentParserContext;
4646
import org.elasticsearch.index.mapper.FieldMapper;
4747
import org.elasticsearch.index.mapper.MapperBuilderContext;
48-
import org.elasticsearch.index.mapper.SourceLoader;
4948
import org.elasticsearch.index.mapper.SourceValueFetcher;
5049
import org.elasticsearch.index.mapper.StringFieldType;
5150
import org.elasticsearch.index.mapper.StringStoredFieldFieldLoader;
@@ -433,22 +432,14 @@ public MatchOnlyTextFieldType fieldType() {
433432
}
434433

435434
@Override
436-
protected SyntheticSourceMode syntheticSourceMode() {
437-
return SyntheticSourceMode.NATIVE;
438-
}
439-
440-
@Override
441-
public SourceLoader.SyntheticFieldLoader syntheticFieldLoader() {
442-
if (copyTo().copyToFields().isEmpty() != true) {
443-
throw new IllegalArgumentException(
444-
"field [" + fullPath() + "] of type [" + typeName() + "] doesn't support synthetic source because it declares copy_to"
445-
);
446-
}
447-
return new StringStoredFieldFieldLoader(fieldType().storedFieldNameForSyntheticSource(), leafName()) {
435+
protected SyntheticSourceSupport syntheticSourceSupport() {
436+
var loader = new StringStoredFieldFieldLoader(fieldType().storedFieldNameForSyntheticSource(), leafName()) {
448437
@Override
449438
protected void write(XContentBuilder b, Object value) throws IOException {
450439
b.value((String) value);
451440
}
452441
};
442+
443+
return new SyntheticSourceSupport.Native(loader);
453444
}
454445
}

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapper.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.apache.lucene.search.Query;
1313
import org.elasticsearch.index.mapper.MappedFieldType;
1414
import org.elasticsearch.index.mapper.MetadataFieldMapper;
15-
import org.elasticsearch.index.mapper.SourceLoader;
1615
import org.elasticsearch.index.mapper.TextSearchInfo;
1716
import org.elasticsearch.index.mapper.ValueFetcher;
1817
import org.elasticsearch.index.query.SearchExecutionContext;
@@ -75,9 +74,4 @@ private RankFeatureMetaFieldMapper() {
7574
protected String contentType() {
7675
return CONTENT_TYPE;
7776
}
78-
79-
@Override
80-
public SourceLoader.SyntheticFieldLoader syntheticFieldLoader() {
81-
return SourceLoader.SyntheticFieldLoader.NOTHING;
82-
}
8377
}

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapper.java

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.elasticsearch.index.mapper.NumberFieldMapper;
3939
import org.elasticsearch.index.mapper.SimpleMappedFieldType;
4040
import org.elasticsearch.index.mapper.SortedNumericDocValuesSyntheticFieldLoader;
41-
import org.elasticsearch.index.mapper.SourceLoader;
4241
import org.elasticsearch.index.mapper.SourceValueFetcher;
4342
import org.elasticsearch.index.mapper.TextSearchInfo;
4443
import org.elasticsearch.index.mapper.TimeSeriesParams;
@@ -705,32 +704,19 @@ public int docValueCount() {
705704
}
706705

707706
@Override
708-
protected SyntheticSourceMode syntheticSourceMode() {
709-
return SyntheticSourceMode.NATIVE;
710-
}
707+
protected SyntheticSourceSupport syntheticSourceSupport() {
708+
if (hasDocValues) {
709+
var loader = new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value()) {
710+
@Override
711+
protected void writeValue(XContentBuilder b, long value) throws IOException {
712+
b.value(decodeForSyntheticSource(value, scalingFactor));
713+
}
714+
};
711715

712-
@Override
713-
public SourceLoader.SyntheticFieldLoader syntheticFieldLoader() {
714-
if (hasDocValues == false) {
715-
throw new IllegalArgumentException(
716-
"field ["
717-
+ fullPath()
718-
+ "] of type ["
719-
+ typeName()
720-
+ "] doesn't support synthetic source because it doesn't have doc values"
721-
);
722-
}
723-
if (copyTo().copyToFields().isEmpty() != true) {
724-
throw new IllegalArgumentException(
725-
"field [" + fullPath() + "] of type [" + typeName() + "] doesn't support synthetic source because it declares copy_to"
726-
);
716+
return new SyntheticSourceSupport.Native(loader);
727717
}
728-
return new SortedNumericDocValuesSyntheticFieldLoader(fullPath(), leafName(), ignoreMalformed.value()) {
729-
@Override
730-
protected void writeValue(XContentBuilder b, long value) throws IOException {
731-
b.value(decodeForSyntheticSource(value, scalingFactor));
732-
}
733-
};
718+
719+
return super.syntheticSourceSupport();
734720
}
735721

736722
/**

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/ScaledFloatFieldMapperTests.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -470,12 +470,7 @@ private void mapping(XContentBuilder b) throws IOException {
470470

471471
@Override
472472
public List<SyntheticSourceInvalidExample> invalidExample() throws IOException {
473-
return List.of(
474-
new SyntheticSourceInvalidExample(
475-
equalTo("field [field] of type [scaled_float] doesn't support synthetic source because it doesn't have doc values"),
476-
b -> b.field("type", "scaled_float").field("scaling_factor", 10).field("doc_values", false)
477-
)
478-
);
473+
return List.of();
479474
}
480475
}
481476

modules/mapper-extras/src/yamlRestTest/resources/rest-api-spec/test/match_only_text/10_basic.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,3 +351,45 @@ tsdb:
351351
"@timestamp" : "2000-01-01T00:00:00.000Z"
352352
"dimension" : "a"
353353
foo: "Apache Lucene powers Elasticsearch"
354+
355+
---
356+
synthetic_source with copy_to:
357+
- requires:
358+
cluster_features: ["mapper.source.synthetic_source_with_copy_to_and_doc_values_false"]
359+
reason: requires copy_to support in synthetic source
360+
361+
- do:
362+
indices.create:
363+
index: synthetic_source_test
364+
body:
365+
mappings:
366+
_source:
367+
mode: synthetic
368+
properties:
369+
foo:
370+
type: match_only_text
371+
copy_to: copy
372+
copy:
373+
type: keyword
374+
375+
- do:
376+
index:
377+
index: synthetic_source_test
378+
id: "1"
379+
refresh: true
380+
body:
381+
foo: "Apache Lucene powers Elasticsearch"
382+
383+
- do:
384+
search:
385+
index: synthetic_source_test
386+
body:
387+
fields: ["copy"]
388+
389+
- match: { "hits.total.value": 1 }
390+
- match:
391+
hits.hits.0._source.foo: "Apache Lucene powers Elasticsearch"
392+
- match:
393+
hits.hits.0.fields.copy.0: "Apache Lucene powers Elasticsearch"
394+
395+

plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapper.java

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import org.elasticsearch.index.mapper.FieldMapper;
3131
import org.elasticsearch.index.mapper.KeywordFieldMapper;
3232
import org.elasticsearch.index.mapper.MapperBuilderContext;
33-
import org.elasticsearch.index.mapper.SourceLoader;
3433
import org.elasticsearch.index.mapper.StringStoredFieldFieldLoader;
3534
import org.elasticsearch.index.mapper.TextFieldMapper;
3635
import org.elasticsearch.index.mapper.TextParams;
@@ -46,7 +45,6 @@
4645
import java.nio.charset.StandardCharsets;
4746
import java.util.ArrayList;
4847
import java.util.List;
49-
import java.util.Locale;
5048
import java.util.Map;
5149
import java.util.regex.Matcher;
5250
import java.util.regex.Pattern;
@@ -570,39 +568,23 @@ public FieldMapper.Builder getMergeBuilder() {
570568
}
571569

572570
@Override
573-
protected SyntheticSourceMode syntheticSourceMode() {
574-
return SyntheticSourceMode.NATIVE;
575-
}
576-
577-
@Override
578-
public SourceLoader.SyntheticFieldLoader syntheticFieldLoader() {
579-
if (copyTo().copyToFields().isEmpty() != true) {
580-
throw new IllegalArgumentException(
581-
"field [" + fullPath() + "] of type [" + typeName() + "] doesn't support synthetic source because it declares copy_to"
582-
);
583-
}
571+
protected SyntheticSourceSupport syntheticSourceSupport() {
584572
if (fieldType.stored()) {
585-
return new StringStoredFieldFieldLoader(fullPath(), leafName()) {
573+
var loader = new StringStoredFieldFieldLoader(fullPath(), leafName()) {
586574
@Override
587575
protected void write(XContentBuilder b, Object value) throws IOException {
588576
b.value((String) value);
589577
}
590578
};
579+
580+
return new SyntheticSourceSupport.Native(loader);
591581
}
592582

593583
var kwd = TextFieldMapper.SyntheticSourceHelper.getKeywordFieldMapperForSyntheticSource(this);
594584
if (kwd != null) {
595-
return kwd.syntheticFieldLoader(leafName());
585+
return new SyntheticSourceSupport.Native(kwd.syntheticFieldLoader(leafName()));
596586
}
597587

598-
throw new IllegalArgumentException(
599-
String.format(
600-
Locale.ROOT,
601-
"field [%s] of type [%s] doesn't support synthetic source unless it is stored or has a sub-field of"
602-
+ " type [keyword] with doc values or stored and without a normalizer",
603-
fullPath(),
604-
typeName()
605-
)
606-
);
588+
return super.syntheticSourceSupport();
607589
}
608590
}

plugins/mapper-annotated-text/src/yamlRestTest/resources/rest-api-spec/test/mapper_annotatedtext/20_synthetic_source.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,3 +195,34 @@ multiple values in stored annotated_text field with keyword multi-field:
195195
- match:
196196
hits.hits.0._source:
197197
annotated_text: ["world", "hello", "world"]
198+
199+
---
200+
fallback synthetic source:
201+
- do:
202+
indices.create:
203+
index: test
204+
body:
205+
mappings:
206+
_source:
207+
mode: synthetic
208+
properties:
209+
annotated_text:
210+
type: annotated_text
211+
store: false
212+
213+
- do:
214+
index:
215+
index: test
216+
id: 1
217+
refresh: true
218+
body:
219+
annotated_text: ["world", "hello", "world"]
220+
221+
- do:
222+
search:
223+
index: test
224+
225+
- match:
226+
hits.hits.0._source:
227+
annotated_text: ["world", "hello", "world"]
228+

0 commit comments

Comments
 (0)