20
20
import org .apache .lucene .analysis .tokenattributes .TypeAttribute ;
21
21
import org .apache .lucene .document .Field ;
22
22
import org .apache .lucene .document .FieldType ;
23
+ import org .apache .lucene .document .StoredField ;
23
24
import org .apache .lucene .index .IndexOptions ;
24
25
import org .elasticsearch .ElasticsearchParseException ;
25
26
import org .elasticsearch .index .IndexVersion ;
26
27
import org .elasticsearch .index .analysis .AnalyzerScope ;
27
28
import org .elasticsearch .index .analysis .IndexAnalyzers ;
28
29
import org .elasticsearch .index .analysis .NamedAnalyzer ;
30
+ import org .elasticsearch .index .mapper .CompositeSyntheticFieldLoader ;
29
31
import org .elasticsearch .index .mapper .DocumentParserContext ;
30
32
import org .elasticsearch .index .mapper .FieldMapper ;
31
33
import org .elasticsearch .index .mapper .KeywordFieldMapper ;
32
34
import org .elasticsearch .index .mapper .MapperBuilderContext ;
33
35
import org .elasticsearch .index .mapper .SourceFieldMapper ;
36
+ import org .elasticsearch .index .mapper .SourceLoader ;
34
37
import org .elasticsearch .index .mapper .StringStoredFieldFieldLoader ;
35
38
import org .elasticsearch .index .mapper .TextFieldMapper ;
36
39
import org .elasticsearch .index .mapper .TextParams ;
@@ -78,7 +81,7 @@ private static NamedAnalyzer wrapAnalyzer(NamedAnalyzer in) {
78
81
);
79
82
}
80
83
81
- public static class Builder extends FieldMapper . Builder {
84
+ public static class Builder extends BuilderWithSyntheticSourceContext {
82
85
83
86
final Parameter <SimilarityProvider > similarity = TextParams .similarity (m -> builder (m ).similarity .getValue ());
84
87
final Parameter <String > indexOptions = TextParams .textIndexOptions (m -> builder (m ).indexOptions .getValue ());
@@ -87,25 +90,29 @@ public static class Builder extends FieldMapper.Builder {
87
90
88
91
private final Parameter <Map <String , String >> meta = Parameter .metaParam ();
89
92
90
- private final IndexVersion indexCreatedVersion ;
91
93
private final TextParams .Analyzers analyzers ;
92
- private final boolean isSyntheticSourceEnabled ;
93
94
private final Parameter <Boolean > store ;
94
95
95
- public Builder (String name , IndexVersion indexCreatedVersion , IndexAnalyzers indexAnalyzers , boolean isSyntheticSourceEnabled ) {
96
- super (name );
97
- this .indexCreatedVersion = indexCreatedVersion ;
96
+ public Builder (
97
+ String name ,
98
+ IndexVersion indexCreatedVersion ,
99
+ IndexAnalyzers indexAnalyzers ,
100
+ boolean isSyntheticSourceEnabled ,
101
+ boolean isWithinMultiField
102
+ ) {
103
+ super (name , indexCreatedVersion , isSyntheticSourceEnabled , isWithinMultiField );
98
104
this .analyzers = new TextParams .Analyzers (
99
105
indexAnalyzers ,
100
106
m -> builder (m ).analyzers .getIndexAnalyzer (),
101
107
m -> builder (m ).analyzers .positionIncrementGap .getValue (),
102
108
indexCreatedVersion
103
109
);
104
- this .isSyntheticSourceEnabled = isSyntheticSourceEnabled ;
105
- this .store = Parameter .storeParam (
106
- m -> builder (m ).store .getValue (),
107
- () -> isSyntheticSourceEnabled && multiFieldsBuilder .hasSyntheticSourceCompatibleKeywordField () == false
108
- );
110
+ this .store = Parameter .storeParam (m -> builder (m ).store .getValue (), () -> {
111
+ if (TextFieldMapper .keywordMultiFieldsNotStoredWhenIgnoredIndexVersionCheck (indexCreatedVersion ())) {
112
+ return false ;
113
+ }
114
+ return isSyntheticSourceEnabled () && multiFieldsBuilder .hasSyntheticSourceCompatibleKeywordField () == false ;
115
+ });
109
116
}
110
117
111
118
@ Override
@@ -135,6 +142,7 @@ private AnnotatedTextFieldType buildFieldType(FieldType fieldType, MapperBuilder
135
142
store .getValue (),
136
143
tsi ,
137
144
context .isSourceSynthetic (),
145
+ isWithinMultiField (),
138
146
TextFieldMapper .SyntheticSourceHelper .syntheticSourceDelegate (fieldType .stored (), multiFields ),
139
147
meta .getValue ()
140
148
);
@@ -165,7 +173,13 @@ public AnnotatedTextFieldMapper build(MapperBuilderContext context) {
165
173
}
166
174
167
175
public static final TypeParser PARSER = new TypeParser (
168
- (n , c ) -> new Builder (n , c .indexVersionCreated (), c .getIndexAnalyzers (), SourceFieldMapper .isSynthetic (c .getIndexSettings ()))
176
+ (n , c ) -> new Builder (
177
+ n ,
178
+ c .indexVersionCreated (),
179
+ c .getIndexAnalyzers (),
180
+ SourceFieldMapper .isSynthetic (c .getIndexSettings ()),
181
+ c .isWithinMultiField ()
182
+ )
169
183
);
170
184
171
185
/**
@@ -484,15 +498,17 @@ private void emitAnnotation(int firstSpannedTextPosInc, int annotationPosLen) th
484
498
}
485
499
486
500
public static final class AnnotatedTextFieldType extends TextFieldMapper .TextFieldType {
501
+
487
502
private AnnotatedTextFieldType (
488
503
String name ,
489
504
boolean store ,
490
505
TextSearchInfo tsi ,
491
506
boolean isSyntheticSource ,
507
+ boolean isWithinMultiField ,
492
508
KeywordFieldMapper .KeywordFieldType syntheticSourceDelegate ,
493
509
Map <String , String > meta
494
510
) {
495
- super (name , true , store , tsi , isSyntheticSource , syntheticSourceDelegate , meta , false , false );
511
+ super (name , true , store , tsi , isSyntheticSource , isWithinMultiField , syntheticSourceDelegate , meta , false , false );
496
512
}
497
513
498
514
public AnnotatedTextFieldType (String name , Map <String , String > meta ) {
@@ -505,9 +521,9 @@ public String typeName() {
505
521
}
506
522
}
507
523
524
+ private final IndexVersion indexCreatedVersion ;
508
525
private final FieldType fieldType ;
509
526
private final Builder builder ;
510
-
511
527
private final NamedAnalyzer indexAnalyzer ;
512
528
513
529
protected AnnotatedTextFieldMapper (
@@ -518,10 +534,18 @@ protected AnnotatedTextFieldMapper(
518
534
Builder builder
519
535
) {
520
536
super (simpleName , mappedFieldType , builderParams );
537
+
521
538
assert fieldType .tokenized ();
539
+
522
540
this .fieldType = freezeAndDeduplicateFieldType (fieldType );
523
541
this .builder = builder ;
524
542
this .indexAnalyzer = wrapAnalyzer (builder .analyzers .getIndexAnalyzer ());
543
+ this .indexCreatedVersion = builder .indexCreatedVersion ();
544
+ }
545
+
546
+ @ Override
547
+ public AnnotatedTextFieldType fieldType () {
548
+ return (AnnotatedTextFieldType ) super .fieldType ();
525
549
}
526
550
527
551
@ Override
@@ -544,6 +568,26 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
544
568
context .addToFieldNames (fieldType ().name ());
545
569
}
546
570
}
571
+
572
+ // if synthetic source needs to be supported, yet the field isn't stored, then we need to rely on something else
573
+ if (needsToSupportSyntheticSource () && fieldType .stored () == false ) {
574
+ // if we can rely on the synthetic source delegate for synthetic source, then return
575
+ if (fieldType ().canUseSyntheticSourceDelegateForSyntheticSource (value )) {
576
+ return ;
577
+ }
578
+
579
+ // otherwise, we need to store a copy of this value so that synthetic source can load it
580
+ final String fieldName = fieldType ().syntheticSourceFallbackFieldName ();
581
+ context .doc ().add (new StoredField (fieldName , value ));
582
+ }
583
+ }
584
+
585
+ private boolean needsToSupportSyntheticSource () {
586
+ if (TextFieldMapper .multiFieldsNotStoredByDefaultIndexVersionCheck (indexCreatedVersion )) {
587
+ // if we're within a multi field, then supporting synthetic source isn't necessary as that's the responsibility of the parent
588
+ return fieldType ().isSyntheticSourceEnabled () && fieldType ().isWithinMultiField () == false ;
589
+ }
590
+ return fieldType ().isSyntheticSourceEnabled ();
547
591
}
548
592
549
593
@ Override
@@ -553,8 +597,13 @@ protected String contentType() {
553
597
554
598
@ Override
555
599
public FieldMapper .Builder getMergeBuilder () {
556
- return new Builder (leafName (), builder .indexCreatedVersion , builder .analyzers .indexAnalyzers , builder .isSyntheticSourceEnabled )
557
- .init (this );
600
+ return new Builder (
601
+ leafName (),
602
+ builder .indexCreatedVersion (),
603
+ builder .analyzers .indexAnalyzers ,
604
+ fieldType ().isSyntheticSourceEnabled (),
605
+ fieldType ().isWithinMultiField ()
606
+ ).init (this );
558
607
}
559
608
560
609
@ Override
@@ -568,11 +617,32 @@ protected void write(XContentBuilder b, Object value) throws IOException {
568
617
});
569
618
}
570
619
620
+ return new SyntheticSourceSupport .Native (() -> syntheticFieldLoader (fullPath (), leafName ()));
621
+ }
622
+
623
+ private SourceLoader .SyntheticFieldLoader syntheticFieldLoader (String fullFieldName , String leafFieldName ) {
624
+ // since we don't know whether the delegate field loader can be used for synthetic source until parsing, we need to check both this
625
+ // field and the delegate
626
+
627
+ // first field loader - to check whether the field's value was stored under this match_only_text field
628
+ final String fieldName = fieldType ().syntheticSourceFallbackFieldName ();
629
+ final var thisFieldLayer = new CompositeSyntheticFieldLoader .StoredFieldLayer (fieldName ) {
630
+ @ Override
631
+ protected void writeValue (Object value , XContentBuilder b ) throws IOException {
632
+ b .value (value .toString ());
633
+ }
634
+ };
635
+
636
+ final CompositeSyntheticFieldLoader fieldLoader = new CompositeSyntheticFieldLoader (leafFieldName , fullFieldName , thisFieldLayer );
637
+
638
+ // second loader - to check whether the field's value was stored by a keyword delegate field
571
639
var kwd = TextFieldMapper .SyntheticSourceHelper .getKeywordFieldMapperForSyntheticSource (this );
572
640
if (kwd != null ) {
573
- return new SyntheticSourceSupport .Native (() -> kwd .syntheticFieldLoader (fullPath (), leafName ()));
641
+ // merge the two field loaders into one
642
+ var kwdFieldLoader = kwd .syntheticFieldLoader (fullPath (), leafName ());
643
+ return fieldLoader .mergedWith (kwdFieldLoader );
574
644
}
575
645
576
- return super . syntheticSourceSupport () ;
646
+ return fieldLoader ;
577
647
}
578
648
}
0 commit comments