1919import org .elasticsearch .common .logging .DeprecationLogger ;
2020import org .elasticsearch .common .time .DateMathParser ;
2121import org .elasticsearch .common .util .BigArrays ;
22- import org .elasticsearch .common .xcontent .XContentHelper ;
2322import org .elasticsearch .index .IndexMode ;
2423import org .elasticsearch .index .IndexVersion ;
2524import org .elasticsearch .index .fielddata .FieldDataContext ;
2827import org .elasticsearch .index .fielddata .ScriptDocValues .DoublesSupplier ;
2928import org .elasticsearch .index .fielddata .SortedBinaryDocValues ;
3029import org .elasticsearch .index .fielddata .SortedNumericDoubleValues ;
30+ import org .elasticsearch .index .mapper .CompositeSyntheticFieldLoader ;
3131import org .elasticsearch .index .mapper .DocumentParserContext ;
3232import org .elasticsearch .index .mapper .FieldMapper ;
33- import org .elasticsearch .index .mapper .IgnoredSourceFieldMapper ;
33+ import org .elasticsearch .index .mapper .IgnoreMalformedStoredValues ;
3434import org .elasticsearch .index .mapper .MappedFieldType ;
3535import org .elasticsearch .index .mapper .Mapper ;
3636import org .elasticsearch .index .mapper .MapperBuilderContext ;
4343import org .elasticsearch .index .mapper .TimeSeriesParams ;
4444import org .elasticsearch .index .mapper .TimeSeriesParams .MetricType ;
4545import org .elasticsearch .index .mapper .ValueFetcher ;
46- import org .elasticsearch .index .mapper .XContentDataHelper ;
4746import org .elasticsearch .index .query .QueryRewriteContext ;
4847import org .elasticsearch .index .query .SearchExecutionContext ;
4948import org .elasticsearch .script .ScriptCompiler ;
5352import org .elasticsearch .search .MultiValueMode ;
5453import org .elasticsearch .search .sort .BucketedSort ;
5554import org .elasticsearch .search .sort .SortOrder ;
55+ import org .elasticsearch .xcontent .CopyingXContentParser ;
5656import org .elasticsearch .xcontent .XContentBuilder ;
5757import org .elasticsearch .xcontent .XContentParser ;
5858import org .elasticsearch .xcontent .XContentSubParser ;
@@ -592,9 +592,7 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
592592 EnumMap <Metric , Number > metricsParsed = new EnumMap <>(Metric .class );
593593 // Preserves the content of the field in order to be able to construct synthetic source
594594 // if field value is malformed.
595- XContentBuilder malformedContentForSyntheticSource = context .mappingLookup ().isSourceSynthetic () && ignoreMalformed
596- ? XContentBuilder .builder (context .parser ().contentType ().xContent ())
597- : null ;
595+ XContentBuilder malformedDataForSyntheticSource = null ;
598596
599597 try {
600598 token = context .parser ().currentToken ();
@@ -603,11 +601,14 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
603601 return ;
604602 }
605603 ensureExpectedToken (XContentParser .Token .START_OBJECT , token , context .parser ());
606- subParser = new XContentSubParser (context .parser ());
607- token = subParser .nextToken ();
608- if (malformedContentForSyntheticSource != null ) {
609- malformedContentForSyntheticSource .startObject ();
604+ if (context .mappingLookup ().isSourceSynthetic () && ignoreMalformed ) {
605+ var copyingParser = new CopyingXContentParser (context .parser ());
606+ malformedDataForSyntheticSource = copyingParser .getBuilder ();
607+ subParser = new XContentSubParser (copyingParser );
608+ } else {
609+ subParser = new XContentSubParser (context .parser ());
610610 }
611+ token = subParser .nextToken ();
611612 while (token != XContentParser .Token .END_OBJECT ) {
612613 // should be an object sub-field with name a metric name
613614 ensureExpectedToken (XContentParser .Token .FIELD_NAME , token , subParser );
@@ -621,9 +622,6 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
621622 }
622623
623624 token = subParser .nextToken ();
624- if (malformedContentForSyntheticSource != null ) {
625- malformedContentForSyntheticSource .field (fieldName );
626- }
627625 // Make sure that the value is a number. Probably this will change when
628626 // new aggregate metric types are added (histogram, cardinality etc)
629627 ensureExpectedToken (XContentParser .Token .VALUE_NUMBER , token , subParser );
@@ -632,9 +630,6 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
632630 try {
633631 Number metricValue = delegateFieldMapper .value (context .parser ());
634632 metricsParsed .put (metric , metricValue );
635- if (malformedContentForSyntheticSource != null ) {
636- malformedContentForSyntheticSource .value (metricValue );
637- }
638633 } catch (IllegalArgumentException e ) {
639634 throw new IllegalArgumentException ("failed to parse [" + metric .name () + "] sub field: " + e .getMessage (), e );
640635 }
@@ -677,24 +672,20 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
677672 }
678673 } catch (Exception e ) {
679674 if (ignoreMalformed ) {
680- if (malformedContentForSyntheticSource != null ) {
681- if (subParser != null ) {
682- // Remaining data in parser needs to be stored as is in order to provide it in synthetic source.
683- XContentHelper .drainAndClose (subParser , malformedContentForSyntheticSource );
684- } else {
685- // We don't use DrainingXContentParser since we don't want to go beyond current field
686- malformedContentForSyntheticSource .copyCurrentStructure (context .parser ());
687- }
688- ;
689- var nameValue = IgnoredSourceFieldMapper .NameValue .fromContext (
690- context ,
691- name (),
692- XContentDataHelper .encodeXContentBuilder (malformedContentForSyntheticSource )
693- );
694- context .addIgnoredField (nameValue );
695- } else if (subParser != null ) {
675+ if (subParser != null ) {
696676 // close the subParser, so we advance to the end of the object
697677 subParser .close ();
678+ } else {
679+ if (context .mappingLookup ().isSourceSynthetic ()) {
680+ // There is a malformed value, but it is not an object (since subParser is null).
681+ // So we just need to copy this single value.
682+ malformedDataForSyntheticSource = XContentBuilder .builder (context .parser ().contentType ().xContent ())
683+ .copyCurrentStructure (context .parser ());
684+ }
685+ }
686+
687+ if (malformedDataForSyntheticSource != null ) {
688+ context .doc ().add (IgnoreMalformedStoredValues .storedField (name (), malformedDataForSyntheticSource ));
698689 }
699690
700691 context .addIgnoredField (name ());
@@ -724,11 +715,15 @@ protected SyntheticSourceMode syntheticSourceMode() {
724715
725716 @ Override
726717 public SourceLoader .SyntheticFieldLoader syntheticFieldLoader () {
727- // Note that malformed values are handled via `IgnoredSourceFieldMapper` infrastructure
728- return new AggregateMetricSyntheticFieldLoader (name (), simpleName (), metrics );
718+ return new CompositeSyntheticFieldLoader (
719+ simpleName (),
720+ name (),
721+ new AggregateMetricSyntheticFieldLoader (name (), simpleName (), metrics ),
722+ new CompositeSyntheticFieldLoader .MalformedValuesLayer (name ())
723+ );
729724 }
730725
731- public static class AggregateMetricSyntheticFieldLoader implements SourceLoader . SyntheticFieldLoader {
726+ public static class AggregateMetricSyntheticFieldLoader implements CompositeSyntheticFieldLoader . SyntheticFieldLoaderLayer {
732727 private final String name ;
733728 private final String simpleName ;
734729 private final EnumSet <Metric > metrics ;
@@ -746,6 +741,11 @@ public String fieldName() {
746741 return name ;
747742 }
748743
744+ @ Override
745+ public long valueCount () {
746+ return hasValue () ? 1 : 0 ;
747+ }
748+
749749 @ Override
750750 public Stream <Map .Entry <String , StoredFieldLoader >> storedFieldLoaders () {
751751 return Stream .of ();
@@ -779,7 +779,7 @@ public void write(XContentBuilder b) throws IOException {
779779 if (metricHasValue .isEmpty ()) {
780780 return ;
781781 }
782- b .startObject (simpleName );
782+ b .startObject ();
783783 for (Map .Entry <Metric , SortedNumericDocValues > entry : metricDocValues .entrySet ()) {
784784 if (metricHasValue .contains (entry .getKey ())) {
785785 String metricName = entry .getKey ().name ();
0 commit comments