2020import  org .apache .lucene .analysis .tokenattributes .TypeAttribute ;
2121import  org .apache .lucene .document .Field ;
2222import  org .apache .lucene .document .FieldType ;
23+ import  org .apache .lucene .document .StoredField ;
2324import  org .apache .lucene .index .IndexOptions ;
2425import  org .elasticsearch .ElasticsearchParseException ;
2526import  org .elasticsearch .index .IndexVersion ;
2627import  org .elasticsearch .index .analysis .AnalyzerScope ;
2728import  org .elasticsearch .index .analysis .IndexAnalyzers ;
2829import  org .elasticsearch .index .analysis .NamedAnalyzer ;
30+ import  org .elasticsearch .index .mapper .CompositeSyntheticFieldLoader ;
2931import  org .elasticsearch .index .mapper .DocumentParserContext ;
3032import  org .elasticsearch .index .mapper .FieldMapper ;
3133import  org .elasticsearch .index .mapper .KeywordFieldMapper ;
3234import  org .elasticsearch .index .mapper .MapperBuilderContext ;
33- import  org .elasticsearch .index .mapper .SourceFieldMapper ;
35+ import  org .elasticsearch .index .mapper .SourceLoader ;
3436import  org .elasticsearch .index .mapper .StringStoredFieldFieldLoader ;
37+ import  org .elasticsearch .index .mapper .TextFamilyFieldMapper ;
3538import  org .elasticsearch .index .mapper .TextFieldMapper ;
3639import  org .elasticsearch .index .mapper .TextParams ;
3740import  org .elasticsearch .index .mapper .TextSearchInfo ;
6164 * This code is largely a copy of TextFieldMapper which is less than ideal - 
6265 * my attempts to subclass TextFieldMapper failed but we can revisit this. 
6366 **/ 
64- public  class  AnnotatedTextFieldMapper  extends  FieldMapper  {
67+ public  class  AnnotatedTextFieldMapper  extends  TextFamilyFieldMapper  {
6568
6669    public  static  final  String  CONTENT_TYPE  = "annotated_text" ;
6770
@@ -84,28 +87,26 @@ public static class Builder extends FieldMapper.Builder {
8487        final  Parameter <String > indexOptions  = TextParams .textIndexOptions (m  -> builder (m ).indexOptions .getValue ());
8588        final  Parameter <Boolean > norms  = TextParams .norms (true , m  -> builder (m ).norms .getValue ());
8689        final  Parameter <String > termVectors  = TextParams .termVectors (m  -> builder (m ).termVectors .getValue ());
90+         private  final  Parameter <Boolean > store  = Parameter .storeParam (m  -> builder (m ).store .getValue (), false );
8791
8892        private  final  Parameter <Map <String , String >> meta  = Parameter .metaParam ();
8993
9094        private  final  IndexVersion  indexCreatedVersion ;
9195        private  final  TextParams .Analyzers  analyzers ;
92-         private  final  boolean  isSyntheticSourceEnabled ;
93-         private  final  Parameter <Boolean > store ;
96+         private  final  boolean  isWithinMultiField ;
9497
95-         public  Builder (String  name , IndexVersion  indexCreatedVersion , IndexAnalyzers  indexAnalyzers , boolean  isSyntheticSourceEnabled ) {
98+         private  boolean  isSyntheticSourceEnabled ;
99+ 
100+         public  Builder (String  name , IndexVersion  indexCreatedVersion , IndexAnalyzers  indexAnalyzers , boolean  isWithinMultiField ) {
96101            super (name );
97102            this .indexCreatedVersion  = indexCreatedVersion ;
103+             this .isWithinMultiField  = isWithinMultiField ;
98104            this .analyzers  = new  TextParams .Analyzers (
99105                indexAnalyzers ,
100106                m  -> builder (m ).analyzers .getIndexAnalyzer (),
101107                m  -> builder (m ).analyzers .positionIncrementGap .getValue (),
102108                indexCreatedVersion 
103109            );
104-             this .isSyntheticSourceEnabled  = isSyntheticSourceEnabled ;
105-             this .store  = Parameter .storeParam (
106-                 m  -> builder (m ).store .getValue (),
107-                 () -> isSyntheticSourceEnabled  && multiFieldsBuilder .hasSyntheticSourceCompatibleKeywordField () == false 
108-             );
109110        }
110111
111112        @ Override 
@@ -135,6 +136,7 @@ private AnnotatedTextFieldType buildFieldType(FieldType fieldType, MapperBuilder
135136                store .getValue (),
136137                tsi ,
137138                context .isSourceSynthetic (),
139+                 isWithinMultiField ,
138140                TextFieldMapper .SyntheticSourceHelper .syntheticSourceDelegate (fieldType , multiFields ),
139141                meta .getValue ()
140142            );
@@ -154,6 +156,7 @@ public AnnotatedTextFieldMapper build(MapperBuilderContext context) {
154156                }
155157            }
156158            BuilderParams  builderParams  = builderParams (this , context );
159+             this .isSyntheticSourceEnabled  = context .isSourceSynthetic ();
157160            return  new  AnnotatedTextFieldMapper (
158161                leafName (),
159162                fieldType ,
@@ -165,7 +168,7 @@ public AnnotatedTextFieldMapper build(MapperBuilderContext context) {
165168    }
166169
167170    public  static  final  TypeParser  PARSER  = new  TypeParser (
168-         (n , c ) -> new  Builder (n , c .indexVersionCreated (), c .getIndexAnalyzers (), SourceFieldMapper . isSynthetic ( c . getIndexSettings () ))
171+         (n , c ) -> new  Builder (n , c .indexVersionCreated (), c .getIndexAnalyzers (), c . isWithinMultiField ( ))
169172    );
170173
171174    /** 
@@ -484,15 +487,17 @@ private void emitAnnotation(int firstSpannedTextPosInc, int annotationPosLen) th
484487    }
485488
486489    public  static  final  class  AnnotatedTextFieldType  extends  TextFieldMapper .TextFieldType  {
490+ 
487491        private  AnnotatedTextFieldType (
488492            String  name ,
489493            boolean  store ,
490494            TextSearchInfo  tsi ,
491495            boolean  isSyntheticSource ,
496+             boolean  isWithinMultiField ,
492497            KeywordFieldMapper .KeywordFieldType  syntheticSourceDelegate ,
493498            Map <String , String > meta 
494499        ) {
495-             super (name , true , store , tsi , isSyntheticSource , syntheticSourceDelegate , meta , false , false );
500+             super (name , true , store , tsi , isSyntheticSource , isWithinMultiField ,  syntheticSourceDelegate , meta , false , false );
496501        }
497502
498503        public  AnnotatedTextFieldType (String  name , Map <String , String > meta ) {
@@ -505,9 +510,9 @@ public String typeName() {
505510        }
506511    }
507512
513+     private  final  IndexVersion  indexCreatedVersion ;
508514    private  final  FieldType  fieldType ;
509515    private  final  Builder  builder ;
510- 
511516    private  final  NamedAnalyzer  indexAnalyzer ;
512517
513518    protected  AnnotatedTextFieldMapper (
@@ -517,11 +522,26 @@ protected AnnotatedTextFieldMapper(
517522        BuilderParams  builderParams ,
518523        Builder  builder 
519524    ) {
520-         super (simpleName , mappedFieldType , builderParams );
525+         super (
526+             simpleName ,
527+             builder .indexCreatedVersion ,
528+             builder .isSyntheticSourceEnabled ,
529+             builder .isWithinMultiField ,
530+             mappedFieldType ,
531+             builderParams 
532+         );
533+ 
521534        assert  fieldType .tokenized ();
535+ 
522536        this .fieldType  = freezeAndDeduplicateFieldType (fieldType );
523537        this .builder  = builder ;
524538        this .indexAnalyzer  = wrapAnalyzer (builder .analyzers .getIndexAnalyzer ());
539+         this .indexCreatedVersion  = builder .indexCreatedVersion ;
540+     }
541+ 
542+     @ Override 
543+     public  AnnotatedTextFieldType  fieldType () {
544+         return  (AnnotatedTextFieldType ) super .fieldType ();
525545    }
526546
527547    @ Override 
@@ -543,6 +563,18 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
543563            if  (fieldType .omitNorms ()) {
544564                context .addToFieldNames (fieldType ().name ());
545565            }
566+         } else  if  (needsToSupportSyntheticSource () && fieldType .stored () == false ) {
567+             // if synthetic source needs to be supported, yet the field isn't stored, then we need to rely on something 
568+             // else to support synthetic source 
569+ 
570+             // if we can rely on the synthetic source delegate for synthetic source, then return 
571+             if  (fieldType ().canUseSyntheticSourceDelegateForSyntheticSource (value )) {
572+                 return ;
573+             }
574+ 
575+             // otherwise, we need to store a copy of this value so that synthetic source can load it 
576+             final  String  fieldName  = fieldType ().syntheticSourceFallbackFieldName ();
577+             context .doc ().add (new  StoredField (fieldName , value ));
546578        }
547579    }
548580
@@ -553,8 +585,7 @@ protected String contentType() {
553585
554586    @ Override 
555587    public  FieldMapper .Builder  getMergeBuilder () {
556-         return  new  Builder (leafName (), builder .indexCreatedVersion , builder .analyzers .indexAnalyzers , builder .isSyntheticSourceEnabled )
557-             .init (this );
588+         return  new  Builder (leafName (), builder .indexCreatedVersion , builder .analyzers .indexAnalyzers , isWithinMultiField ).init (this );
558589    }
559590
560591    @ Override 
@@ -568,11 +599,31 @@ protected void write(XContentBuilder b, Object value) throws IOException {
568599            });
569600        }
570601
602+         return  new  SyntheticSourceSupport .Native (() -> syntheticFieldLoader (fullPath (), leafName ()));
603+     }
604+ 
605+     private  SourceLoader .SyntheticFieldLoader  syntheticFieldLoader (String  fullFieldName , String  leafFieldName ) {
606+         // since we don't know whether the delegate field loader can be used for synthetic source until parsing, we 
607+         // need to check both this field and the delegate 
608+ 
609+         // first field loader, representing this field 
610+         final  String  fieldName  = fieldType ().syntheticSourceFallbackFieldName ();
611+         final  var  thisFieldLayer  = new  CompositeSyntheticFieldLoader .StoredFieldLayer (fieldName ) {
612+             @ Override 
613+             protected  void  writeValue (Object  value , XContentBuilder  b ) throws  IOException  {
614+                 b .value (value .toString ());
615+             }
616+         };
617+ 
618+         final  CompositeSyntheticFieldLoader  fieldLoader  = new  CompositeSyntheticFieldLoader (leafFieldName , fullFieldName , thisFieldLayer );
619+ 
620+         // second loader, representing a delegate field, if one exists 
571621        var  kwd  = TextFieldMapper .SyntheticSourceHelper .getKeywordFieldMapperForSyntheticSource (this );
572622        if  (kwd  != null ) {
573-             return  new  SyntheticSourceSupport .Native (() -> kwd .syntheticFieldLoader (fullPath (), leafName ()));
623+             // merge the two field loaders into one 
624+             return  fieldLoader .mergedWith (kwd .syntheticFieldLoader (fullPath (), leafName ()));
574625        }
575626
576-         return  super . syntheticSourceSupport () ;
627+         return  fieldLoader ;
577628    }
578629}
0 commit comments