@@ -87,6 +87,7 @@ public final class KeywordFieldMapper extends FieldMapper {
87
87
private static final Logger logger = LogManager .getLogger (KeywordFieldMapper .class );
88
88
89
89
public static final String CONTENT_TYPE = "keyword" ;
90
+ public static final String OFFSETS_FIELD_NAME_SUFFIX = ".offsets" ;
90
91
91
92
public static class Defaults {
92
93
public static final FieldType FIELD_TYPE ;
@@ -182,14 +183,16 @@ public static final class Builder extends FieldMapper.DimensionBuilder {
182
183
private final IndexAnalyzers indexAnalyzers ;
183
184
private final ScriptCompiler scriptCompiler ;
184
185
private final IndexVersion indexCreatedVersion ;
186
+ private final SourceKeepMode indexSourceKeepMode ;
185
187
186
188
public Builder (final String name , final MappingParserContext mappingParserContext ) {
187
189
this (
188
190
name ,
189
191
mappingParserContext .getIndexAnalyzers (),
190
192
mappingParserContext .scriptCompiler (),
191
193
IGNORE_ABOVE_SETTING .get (mappingParserContext .getSettings ()),
192
- mappingParserContext .getIndexSettings ().getIndexVersionCreated ()
194
+ mappingParserContext .getIndexSettings ().getIndexVersionCreated (),
195
+ mappingParserContext .getIndexSettings ().sourceKeepMode ()
193
196
);
194
197
}
195
198
@@ -198,7 +201,8 @@ public Builder(final String name, final MappingParserContext mappingParserContex
198
201
IndexAnalyzers indexAnalyzers ,
199
202
ScriptCompiler scriptCompiler ,
200
203
int ignoreAboveDefault ,
201
- IndexVersion indexCreatedVersion
204
+ IndexVersion indexCreatedVersion ,
205
+ SourceKeepMode indexSourceKeepMode
202
206
) {
203
207
super (name );
204
208
this .indexAnalyzers = indexAnalyzers ;
@@ -233,10 +237,11 @@ public Builder(final String name, final MappingParserContext mappingParserContex
233
237
throw new IllegalArgumentException ("[ignore_above] must be positive, got [" + v + "]" );
234
238
}
235
239
});
240
+ this .indexSourceKeepMode = indexSourceKeepMode ;
236
241
}
237
242
238
243
public Builder (String name , IndexVersion indexCreatedVersion ) {
239
- this (name , null , ScriptCompiler .NONE , Integer .MAX_VALUE , indexCreatedVersion );
244
+ this (name , null , ScriptCompiler .NONE , Integer .MAX_VALUE , indexCreatedVersion , SourceKeepMode . NONE );
240
245
}
241
246
242
247
public Builder ignoreAbove (int ignoreAbove ) {
@@ -370,13 +375,36 @@ public KeywordFieldMapper build(MapperBuilderContext context) {
370
375
}
371
376
super .hasScript = script .get () != null ;
372
377
super .onScriptError = onScriptError .getValue ();
378
+
379
+ var sourceKeepMode = this .sourceKeepMode .orElse (indexSourceKeepMode );
380
+ BinaryFieldMapper offsetsFieldMapper ;
381
+ if (context .isSourceSynthetic ()
382
+ && sourceKeepMode == SourceKeepMode .ARRAYS
383
+ && fieldtype .stored () == false
384
+ && copyTo .copyToFields ().isEmpty ()
385
+ && multiFieldsBuilder .hasMultiFields () == false ) {
386
+ // Skip stored, we will be synthesizing from stored fields, no point to keep track of the offsets
387
+ // Skip copy_to, supporting that requires more work. However, copy_to usage is rare in metrics and logging use cases
388
+
389
+ // keep track of value offsets so that we can reconstruct arrays from doc values in order as was specified during indexing
390
+ // (if field is stored then there is no point of doing this)
391
+ offsetsFieldMapper = new BinaryFieldMapper .Builder (
392
+ context .buildFullName (leafName () + OFFSETS_FIELD_NAME_SUFFIX ),
393
+ context .isSourceSynthetic ()
394
+ ).docValues (true ).build (context );
395
+ } else {
396
+ offsetsFieldMapper = null ;
397
+ }
398
+
373
399
return new KeywordFieldMapper (
374
400
leafName (),
375
401
fieldtype ,
376
402
buildFieldType (context , fieldtype ),
377
403
builderParams (this , context ),
378
404
context .isSourceSynthetic (),
379
- this
405
+ this ,
406
+ offsetsFieldMapper ,
407
+ indexSourceKeepMode
380
408
);
381
409
}
382
410
}
@@ -867,14 +895,18 @@ public boolean hasNormalizer() {
867
895
private final IndexAnalyzers indexAnalyzers ;
868
896
private final int ignoreAboveDefault ;
869
897
private final int ignoreAbove ;
898
+ private final BinaryFieldMapper offsetsFieldMapper ;
899
+ private final SourceKeepMode indexSourceKeepMode ;
870
900
871
901
private KeywordFieldMapper (
872
902
String simpleName ,
873
903
FieldType fieldType ,
874
904
KeywordFieldType mappedFieldType ,
875
905
BuilderParams builderParams ,
876
906
boolean isSyntheticSource ,
877
- Builder builder
907
+ Builder builder ,
908
+ BinaryFieldMapper offsetsFieldMapper ,
909
+ SourceKeepMode indexSourceKeepMode
878
910
) {
879
911
super (simpleName , mappedFieldType , builderParams );
880
912
assert fieldType .indexOptions ().compareTo (IndexOptions .DOCS_AND_FREQS ) <= 0 ;
@@ -891,6 +923,8 @@ private KeywordFieldMapper(
891
923
this .isSyntheticSource = isSyntheticSource ;
892
924
this .ignoreAboveDefault = builder .ignoreAboveDefault ;
893
925
this .ignoreAbove = builder .ignoreAbove .getValue ();
926
+ this .offsetsFieldMapper = offsetsFieldMapper ;
927
+ this .indexSourceKeepMode = indexSourceKeepMode ;
894
928
}
895
929
896
930
@ Override
@@ -967,6 +1001,9 @@ private void indexValue(DocumentParserContext context, String value) {
967
1001
if (fieldType ().hasDocValues () == false && fieldType .omitNorms ()) {
968
1002
context .addToFieldNames (fieldType ().name ());
969
1003
}
1004
+ if (offsetsFieldMapper != null ) {
1005
+ context .recordOffset (offsetsFieldMapper .fullPath (), value );
1006
+ }
970
1007
}
971
1008
972
1009
private static String normalizeValue (NamedAnalyzer normalizer , String field , String value ) {
@@ -1008,9 +1045,9 @@ public Map<String, NamedAnalyzer> indexAnalyzers() {
1008
1045
1009
1046
@ Override
1010
1047
public FieldMapper .Builder getMergeBuilder () {
1011
- return new Builder (leafName (), indexAnalyzers , scriptCompiler , ignoreAboveDefault , indexCreatedVersion ). dimension (
1012
- fieldType ().isDimension ()
1013
- ) .init (this );
1048
+ return new Builder (leafName (), indexAnalyzers , scriptCompiler , ignoreAboveDefault , indexCreatedVersion , indexSourceKeepMode )
1049
+ . dimension ( fieldType ().isDimension () )
1050
+ .init (this );
1014
1051
}
1015
1052
1016
1053
@ Override
@@ -1063,7 +1100,8 @@ protected void writeValue(Object value, XContentBuilder b) throws IOException {
1063
1100
}
1064
1101
});
1065
1102
} else if (hasDocValues ) {
1066
- layers .add (new SortedSetDocValuesSyntheticFieldLoaderLayer (fullPath ()) {
1103
+ String offsetsFullPath = offsetsFieldMapper != null ? offsetsFieldMapper .fullPath () : null ;
1104
+ layers .add (new SortedSetDocValuesSyntheticFieldLoaderLayer (fullPath (), offsetsFullPath ) {
1067
1105
1068
1106
@ Override
1069
1107
protected BytesRef convert (BytesRef value ) {
@@ -1090,4 +1128,9 @@ protected void writeValue(Object value, XContentBuilder b) throws IOException {
1090
1128
1091
1129
return new CompositeSyntheticFieldLoader (leafFieldName , fullFieldName , layers );
1092
1130
}
1131
+
1132
+ @ Override
1133
+ public boolean supportsStoringArraysNatively () {
1134
+ return offsetsFieldMapper != null ;
1135
+ }
1093
1136
}
0 commit comments