Skip to content

Commit 3af4d67

Browse files
Allow synthetic source and disabled source for standard indices (#114817)
When using the index.mapping.source.mode setting we need to make sure that it takes precedence and that is used also when standard index mode is used. Without this patch we always return stored source if _source.mode is not used and the setting is. Relates #114433
1 parent e87b894 commit 3af4d67

File tree

2 files changed

+239
-20
lines changed

2 files changed

+239
-20
lines changed

server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,42 @@ public enum Mode {
8888
true
8989
);
9090

91+
private static final SourceFieldMapper DEFAULT_DISABLED = new SourceFieldMapper(
92+
Mode.DISABLED,
93+
Explicit.IMPLICIT_TRUE,
94+
Strings.EMPTY_ARRAY,
95+
Strings.EMPTY_ARRAY,
96+
null,
97+
true
98+
);
99+
100+
private static final SourceFieldMapper DEFAULT_DISABLED_NO_RECOVERY_SOURCE = new SourceFieldMapper(
101+
Mode.DISABLED,
102+
Explicit.IMPLICIT_TRUE,
103+
Strings.EMPTY_ARRAY,
104+
Strings.EMPTY_ARRAY,
105+
null,
106+
false
107+
);
108+
109+
private static final SourceFieldMapper DEFAULT_SYNTHETIC = new SourceFieldMapper(
110+
Mode.SYNTHETIC,
111+
Explicit.IMPLICIT_TRUE,
112+
Strings.EMPTY_ARRAY,
113+
Strings.EMPTY_ARRAY,
114+
null,
115+
true
116+
);
117+
118+
private static final SourceFieldMapper DEFAULT_SYNTHETIC_NO_RECOVERY_SOURCE = new SourceFieldMapper(
119+
Mode.SYNTHETIC,
120+
Explicit.IMPLICIT_TRUE,
121+
Strings.EMPTY_ARRAY,
122+
Strings.EMPTY_ARRAY,
123+
null,
124+
false
125+
);
126+
91127
private static final SourceFieldMapper DEFAULT_NO_RECOVERY_SOURCE = new SourceFieldMapper(
92128
null,
93129
Explicit.IMPLICIT_TRUE,
@@ -297,7 +333,7 @@ public SourceFieldMapper build() {
297333
? INDEX_MAPPER_SOURCE_MODE_SETTING.get(settings)
298334
: mode.get();
299335
if (isDefault(sourceMode)) {
300-
return resolveSourceMode(indexMode, sourceMode, enableRecoverySource);
336+
return resolveSourceMode(indexMode, sourceMode == null ? Mode.STORED : sourceMode, enableRecoverySource);
301337

302338
}
303339
if (supportsNonDefaultParameterValues == false) {
@@ -340,25 +376,39 @@ public SourceFieldMapper build() {
340376
}
341377

342378
private static SourceFieldMapper resolveSourceMode(final IndexMode indexMode, final Mode sourceMode, boolean enableRecoverySource) {
343-
if (indexMode == IndexMode.STANDARD) {
344-
return enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE;
379+
switch (indexMode) {
380+
case STANDARD:
381+
switch (sourceMode) {
382+
case SYNTHETIC:
383+
return enableRecoverySource ? DEFAULT_SYNTHETIC : DEFAULT_SYNTHETIC_NO_RECOVERY_SOURCE;
384+
case STORED:
385+
return enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE;
386+
case DISABLED:
387+
return enableRecoverySource ? DEFAULT_DISABLED : DEFAULT_DISABLED_NO_RECOVERY_SOURCE;
388+
default:
389+
throw new IllegalArgumentException("Unsupported source mode: " + sourceMode);
390+
}
391+
case TIME_SERIES:
392+
case LOGSDB:
393+
switch (sourceMode) {
394+
case SYNTHETIC:
395+
return enableRecoverySource
396+
? (indexMode == IndexMode.TIME_SERIES ? TSDB_DEFAULT : LOGSDB_DEFAULT)
397+
: (indexMode == IndexMode.TIME_SERIES ? TSDB_DEFAULT_NO_RECOVERY_SOURCE : LOGSDB_DEFAULT_NO_RECOVERY_SOURCE);
398+
case STORED:
399+
return enableRecoverySource
400+
? (indexMode == IndexMode.TIME_SERIES ? TSDB_DEFAULT_STORED : LOGSDB_DEFAULT_STORED)
401+
: (indexMode == IndexMode.TIME_SERIES
402+
? TSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED
403+
: LOGSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED);
404+
case DISABLED:
405+
throw new IllegalArgumentException("_source can not be disabled in index using [" + indexMode + "] index mode");
406+
default:
407+
throw new IllegalArgumentException("Unsupported source mode: " + sourceMode);
408+
}
409+
default:
410+
throw new IllegalArgumentException("Unsupported index mode: " + indexMode);
345411
}
346-
final SourceFieldMapper syntheticWithoutRecoverySource = indexMode == IndexMode.TIME_SERIES
347-
? TSDB_DEFAULT_NO_RECOVERY_SOURCE
348-
: LOGSDB_DEFAULT_NO_RECOVERY_SOURCE;
349-
final SourceFieldMapper syntheticWithRecoverySource = indexMode == IndexMode.TIME_SERIES ? TSDB_DEFAULT : LOGSDB_DEFAULT;
350-
final SourceFieldMapper storedWithoutRecoverySource = indexMode == IndexMode.TIME_SERIES
351-
? TSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED
352-
: LOGSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED;
353-
final SourceFieldMapper storedWithRecoverySource = indexMode == IndexMode.TIME_SERIES ? TSDB_DEFAULT_STORED : LOGSDB_DEFAULT_STORED;
354-
355-
return switch (sourceMode) {
356-
case SYNTHETIC -> enableRecoverySource ? syntheticWithRecoverySource : syntheticWithoutRecoverySource;
357-
case STORED -> enableRecoverySource ? storedWithRecoverySource : storedWithoutRecoverySource;
358-
case DISABLED -> throw new IllegalArgumentException(
359-
"_source can not be disabled in index using [" + indexMode + "] index mode"
360-
);
361-
};
362412
}
363413

364414
public static final TypeParser PARSER = new ConfigurableTypeParser(c -> {
@@ -371,7 +421,7 @@ private static SourceFieldMapper resolveSourceMode(final IndexMode indexMode, fi
371421
return enableRecoverySource ? TSDB_LEGACY_DEFAULT : TSDB_LEGACY_DEFAULT_NO_RECOVERY_SOURCE;
372422
}
373423
}
374-
return resolveSourceMode(indexMode, settingSourceMode, enableRecoverySource);
424+
return resolveSourceMode(indexMode, settingSourceMode == null ? Mode.STORED : settingSourceMode, enableRecoverySource);
375425
},
376426
c -> new Builder(
377427
c.getIndexSettings().getMode(),
@@ -541,4 +591,12 @@ public SourceLoader newSourceLoader(Mapping mapping, SourceFieldMetrics metrics)
541591
public boolean isSynthetic() {
542592
return mode == Mode.SYNTHETIC;
543593
}
594+
595+
public boolean isDisabled() {
596+
return mode == Mode.DISABLED;
597+
}
598+
599+
public boolean isStored() {
600+
return mode == null || mode == Mode.STORED;
601+
}
544602
}

server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,167 @@ public void testRecoverySourceWithLogs() throws IOException {
443443
}
444444
}
445445

446+
public void testStandardIndexModeWithSourceModeSetting() throws IOException {
447+
// Test for IndexMode.STANDARD
448+
{
449+
final XContentBuilder mappings = topMapping(b -> {});
450+
final Settings settings = Settings.builder()
451+
.put(IndexSettings.MODE.getKey(), IndexMode.STANDARD.name())
452+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.SYNTHETIC)
453+
.build();
454+
final MapperService mapperService = createMapperService(settings, mappings);
455+
DocumentMapper docMapper = mapperService.documentMapper();
456+
assertTrue(docMapper.sourceMapper().isSynthetic());
457+
}
458+
{
459+
final XContentBuilder mappings = topMapping(b -> {});
460+
final Settings settings = Settings.builder()
461+
.put(IndexSettings.MODE.getKey(), IndexMode.STANDARD.name())
462+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.STORED)
463+
.build();
464+
final MapperService mapperService = createMapperService(settings, mappings);
465+
final DocumentMapper docMapper = mapperService.documentMapper();
466+
assertTrue(docMapper.sourceMapper().isStored());
467+
}
468+
{
469+
final XContentBuilder mappings = topMapping(b -> {});
470+
final Settings settings = Settings.builder()
471+
.put(IndexSettings.MODE.getKey(), IndexMode.STANDARD.name())
472+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.DISABLED)
473+
.build();
474+
final MapperService mapperService = createMapperService(settings, mappings);
475+
final DocumentMapper docMapper = mapperService.documentMapper();
476+
assertTrue(docMapper.sourceMapper().isDisabled());
477+
}
478+
479+
// Test for IndexMode.LOGSDB
480+
{
481+
final XContentBuilder mappings = topMapping(b -> {});
482+
final Settings settings = Settings.builder()
483+
.put(IndexSettings.MODE.getKey(), IndexMode.LOGSDB.name())
484+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.SYNTHETIC)
485+
.build();
486+
final MapperService mapperService = createMapperService(settings, mappings);
487+
DocumentMapper docMapper = mapperService.documentMapper();
488+
assertTrue(docMapper.sourceMapper().isSynthetic());
489+
}
490+
{
491+
final XContentBuilder mappings = topMapping(b -> {});
492+
final Settings settings = Settings.builder()
493+
.put(IndexSettings.MODE.getKey(), IndexMode.LOGSDB.name())
494+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.STORED)
495+
.build();
496+
final MapperService mapperService = createMapperService(settings, mappings);
497+
final DocumentMapper docMapper = mapperService.documentMapper();
498+
assertTrue(docMapper.sourceMapper().isStored());
499+
}
500+
{
501+
final XContentBuilder mappings = topMapping(b -> {});
502+
final Settings settings = Settings.builder()
503+
.put(IndexSettings.MODE.getKey(), IndexMode.LOGSDB.name())
504+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.DISABLED)
505+
.build();
506+
var ex = expectThrows(MapperParsingException.class, () -> createMapperService(settings, mappings));
507+
assertEquals("Failed to parse mapping: _source can not be disabled in index using [logsdb] index mode", ex.getMessage());
508+
}
509+
510+
// Test for IndexMode.TIME_SERIES
511+
{
512+
final String mappings = """
513+
{
514+
"_doc" : {
515+
"properties": {
516+
"routing_field": {
517+
"type": "keyword",
518+
"time_series_dimension": true
519+
}
520+
}
521+
}
522+
}
523+
""";
524+
final Settings settings = Settings.builder()
525+
.put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES.name())
526+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.SYNTHETIC)
527+
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "routing_field")
528+
.build();
529+
final MapperService mapperService = createMapperService(settings, mappings);
530+
DocumentMapper docMapper = mapperService.documentMapper();
531+
assertTrue(docMapper.sourceMapper().isSynthetic());
532+
}
533+
{
534+
final String mappings = """
535+
{
536+
"_doc" : {
537+
"properties": {
538+
"routing_field": {
539+
"type": "keyword",
540+
"time_series_dimension": true
541+
}
542+
}
543+
}
544+
}
545+
""";
546+
final Settings settings = Settings.builder()
547+
.put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES.name())
548+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.STORED)
549+
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "routing_field")
550+
.build();
551+
final MapperService mapperService = createMapperService(settings, mappings);
552+
final DocumentMapper docMapper = mapperService.documentMapper();
553+
assertTrue(docMapper.sourceMapper().isStored());
554+
}
555+
{
556+
final String mappings = """
557+
{
558+
"_doc" : {
559+
"properties": {
560+
"routing_field": {
561+
"type": "keyword",
562+
"time_series_dimension": true
563+
}
564+
}
565+
}
566+
}
567+
""";
568+
final Settings settings = Settings.builder()
569+
.put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES.name())
570+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.DISABLED)
571+
.put(IndexMetadata.INDEX_ROUTING_PATH.getKey(), "routing_field")
572+
.build();
573+
var ex = expectThrows(MapperParsingException.class, () -> createMapperService(settings, mappings));
574+
assertEquals("Failed to parse mapping: _source can not be disabled in index using [time_series] index mode", ex.getMessage());
575+
}
576+
577+
// Test cases without IndexMode (default to standard)
578+
{
579+
final XContentBuilder mappings = topMapping(b -> {});
580+
final Settings settings = Settings.builder()
581+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.SYNTHETIC)
582+
.build();
583+
final MapperService mapperService = createMapperService(settings, mappings);
584+
DocumentMapper docMapper = mapperService.documentMapper();
585+
assertTrue(docMapper.sourceMapper().isSynthetic());
586+
}
587+
{
588+
final XContentBuilder mappings = topMapping(b -> {});
589+
final Settings settings = Settings.builder()
590+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.STORED)
591+
.build();
592+
final MapperService mapperService = createMapperService(settings, mappings);
593+
final DocumentMapper docMapper = mapperService.documentMapper();
594+
assertTrue(docMapper.sourceMapper().isStored());
595+
}
596+
{
597+
final XContentBuilder mappings = topMapping(b -> {});
598+
final Settings settings = Settings.builder()
599+
.put(SourceFieldMapper.INDEX_MAPPER_SOURCE_MODE_SETTING.getKey(), SourceFieldMapper.Mode.DISABLED)
600+
.build();
601+
final MapperService mapperService = createMapperService(settings, mappings);
602+
final DocumentMapper docMapper = mapperService.documentMapper();
603+
assertTrue(docMapper.sourceMapper().isDisabled());
604+
}
605+
}
606+
446607
public void testRecoverySourceWithLogsCustom() throws IOException {
447608
XContentBuilder mappings = topMapping(b -> b.startObject(SourceFieldMapper.NAME).field("mode", "synthetic").endObject());
448609
{

0 commit comments

Comments
 (0)