14
14
import org .apache .lucene .document .Field ;
15
15
import org .apache .lucene .document .FieldType ;
16
16
import org .apache .lucene .document .StoredField ;
17
+ import org .apache .lucene .index .DocValues ;
17
18
import org .apache .lucene .index .IndexOptions ;
18
19
import org .apache .lucene .index .LeafReaderContext ;
19
20
import org .apache .lucene .index .Term ;
41
42
import org .elasticsearch .index .fielddata .IndexFieldData ;
42
43
import org .elasticsearch .index .fielddata .SourceValueFetcherSortedBinaryIndexFieldData ;
43
44
import org .elasticsearch .index .fielddata .StoredFieldSortedBinaryIndexFieldData ;
44
- import org .elasticsearch .index .fieldvisitor .LeafStoredFieldLoader ;
45
45
import org .elasticsearch .index .fieldvisitor .StoredFieldLoader ;
46
46
import org .elasticsearch .index .mapper .BlockLoader ;
47
47
import org .elasticsearch .index .mapper .BlockSourceReader ;
@@ -132,7 +132,9 @@ private MatchOnlyTextFieldType buildFieldType(MapperBuilderContext context) {
132
132
tsi ,
133
133
indexAnalyzer ,
134
134
context .isSourceSynthetic (),
135
- meta .getValue ()
135
+ meta .getValue (),
136
+ withinMultiField ,
137
+ multiFieldsBuilder .hasSyntheticSourceCompatibleKeywordField ()
136
138
);
137
139
return ft ;
138
140
}
@@ -162,17 +164,24 @@ public static class MatchOnlyTextFieldType extends StringFieldType {
162
164
private final TextFieldType textFieldType ;
163
165
private final String originalName ;
164
166
167
+ private final boolean withinMultiField ;
168
+ private final boolean hasCompatibleMultiFields ;
169
+
165
170
public MatchOnlyTextFieldType (
166
171
String name ,
167
172
TextSearchInfo tsi ,
168
173
Analyzer indexAnalyzer ,
169
174
boolean isSyntheticSource ,
170
- Map <String , String > meta
175
+ Map <String , String > meta ,
176
+ boolean withinMultiField ,
177
+ boolean hasCompatibleMultiFields
171
178
) {
172
179
super (name , true , false , false , tsi , meta );
173
180
this .indexAnalyzer = Objects .requireNonNull (indexAnalyzer );
174
181
this .textFieldType = new TextFieldType (name , isSyntheticSource );
175
182
this .originalName = isSyntheticSource ? name () + "._original" : null ;
183
+ this .withinMultiField = withinMultiField ;
184
+ this .hasCompatibleMultiFields = hasCompatibleMultiFields ;
176
185
}
177
186
178
187
public MatchOnlyTextFieldType (String name ) {
@@ -181,7 +190,9 @@ public MatchOnlyTextFieldType(String name) {
181
190
new TextSearchInfo (Defaults .FIELD_TYPE , null , Lucene .STANDARD_ANALYZER , Lucene .STANDARD_ANALYZER ),
182
191
Lucene .STANDARD_ANALYZER ,
183
192
false ,
184
- Collections .emptyMap ()
193
+ Collections .emptyMap (),
194
+ false ,
195
+ false
185
196
);
186
197
}
187
198
@@ -208,16 +219,34 @@ private IOFunction<LeafReaderContext, CheckedIntFunction<List<Object>, IOExcepti
208
219
"Field [" + name () + "] of type [" + CONTENT_TYPE + "] cannot run positional queries since [_source] is disabled."
209
220
);
210
221
}
211
- if (searchExecutionContext .isSourceSynthetic ()) {
222
+ if (searchExecutionContext .isSourceSynthetic () && withinMultiField ) {
223
+ String parentField = searchExecutionContext .parentPath (name ());
224
+ var parent = searchExecutionContext .lookup ().fieldType (parentField );
225
+ if (parent .isStored ()) {
226
+ return storedFieldFetcher (parentField );
227
+ } else if (parent .hasDocValues ()) {
228
+ return docValuesFieldFetcher (parentField );
229
+ } else {
230
+ assert false : "parent field should either be stored or have doc values" ;
231
+ }
232
+ } else if (searchExecutionContext .isSourceSynthetic () && hasCompatibleMultiFields ) {
233
+ var mapper = (MatchOnlyTextFieldMapper ) searchExecutionContext .getMappingLookup ().getMapper (name ());
234
+ var kwd = TextFieldMapper .SyntheticSourceHelper .getKeywordFieldMapperForSyntheticSource (mapper );
235
+ if (kwd != null ) {
236
+ var fieldType = kwd .fieldType ();
237
+ if (fieldType .isStored ()) {
238
+ return storedFieldFetcher (fieldType .name ());
239
+ } else if (fieldType .hasDocValues ()) {
240
+ return docValuesFieldFetcher (fieldType .name ());
241
+ } else {
242
+ assert false : "multi field should either be stored or have doc values" ;
243
+ }
244
+ } else {
245
+ assert false : "multi field of type keyword should exist" ;
246
+ }
247
+ } else if (searchExecutionContext .isSourceSynthetic ()) {
212
248
String name = storedFieldNameForSyntheticSource ();
213
- StoredFieldLoader loader = StoredFieldLoader .create (false , Set .of (name ));
214
- return context -> {
215
- LeafStoredFieldLoader leafLoader = loader .getLoader (context , null );
216
- return docId -> {
217
- leafLoader .advanceTo (docId );
218
- return leafLoader .storedFields ().get (name );
219
- };
220
- };
249
+ return storedFieldFetcher (name );
221
250
}
222
251
return context -> {
223
252
ValueFetcher valueFetcher = valueFetcher (searchExecutionContext , null );
@@ -233,6 +262,35 @@ private IOFunction<LeafReaderContext, CheckedIntFunction<List<Object>, IOExcepti
233
262
};
234
263
}
235
264
265
+ private static IOFunction <LeafReaderContext , CheckedIntFunction <List <Object >, IOException >> docValuesFieldFetcher (String name ) {
266
+ return context -> {
267
+ var sortedDocValues = DocValues .getSortedSet (context .reader (), name );
268
+ return docId -> {
269
+ if (sortedDocValues .advanceExact (docId )) {
270
+ var values = new ArrayList <>(sortedDocValues .docValueCount ());
271
+ for (int i = 0 ; i < sortedDocValues .docValueCount (); i ++) {
272
+ long ord = sortedDocValues .nextOrd ();
273
+ values .add (sortedDocValues .lookupOrd (ord ).utf8ToString ());
274
+ }
275
+ return values ;
276
+ } else {
277
+ return List .of ();
278
+ }
279
+ };
280
+ };
281
+ }
282
+
283
+ private static IOFunction <LeafReaderContext , CheckedIntFunction <List <Object >, IOException >> storedFieldFetcher (String name ) {
284
+ var loader = StoredFieldLoader .create (false , Set .of (name ));
285
+ return context -> {
286
+ var leafLoader = loader .getLoader (context , null );
287
+ return docId -> {
288
+ leafLoader .advanceTo (docId );
289
+ return leafLoader .storedFields ().get (name );
290
+ };
291
+ };
292
+ }
293
+
236
294
private Query toQuery (Query query , SearchExecutionContext searchExecutionContext ) {
237
295
return new ConstantScoreQuery (
238
296
new SourceConfirmedTextQuery (query , getValueFetcherProvider (searchExecutionContext ), indexAnalyzer )
@@ -505,18 +563,27 @@ public MatchOnlyTextFieldType fieldType() {
505
563
506
564
@ Override
507
565
protected SyntheticSourceSupport syntheticSourceSupport () {
508
- return new SyntheticSourceSupport .Native (
509
- () -> new StringStoredFieldFieldLoader (fieldType ().storedFieldNameForSyntheticSource (), fieldType ().name (), leafName ()) {
510
- @ Override
511
- protected void write (XContentBuilder b , Object value ) throws IOException {
512
- if (value instanceof BytesRef valueBytes ) {
513
- b .value (valueBytes .utf8ToString ());
514
- } else {
515
- assert value instanceof String ;
516
- b .value (value .toString ());
566
+ if (storeSource ) {
567
+ return new SyntheticSourceSupport .Native (
568
+ () -> new StringStoredFieldFieldLoader (fieldType ().storedFieldNameForSyntheticSource (), fieldType ().name (), leafName ()) {
569
+ @ Override
570
+ protected void write (XContentBuilder b , Object value ) throws IOException {
571
+ if (value instanceof BytesRef valueBytes ) {
572
+ b .value (valueBytes .utf8ToString ());
573
+ } else {
574
+ assert value instanceof String ;
575
+ b .value (value .toString ());
576
+ }
517
577
}
518
578
}
579
+ );
580
+ } else {
581
+ var kwd = TextFieldMapper .SyntheticSourceHelper .getKeywordFieldMapperForSyntheticSource (this );
582
+ if (kwd != null ) {
583
+ return new SyntheticSourceSupport .Native (() -> kwd .syntheticFieldLoader (fullPath (), leafName ()));
519
584
}
520
- );
585
+ assert false : "there should be a suite field mapper with native synthetic source support" ;
586
+ return super .syntheticSourceSupport ();
587
+ }
521
588
}
522
589
}
0 commit comments