@@ -92,8 +92,15 @@ public static final class Builder extends FieldMapper.DimensionBuilder {
9292 private final boolean ignoreMalformedByDefault ;
9393 private final IndexVersion indexCreatedVersion ;
9494 private final ScriptCompiler scriptCompiler ;
95+ private final SourceKeepMode indexSourceKeepMode ;
9596
96- public Builder (String name , ScriptCompiler scriptCompiler , boolean ignoreMalformedByDefault , IndexVersion indexCreatedVersion ) {
97+ public Builder (
98+ String name ,
99+ ScriptCompiler scriptCompiler ,
100+ boolean ignoreMalformedByDefault ,
101+ IndexVersion indexCreatedVersion ,
102+ SourceKeepMode indexSourceKeepMode
103+ ) {
97104 super (name );
98105 this .scriptCompiler = Objects .requireNonNull (scriptCompiler );
99106 this .ignoreMalformedByDefault = ignoreMalformedByDefault ;
@@ -114,6 +121,7 @@ public Builder(String name, ScriptCompiler scriptCompiler, boolean ignoreMalform
114121 );
115122 }
116123 });
124+ this .indexSourceKeepMode = indexSourceKeepMode ;
117125 }
118126
119127 Builder nullValue (String nullValue ) {
@@ -184,6 +192,27 @@ public IpFieldMapper build(MapperBuilderContext context) {
184192 }
185193 hasScript = script .get () != null ;
186194 onScriptError = onScriptErrorParam .getValue ();
195+
196+ var sourceKeepMode = this .sourceKeepMode .orElse (indexSourceKeepMode );
197+ String offsetsFieldName ;
198+ if (context .isSourceSynthetic ()
199+ && sourceKeepMode == SourceKeepMode .ARRAYS
200+ && hasDocValues .get ()
201+ && stored .get () == false
202+ && copyTo .copyToFields ().isEmpty ()
203+ && multiFieldsBuilder .hasMultiFields () == false
204+ && indexCreatedVersion .onOrAfter (IndexVersions .SYNTHETIC_SOURCE_STORE_ARRAYS_NATIVELY_KEYWORD )) {
205+ // Skip stored, we will be synthesizing from stored fields, no point to keep track of the offsets
206+ // Skip copy_to and multi fields, supporting that requires more work. However, copy_to usage is rare in metrics and
207+ // logging use cases
208+
209+ // keep track of value offsets so that we can reconstruct arrays from doc values in order as was specified during indexing
210+ // (if field is stored then there is no point of doing this)
211+ offsetsFieldName = context .buildFullName (leafName () + FieldArrayContext .OFFSETS_FIELD_NAME_SUFFIX );
212+ } else {
213+ offsetsFieldName = null ;
214+ }
215+
187216 return new IpFieldMapper (
188217 leafName (),
189218 new IpFieldType (
@@ -198,15 +227,16 @@ public IpFieldMapper build(MapperBuilderContext context) {
198227 ),
199228 builderParams (this , context ),
200229 context .isSourceSynthetic (),
201- this
230+ this ,
231+ offsetsFieldName
202232 );
203233 }
204234
205235 }
206236
207237 public static final TypeParser PARSER = createTypeParserWithLegacySupport ((n , c ) -> {
208238 boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING .get (c .getSettings ());
209- return new Builder (n , c .scriptCompiler (), ignoreMalformedByDefault , c .indexVersionCreated ());
239+ return new Builder (n , c .scriptCompiler (), ignoreMalformedByDefault , c .indexVersionCreated (), c . getIndexSettings (). sourceKeepMode () );
210240 });
211241
212242 public static final class IpFieldType extends SimpleMappedFieldType {
@@ -501,13 +531,16 @@ public TermsEnum getTerms(IndexReader reader, String prefix, boolean caseInsensi
501531 private final Script script ;
502532 private final FieldValues <InetAddress > scriptValues ;
503533 private final ScriptCompiler scriptCompiler ;
534+ private final SourceKeepMode indexSourceKeepMode ;
535+ private final String offsetsFieldName ;
504536
505537 private IpFieldMapper (
506538 String simpleName ,
507539 MappedFieldType mappedFieldType ,
508540 BuilderParams builderParams ,
509541 boolean storeIgnored ,
510- Builder builder
542+ Builder builder ,
543+ String offsetsFieldName
511544 ) {
512545 super (simpleName , mappedFieldType , builderParams );
513546 this .ignoreMalformedByDefault = builder .ignoreMalformedByDefault ;
@@ -523,6 +556,8 @@ private IpFieldMapper(
523556 this .scriptCompiler = builder .scriptCompiler ;
524557 this .dimension = builder .dimension .getValue ();
525558 this .storeIgnored = storeIgnored ;
559+ this .indexSourceKeepMode = builder .indexSourceKeepMode ;
560+ this .offsetsFieldName = offsetsFieldName ;
526561 }
527562
528563 @ Override
@@ -561,6 +596,14 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
561596 if (address != null ) {
562597 indexValue (context , address );
563598 }
599+ if (offsetsFieldName != null && context .isImmediateParentAnArray () && context .getRecordedSource () == false ) {
600+ if (address != null ) {
601+ BytesRef sortableValue = new BytesRef (InetAddressPoint .encode (address ));
602+ context .getOffSetContext ().recordOffset (offsetsFieldName , sortableValue );
603+ } else {
604+ context .getOffSetContext ().recordNull (offsetsFieldName );
605+ }
606+ }
564607 }
565608
566609 private void indexValue (DocumentParserContext context , InetAddress address ) {
@@ -593,7 +636,9 @@ protected void indexScriptValues(
593636
594637 @ Override
595638 public FieldMapper .Builder getMergeBuilder () {
596- return new Builder (leafName (), scriptCompiler , ignoreMalformedByDefault , indexCreatedVersion ).dimension (dimension ).init (this );
639+ return new Builder (leafName (), scriptCompiler , ignoreMalformedByDefault , indexCreatedVersion , indexSourceKeepMode ).dimension (
640+ dimension
641+ ).init (this );
597642 }
598643
599644 @ Override
@@ -610,19 +655,24 @@ protected SyntheticSourceSupport syntheticSourceSupport() {
610655 if (hasDocValues ) {
611656 return new SyntheticSourceSupport .Native (() -> {
612657 var layers = new ArrayList <CompositeSyntheticFieldLoader .Layer >();
613- layers .add (new SortedSetDocValuesSyntheticFieldLoaderLayer (fullPath ()) {
614- @ Override
615- protected BytesRef convert (BytesRef value ) {
616- byte [] bytes = Arrays .copyOfRange (value .bytes , value .offset , value .offset + value .length );
617- return new BytesRef (NetworkAddress .format (InetAddressPoint .decode (bytes )));
618- }
619-
620- @ Override
621- protected BytesRef preserve (BytesRef value ) {
622- // No need to copy because convert has made a deep copy
623- return value ;
624- }
625- });
658+ if (offsetsFieldName != null ) {
659+ layers .add (
660+ new SortedSetWithOffsetsDocValuesSyntheticFieldLoaderLayer (fullPath (), offsetsFieldName , IpFieldMapper ::convert )
661+ );
662+ } else {
663+ layers .add (new SortedSetDocValuesSyntheticFieldLoaderLayer (fullPath ()) {
664+ @ Override
665+ protected BytesRef convert (BytesRef value ) {
666+ return IpFieldMapper .convert (value );
667+ }
668+
669+ @ Override
670+ protected BytesRef preserve (BytesRef value ) {
671+ // No need to copy because convert has made a deep copy
672+ return value ;
673+ }
674+ });
675+ }
626676
627677 if (ignoreMalformed ) {
628678 layers .add (new CompositeSyntheticFieldLoader .MalformedValuesLayer (fullPath ()));
@@ -633,4 +683,14 @@ protected BytesRef preserve(BytesRef value) {
633683
634684 return super .syntheticSourceSupport ();
635685 }
686+
687+ static BytesRef convert (BytesRef value ) {
688+ byte [] bytes = Arrays .copyOfRange (value .bytes , value .offset , value .offset + value .length );
689+ return new BytesRef (NetworkAddress .format (InetAddressPoint .decode (bytes )));
690+ }
691+
692+ @ Override
693+ public String getOffsetFieldName () {
694+ return offsetsFieldName ;
695+ }
636696}
0 commit comments