1818import org .elasticsearch .common .Explicit ;
1919import org .elasticsearch .common .Strings ;
2020import org .elasticsearch .common .bytes .BytesReference ;
21+ import org .elasticsearch .common .settings .Setting ;
2122import org .elasticsearch .common .settings .Settings ;
2223import org .elasticsearch .common .util .CollectionUtils ;
2324import org .elasticsearch .core .Nullable ;
2425import org .elasticsearch .features .NodeFeature ;
2526import org .elasticsearch .index .IndexMode ;
27+ import org .elasticsearch .index .IndexSettings ;
2628import org .elasticsearch .index .IndexVersions ;
2729import org .elasticsearch .index .query .QueryShardException ;
2830import org .elasticsearch .index .query .SearchExecutionContext ;
@@ -62,8 +64,16 @@ public class SourceFieldMapper extends MetadataFieldMapper {
6264
6365 public static final String LOSSY_PARAMETERS_ALLOWED_SETTING_NAME = "index.lossy.source-mapping-parameters" ;
6466
67+ public static final Setting <Mode > INDEX_MAPPER_SOURCE_MODE_SETTING = Setting .enumSetting (SourceFieldMapper .Mode .class , settings -> {
68+ final IndexMode indexMode = IndexSettings .MODE .get (settings );
69+ return switch (indexMode ) {
70+ case IndexMode .LOGSDB , IndexMode .TIME_SERIES -> Mode .SYNTHETIC .name ();
71+ default -> Mode .STORED .name ();
72+ };
73+ }, "index.mapping.source.mode" , value -> {}, Setting .Property .Final , Setting .Property .IndexScope );
74+
6575 /** The source mode */
66- private enum Mode {
76+ public enum Mode {
6777 DISABLED ,
6878 STORED ,
6979 SYNTHETIC
@@ -96,6 +106,15 @@ private enum Mode {
96106 true
97107 );
98108
109+ private static final SourceFieldMapper TSDB_DEFAULT_STORED = new SourceFieldMapper (
110+ Mode .STORED ,
111+ Explicit .IMPLICIT_TRUE ,
112+ Strings .EMPTY_ARRAY ,
113+ Strings .EMPTY_ARRAY ,
114+ IndexMode .TIME_SERIES ,
115+ true
116+ );
117+
99118 private static final SourceFieldMapper TSDB_DEFAULT_NO_RECOVERY_SOURCE = new SourceFieldMapper (
100119 Mode .SYNTHETIC ,
101120 Explicit .IMPLICIT_TRUE ,
@@ -105,6 +124,15 @@ private enum Mode {
105124 false
106125 );
107126
127+ private static final SourceFieldMapper TSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED = new SourceFieldMapper (
128+ Mode .STORED ,
129+ Explicit .IMPLICIT_TRUE ,
130+ Strings .EMPTY_ARRAY ,
131+ Strings .EMPTY_ARRAY ,
132+ IndexMode .TIME_SERIES ,
133+ false
134+ );
135+
108136 private static final SourceFieldMapper LOGSDB_DEFAULT = new SourceFieldMapper (
109137 Mode .SYNTHETIC ,
110138 Explicit .IMPLICIT_TRUE ,
@@ -114,6 +142,15 @@ private enum Mode {
114142 true
115143 );
116144
145+ private static final SourceFieldMapper LOGSDB_DEFAULT_STORED = new SourceFieldMapper (
146+ Mode .STORED ,
147+ Explicit .IMPLICIT_TRUE ,
148+ Strings .EMPTY_ARRAY ,
149+ Strings .EMPTY_ARRAY ,
150+ IndexMode .LOGSDB ,
151+ true
152+ );
153+
117154 private static final SourceFieldMapper LOGSDB_DEFAULT_NO_RECOVERY_SOURCE = new SourceFieldMapper (
118155 Mode .SYNTHETIC ,
119156 Explicit .IMPLICIT_TRUE ,
@@ -123,6 +160,15 @@ private enum Mode {
123160 false
124161 );
125162
163+ private static final SourceFieldMapper LOGSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED = new SourceFieldMapper (
164+ Mode .STORED ,
165+ Explicit .IMPLICIT_TRUE ,
166+ Strings .EMPTY_ARRAY ,
167+ Strings .EMPTY_ARRAY ,
168+ IndexMode .LOGSDB ,
169+ false
170+ );
171+
126172 /*
127173 * Synthetic source was added as the default for TSDB in v.8.7. The legacy field mapper below
128174 * is used in bwc tests and mixed clusters containing time series indexes created in an earlier version.
@@ -197,6 +243,8 @@ public static class Builder extends MetadataFieldMapper.Builder {
197243 m -> Arrays .asList (toType (m ).excludes )
198244 );
199245
246+ private final Settings settings ;
247+
200248 private final IndexMode indexMode ;
201249
202250 private final boolean supportsNonDefaultParameterValues ;
@@ -210,6 +258,7 @@ public Builder(
210258 boolean enableRecoverySource
211259 ) {
212260 super (Defaults .NAME );
261+ this .settings = settings ;
213262 this .indexMode = indexMode ;
214263 this .supportsNonDefaultParameterValues = supportsCheckForNonDefaultParams == false
215264 || settings .getAsBoolean (LOSSY_PARAMETERS_ALLOWED_SETTING_NAME , true );
@@ -226,10 +275,10 @@ protected Parameter<?>[] getParameters() {
226275 return new Parameter <?>[] { enabled , mode , includes , excludes };
227276 }
228277
229- private boolean isDefault () {
230- Mode m = mode . get ();
231- if ( m != null
232- && ((( indexMode != null && indexMode . isSyntheticSourceEnabled () && m == Mode . SYNTHETIC ) == false ) || m == Mode .DISABLED )) {
278+ private boolean isDefault (final Mode sourceMode ) {
279+ if ( sourceMode != null
280+ && ((( indexMode != null && indexMode . isSyntheticSourceEnabled () && sourceMode == Mode . SYNTHETIC ) == false )
281+ || sourceMode == Mode .DISABLED )) {
233282 return false ;
234283 }
235284 return enabled .get ().value () && includes .getValue ().isEmpty () && excludes .getValue ().isEmpty ();
@@ -242,12 +291,14 @@ public SourceFieldMapper build() {
242291 throw new MapperParsingException ("Cannot set both [mode] and [enabled] parameters" );
243292 }
244293 }
245- if (isDefault ()) {
246- return switch (indexMode ) {
247- case TIME_SERIES -> enableRecoverySource ? TSDB_DEFAULT : TSDB_DEFAULT_NO_RECOVERY_SOURCE ;
248- case LOGSDB -> enableRecoverySource ? LOGSDB_DEFAULT : LOGSDB_DEFAULT_NO_RECOVERY_SOURCE ;
249- default -> enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE ;
250- };
294+ // NOTE: if the `index.mapper.source.mode` exists it takes precedence to determine the source mode for `_source`
295+ // otherwise the mode is determined according to `index.mode` and `_source.mode`.
296+ final Mode sourceMode = INDEX_MAPPER_SOURCE_MODE_SETTING .exists (settings )
297+ ? INDEX_MAPPER_SOURCE_MODE_SETTING .get (settings )
298+ : mode .get ();
299+ if (isDefault (sourceMode )) {
300+ return resolveSourceMode (indexMode , sourceMode , enableRecoverySource );
301+
251302 }
252303 if (supportsNonDefaultParameterValues == false ) {
253304 List <String > disallowed = new ArrayList <>();
@@ -271,8 +322,9 @@ public SourceFieldMapper build() {
271322 );
272323 }
273324 }
325+
274326 SourceFieldMapper sourceFieldMapper = new SourceFieldMapper (
275- mode . get () ,
327+ sourceMode ,
276328 enabled .get (),
277329 includes .getValue ().toArray (Strings .EMPTY_ARRAY ),
278330 excludes .getValue ().toArray (Strings .EMPTY_ARRAY ),
@@ -287,21 +339,39 @@ public SourceFieldMapper build() {
287339
288340 }
289341
342+ 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 ;
345+ }
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+ };
362+ }
363+
290364 public static final TypeParser PARSER = new ConfigurableTypeParser (c -> {
291- var indexMode = c .getIndexSettings ().getMode ();
365+ final IndexMode indexMode = c .getIndexSettings ().getMode ();
292366 boolean enableRecoverySource = INDICES_RECOVERY_SOURCE_ENABLED_SETTING .get (c .getSettings ());
367+ final Mode settingSourceMode = INDEX_MAPPER_SOURCE_MODE_SETTING .get (c .getSettings ());
368+
293369 if (indexMode .isSyntheticSourceEnabled ()) {
294- if (indexMode == IndexMode .TIME_SERIES ) {
295- if (c .getIndexSettings ().getIndexVersionCreated ().onOrAfter (IndexVersions .V_8_7_0 )) {
296- return enableRecoverySource ? TSDB_DEFAULT : TSDB_DEFAULT_NO_RECOVERY_SOURCE ;
297- } else {
298- return enableRecoverySource ? TSDB_LEGACY_DEFAULT : TSDB_LEGACY_DEFAULT_NO_RECOVERY_SOURCE ;
299- }
300- } else if (indexMode == IndexMode .LOGSDB ) {
301- return enableRecoverySource ? LOGSDB_DEFAULT : LOGSDB_DEFAULT_NO_RECOVERY_SOURCE ;
370+ if (indexMode == IndexMode .TIME_SERIES && c .getIndexSettings ().getIndexVersionCreated ().before (IndexVersions .V_8_7_0 )) {
371+ return enableRecoverySource ? TSDB_LEGACY_DEFAULT : TSDB_LEGACY_DEFAULT_NO_RECOVERY_SOURCE ;
302372 }
303373 }
304- return enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE ;
374+ return resolveSourceMode ( indexMode , settingSourceMode , enableRecoverySource ) ;
305375 },
306376 c -> new Builder (
307377 c .getIndexSettings ().getMode (),
0 commit comments