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 ;
@@ -59,8 +61,20 @@ public class SourceFieldMapper extends MetadataFieldMapper {
5961
6062 public static final String LOSSY_PARAMETERS_ALLOWED_SETTING_NAME = "index.lossy.source-mapping-parameters" ;
6163
64+ public static final Setting <Mode > INDEX_MAPPER_SOURCE_MODE_SETTING = Setting .enumSetting (SourceFieldMapper .Mode .class , settings -> {
65+ final IndexMode indexMode = IndexSettings .MODE .get (settings );
66+
67+ switch (indexMode ) {
68+ case LOGSDB :
69+ case TIME_SERIES :
70+ return Mode .SYNTHETIC .name ();
71+ default :
72+ return Mode .STORED .name ();
73+ }
74+ }, "index.mapping.source.mode" , value -> {}, Setting .Property .Final , Setting .Property .IndexScope );
75+
6276 /** The source mode */
63- private enum Mode {
77+ public enum Mode {
6478 DISABLED ,
6579 STORED ,
6680 SYNTHETIC
@@ -93,6 +107,15 @@ private enum Mode {
93107 true
94108 );
95109
110+ private static final SourceFieldMapper TSDB_DEFAULT_STORED = new SourceFieldMapper (
111+ Mode .STORED ,
112+ Explicit .IMPLICIT_TRUE ,
113+ Strings .EMPTY_ARRAY ,
114+ Strings .EMPTY_ARRAY ,
115+ IndexMode .TIME_SERIES ,
116+ true
117+ );
118+
96119 private static final SourceFieldMapper TSDB_DEFAULT_NO_RECOVERY_SOURCE = new SourceFieldMapper (
97120 Mode .SYNTHETIC ,
98121 Explicit .IMPLICIT_TRUE ,
@@ -102,6 +125,15 @@ private enum Mode {
102125 false
103126 );
104127
128+ private static final SourceFieldMapper TSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED = new SourceFieldMapper (
129+ Mode .STORED ,
130+ Explicit .IMPLICIT_TRUE ,
131+ Strings .EMPTY_ARRAY ,
132+ Strings .EMPTY_ARRAY ,
133+ IndexMode .TIME_SERIES ,
134+ false
135+ );
136+
105137 private static final SourceFieldMapper LOGSDB_DEFAULT = new SourceFieldMapper (
106138 Mode .SYNTHETIC ,
107139 Explicit .IMPLICIT_TRUE ,
@@ -111,6 +143,15 @@ private enum Mode {
111143 true
112144 );
113145
146+ private static final SourceFieldMapper LOGSDB_DEFAULT_STORED = new SourceFieldMapper (
147+ Mode .STORED ,
148+ Explicit .IMPLICIT_TRUE ,
149+ Strings .EMPTY_ARRAY ,
150+ Strings .EMPTY_ARRAY ,
151+ IndexMode .LOGSDB ,
152+ true
153+ );
154+
114155 private static final SourceFieldMapper LOGSDB_DEFAULT_NO_RECOVERY_SOURCE = new SourceFieldMapper (
115156 Mode .SYNTHETIC ,
116157 Explicit .IMPLICIT_TRUE ,
@@ -120,6 +161,15 @@ private enum Mode {
120161 false
121162 );
122163
164+ private static final SourceFieldMapper LOGSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED = new SourceFieldMapper (
165+ Mode .STORED ,
166+ Explicit .IMPLICIT_TRUE ,
167+ Strings .EMPTY_ARRAY ,
168+ Strings .EMPTY_ARRAY ,
169+ IndexMode .LOGSDB ,
170+ false
171+ );
172+
123173 /*
124174 * Synthetic source was added as the default for TSDB in v.8.7. The legacy field mapper below
125175 * is used in bwc tests and mixed clusters containing time series indexes created in an earlier version.
@@ -194,6 +244,8 @@ public static class Builder extends MetadataFieldMapper.Builder {
194244 m -> Arrays .asList (toType (m ).excludes )
195245 );
196246
247+ private final Settings settings ;
248+
197249 private final IndexMode indexMode ;
198250
199251 private final boolean supportsNonDefaultParameterValues ;
@@ -207,6 +259,7 @@ public Builder(
207259 boolean enableRecoverySource
208260 ) {
209261 super (Defaults .NAME );
262+ this .settings = settings ;
210263 this .indexMode = indexMode ;
211264 this .supportsNonDefaultParameterValues = supportsCheckForNonDefaultParams == false
212265 || settings .getAsBoolean (LOSSY_PARAMETERS_ALLOWED_SETTING_NAME , true );
@@ -223,10 +276,10 @@ protected Parameter<?>[] getParameters() {
223276 return new Parameter <?>[] { enabled , mode , includes , excludes };
224277 }
225278
226- private boolean isDefault () {
227- Mode m = mode . get ();
228- if ( m != null
229- && ((( indexMode != null && indexMode . isSyntheticSourceEnabled () && m == Mode . SYNTHETIC ) == false ) || m == Mode .DISABLED )) {
279+ private boolean isDefault (final Mode sourceMode ) {
280+ if ( sourceMode != null
281+ && ((( indexMode != null && indexMode . isSyntheticSourceEnabled () && sourceMode == Mode . SYNTHETIC ) == false )
282+ || sourceMode == Mode .DISABLED )) {
230283 return false ;
231284 }
232285 return enabled .get ().value () && includes .getValue ().isEmpty () && excludes .getValue ().isEmpty ();
@@ -242,12 +295,14 @@ public SourceFieldMapper build() {
242295 throw new MapperParsingException ("Cannot set both [mode] and [enabled] parameters" );
243296 }
244297 }
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- };
298+ // NOTE: if the `index.mapper.source.mode` exists it takes precedence to determine the source mode for `_source`
299+ // otherwise the mode is determined according to `index.mode` and `_source.mode`.
300+ final Mode sourceMode = INDEX_MAPPER_SOURCE_MODE_SETTING .exists (settings )
301+ ? INDEX_MAPPER_SOURCE_MODE_SETTING .get (settings )
302+ : mode .get ();
303+ if (isDefault (sourceMode )) {
304+ return resolveSourceMode (indexMode , sourceMode , enableRecoverySource );
305+
251306 }
252307 if (supportsNonDefaultParameterValues == false ) {
253308 List <String > disallowed = new ArrayList <>();
@@ -271,8 +326,9 @@ public SourceFieldMapper build() {
271326 );
272327 }
273328 }
329+
274330 SourceFieldMapper sourceFieldMapper = new SourceFieldMapper (
275- mode . get () ,
331+ sourceMode ,
276332 enabled .get (),
277333 includes .getValue ().toArray (Strings .EMPTY_ARRAY ),
278334 excludes .getValue ().toArray (Strings .EMPTY_ARRAY ),
@@ -287,21 +343,42 @@ public SourceFieldMapper build() {
287343
288344 }
289345
346+ private static SourceFieldMapper resolveSourceMode (final IndexMode indexMode , final Mode sourceMode , boolean enableRecoverySource ) {
347+ if (indexMode == IndexMode .STANDARD ) {
348+ return enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE ;
349+ }
350+ final SourceFieldMapper syntheticWithoutRecoverySource = indexMode == IndexMode .TIME_SERIES
351+ ? TSDB_DEFAULT_NO_RECOVERY_SOURCE
352+ : LOGSDB_DEFAULT_NO_RECOVERY_SOURCE ;
353+ final SourceFieldMapper syntheticWithRecoverySource = indexMode == IndexMode .TIME_SERIES ? TSDB_DEFAULT : LOGSDB_DEFAULT ;
354+ final SourceFieldMapper storedWithoutRecoverySource = indexMode == IndexMode .TIME_SERIES
355+ ? TSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED
356+ : LOGSDB_DEFAULT_NO_RECOVERY_SOURCE_STORED ;
357+ final SourceFieldMapper storedWithRecoverySource = indexMode == IndexMode .TIME_SERIES ? TSDB_DEFAULT_STORED : LOGSDB_DEFAULT_STORED ;
358+
359+ switch (sourceMode ) {
360+ case SYNTHETIC :
361+ return enableRecoverySource ? syntheticWithRecoverySource : syntheticWithoutRecoverySource ;
362+ case STORED :
363+ return enableRecoverySource ? storedWithRecoverySource : storedWithoutRecoverySource ;
364+ case DISABLED :
365+ throw new IllegalArgumentException ("_source cannot be disabled in index using [" + indexMode + "] index mode" );
366+ default :
367+ throw new IllegalStateException ("Unexpected value: " + sourceMode );
368+ }
369+ }
370+
290371 public static final TypeParser PARSER = new ConfigurableTypeParser (c -> {
291- var indexMode = c .getIndexSettings ().getMode ();
372+ final IndexMode indexMode = c .getIndexSettings ().getMode ();
292373 boolean enableRecoverySource = INDICES_RECOVERY_SOURCE_ENABLED_SETTING .get (c .getSettings ());
374+ final Mode settingSourceMode = INDEX_MAPPER_SOURCE_MODE_SETTING .get (c .getSettings ());
375+
293376 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 ;
377+ if (indexMode == IndexMode .TIME_SERIES && c .getIndexSettings ().getIndexVersionCreated ().before (IndexVersions .V_8_7_0 )) {
378+ return enableRecoverySource ? TSDB_LEGACY_DEFAULT : TSDB_LEGACY_DEFAULT_NO_RECOVERY_SOURCE ;
302379 }
303380 }
304- return enableRecoverySource ? DEFAULT : DEFAULT_NO_RECOVERY_SOURCE ;
381+ return resolveSourceMode ( indexMode , settingSourceMode , enableRecoverySource ) ;
305382 },
306383 c -> new Builder (
307384 c .getIndexSettings ().getMode (),
0 commit comments