19
19
import org .elasticsearch .common .logging .DeprecationLogger ;
20
20
import org .elasticsearch .common .time .DateMathParser ;
21
21
import org .elasticsearch .common .util .BigArrays ;
22
- import org .elasticsearch .common .xcontent .XContentHelper ;
23
22
import org .elasticsearch .index .IndexMode ;
24
23
import org .elasticsearch .index .IndexVersion ;
25
24
import org .elasticsearch .index .fielddata .FieldDataContext ;
28
27
import org .elasticsearch .index .fielddata .ScriptDocValues .DoublesSupplier ;
29
28
import org .elasticsearch .index .fielddata .SortedBinaryDocValues ;
30
29
import org .elasticsearch .index .fielddata .SortedNumericDoubleValues ;
30
+ import org .elasticsearch .index .mapper .CompositeSyntheticFieldLoader ;
31
31
import org .elasticsearch .index .mapper .DocumentParserContext ;
32
32
import org .elasticsearch .index .mapper .FieldMapper ;
33
- import org .elasticsearch .index .mapper .IgnoredSourceFieldMapper ;
33
+ import org .elasticsearch .index .mapper .IgnoreMalformedStoredValues ;
34
34
import org .elasticsearch .index .mapper .MappedFieldType ;
35
35
import org .elasticsearch .index .mapper .Mapper ;
36
36
import org .elasticsearch .index .mapper .MapperBuilderContext ;
43
43
import org .elasticsearch .index .mapper .TimeSeriesParams ;
44
44
import org .elasticsearch .index .mapper .TimeSeriesParams .MetricType ;
45
45
import org .elasticsearch .index .mapper .ValueFetcher ;
46
- import org .elasticsearch .index .mapper .XContentDataHelper ;
47
46
import org .elasticsearch .index .query .QueryRewriteContext ;
48
47
import org .elasticsearch .index .query .SearchExecutionContext ;
49
48
import org .elasticsearch .script .ScriptCompiler ;
53
52
import org .elasticsearch .search .MultiValueMode ;
54
53
import org .elasticsearch .search .sort .BucketedSort ;
55
54
import org .elasticsearch .search .sort .SortOrder ;
55
+ import org .elasticsearch .xcontent .CopyingXContentParser ;
56
56
import org .elasticsearch .xcontent .XContentBuilder ;
57
57
import org .elasticsearch .xcontent .XContentParser ;
58
58
import org .elasticsearch .xcontent .XContentSubParser ;
@@ -592,9 +592,7 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
592
592
EnumMap <Metric , Number > metricsParsed = new EnumMap <>(Metric .class );
593
593
// Preserves the content of the field in order to be able to construct synthetic source
594
594
// 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 ;
598
596
599
597
try {
600
598
token = context .parser ().currentToken ();
@@ -603,11 +601,14 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
603
601
return ;
604
602
}
605
603
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 ());
610
610
}
611
+ token = subParser .nextToken ();
611
612
while (token != XContentParser .Token .END_OBJECT ) {
612
613
// should be an object sub-field with name a metric name
613
614
ensureExpectedToken (XContentParser .Token .FIELD_NAME , token , subParser );
@@ -621,9 +622,6 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
621
622
}
622
623
623
624
token = subParser .nextToken ();
624
- if (malformedContentForSyntheticSource != null ) {
625
- malformedContentForSyntheticSource .field (fieldName );
626
- }
627
625
// Make sure that the value is a number. Probably this will change when
628
626
// new aggregate metric types are added (histogram, cardinality etc)
629
627
ensureExpectedToken (XContentParser .Token .VALUE_NUMBER , token , subParser );
@@ -632,9 +630,6 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
632
630
try {
633
631
Number metricValue = delegateFieldMapper .value (context .parser ());
634
632
metricsParsed .put (metric , metricValue );
635
- if (malformedContentForSyntheticSource != null ) {
636
- malformedContentForSyntheticSource .value (metricValue );
637
- }
638
633
} catch (IllegalArgumentException e ) {
639
634
throw new IllegalArgumentException ("failed to parse [" + metric .name () + "] sub field: " + e .getMessage (), e );
640
635
}
@@ -677,24 +672,20 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
677
672
}
678
673
} catch (Exception e ) {
679
674
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 ) {
696
676
// close the subParser, so we advance to the end of the object
697
677
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 ));
698
689
}
699
690
700
691
context .addIgnoredField (name ());
@@ -724,11 +715,15 @@ protected SyntheticSourceMode syntheticSourceMode() {
724
715
725
716
@ Override
726
717
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
+ );
729
724
}
730
725
731
- public static class AggregateMetricSyntheticFieldLoader implements SourceLoader . SyntheticFieldLoader {
726
+ public static class AggregateMetricSyntheticFieldLoader implements CompositeSyntheticFieldLoader . SyntheticFieldLoaderLayer {
732
727
private final String name ;
733
728
private final String simpleName ;
734
729
private final EnumSet <Metric > metrics ;
@@ -746,6 +741,11 @@ public String fieldName() {
746
741
return name ;
747
742
}
748
743
744
+ @ Override
745
+ public long valueCount () {
746
+ return hasValue () ? 1 : 0 ;
747
+ }
748
+
749
749
@ Override
750
750
public Stream <Map .Entry <String , StoredFieldLoader >> storedFieldLoaders () {
751
751
return Stream .of ();
@@ -779,7 +779,7 @@ public void write(XContentBuilder b) throws IOException {
779
779
if (metricHasValue .isEmpty ()) {
780
780
return ;
781
781
}
782
- b .startObject (simpleName );
782
+ b .startObject ();
783
783
for (Map .Entry <Metric , SortedNumericDocValues > entry : metricDocValues .entrySet ()) {
784
784
if (metricHasValue .contains (entry .getKey ())) {
785
785
String metricName = entry .getKey ().name ();
0 commit comments