|
26 | 26 | import org.elasticsearch.common.settings.Settings;
|
27 | 27 | import org.elasticsearch.common.xcontent.XContentHelper;
|
28 | 28 | import org.elasticsearch.core.Tuple;
|
| 29 | +import org.elasticsearch.index.fielddata.FieldDataContext; |
| 30 | +import org.elasticsearch.index.fielddata.IndexFieldDataCache; |
| 31 | +import org.elasticsearch.index.mapper.DocValueFetcher; |
29 | 32 | import org.elasticsearch.index.mapper.DocumentMapper;
|
30 | 33 | import org.elasticsearch.index.mapper.KeywordFieldMapper;
|
31 | 34 | import org.elasticsearch.index.mapper.LuceneDocument;
|
|
34 | 37 | import org.elasticsearch.index.mapper.MapperService;
|
35 | 38 | import org.elasticsearch.index.mapper.MapperTestCase;
|
36 | 39 | import org.elasticsearch.index.mapper.ParsedDocument;
|
| 40 | +import org.elasticsearch.index.mapper.SourceToParse; |
| 41 | +import org.elasticsearch.index.mapper.ValueFetcher; |
37 | 42 | import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
|
38 | 43 | import org.elasticsearch.index.query.SearchExecutionContext;
|
| 44 | +import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; |
39 | 45 | import org.elasticsearch.plugins.Plugin;
|
| 46 | +import org.elasticsearch.search.lookup.Source; |
| 47 | +import org.elasticsearch.search.lookup.SourceProvider; |
40 | 48 | import org.elasticsearch.xcontent.ToXContent;
|
41 | 49 | import org.elasticsearch.xcontent.XContentBuilder;
|
42 | 50 | import org.elasticsearch.xcontent.XContentFactory;
|
|
47 | 55 | import org.junit.Before;
|
48 | 56 |
|
49 | 57 | import java.io.IOException;
|
| 58 | +import java.util.ArrayList; |
50 | 59 | import java.util.Collection;
|
51 | 60 | import java.util.Collections;
|
52 | 61 | import java.util.List;
|
53 | 62 | import java.util.Map;
|
| 63 | +import java.util.Set; |
| 64 | +import java.util.stream.Collectors; |
54 | 65 |
|
| 66 | +import static java.util.stream.Collectors.toList; |
| 67 | +import static org.hamcrest.Matchers.containsInAnyOrder; |
55 | 68 | import static org.hamcrest.Matchers.containsString;
|
56 | 69 | import static org.hamcrest.Matchers.equalTo;
|
57 | 70 | import static org.hamcrest.Matchers.instanceOf;
|
| 71 | +import static org.mockito.Mockito.mock; |
| 72 | +import static org.mockito.Mockito.when; |
58 | 73 |
|
59 | 74 | public class PatternTextFieldMapperTests extends MapperTestCase {
|
60 | 75 |
|
@@ -298,13 +313,69 @@ public void testDisabledSource() throws IOException {
|
298 | 313 |
|
299 | 314 | @Override
|
300 | 315 | protected Object generateRandomInputValue(MappedFieldType ft) {
|
301 |
| - assumeFalse("We don't have a way to assert things here", true); |
302 |
| - return null; |
| 316 | + return PatternTextIntegrationTests.randomMessageMaybeLarge(); |
303 | 317 | }
|
304 | 318 |
|
305 | 319 | @Override
|
306 |
| - protected void randomFetchTestFieldConfig(XContentBuilder b) throws IOException { |
307 |
| - assumeFalse("We don't have a way to assert things here", true); |
| 320 | + protected void assertFetchMany(MapperService mapperService, String field, Object value, String format, int count) throws IOException { |
| 321 | + assumeFalse("pattern_text currently don't support multiple values in the same field", false); |
| 322 | + } |
| 323 | + |
| 324 | + /** |
| 325 | + * pattern_text does not allow sorting or aggregation and thus only allow field data operations |
| 326 | + * of type SCRIPT to access field data. We still want to use `testFetch` to compare value fetchers against doc |
| 327 | + * values. This method copies MapperTestCase.assertFetch, but uses field data operation type SCRIPT. |
| 328 | + */ |
| 329 | + @Override |
| 330 | + protected void assertFetch(MapperService mapperService, String field, Object value, String format) throws IOException { |
| 331 | + MappedFieldType ft = mapperService.fieldType(field); |
| 332 | + SourceToParse source = source(b -> b.field(ft.name(), value)); |
| 333 | + var fielddataContext = new FieldDataContext("", null, () -> null, Set::of, MappedFieldType.FielddataOperation.SCRIPT); |
| 334 | + var fdt = fielddataContext.fielddataOperation(); |
| 335 | + ValueFetcher docValueFetcher = new DocValueFetcher( |
| 336 | + ft.docValueFormat(format, null), |
| 337 | + ft.fielddataBuilder(fielddataContext).build(new IndexFieldDataCache.None(), new NoneCircuitBreakerService()) |
| 338 | + ); |
| 339 | + SearchExecutionContext searchExecutionContext = mock(SearchExecutionContext.class); |
| 340 | + when(searchExecutionContext.isSourceEnabled()).thenReturn(true); |
| 341 | + when(searchExecutionContext.sourcePath(field)).thenReturn(Set.of(field)); |
| 342 | + when(searchExecutionContext.getForField(ft, fdt)).thenAnswer(inv -> fieldDataLookup(mapperService).apply(ft, () -> { |
| 343 | + throw new UnsupportedOperationException(); |
| 344 | + }, fdt)); |
| 345 | + ValueFetcher nativeFetcher = ft.valueFetcher(searchExecutionContext, format); |
| 346 | + ParsedDocument doc = mapperService.documentMapper().parse(source); |
| 347 | + withLuceneIndex(mapperService, iw -> iw.addDocuments(doc.docs()), ir -> { |
| 348 | + Source s = SourceProvider.fromLookup(mapperService.mappingLookup(), null, mapperService.getMapperMetrics().sourceFieldMetrics()) |
| 349 | + .getSource(ir.leaves().get(0), 0); |
| 350 | + docValueFetcher.setNextReader(ir.leaves().get(0)); |
| 351 | + nativeFetcher.setNextReader(ir.leaves().get(0)); |
| 352 | + List<Object> fromDocValues = docValueFetcher.fetchValues(s, 0, new ArrayList<>()); |
| 353 | + List<Object> fromNative = nativeFetcher.fetchValues(s, 0, new ArrayList<>()); |
| 354 | + /* |
| 355 | + * The native fetcher uses byte, short, etc but doc values always |
| 356 | + * uses long or double. This difference is fine because on the outside |
| 357 | + * users can't see it. |
| 358 | + */ |
| 359 | + fromNative = fromNative.stream().map(o -> { |
| 360 | + if (o instanceof Integer || o instanceof Short || o instanceof Byte) { |
| 361 | + return ((Number) o).longValue(); |
| 362 | + } |
| 363 | + if (o instanceof Float) { |
| 364 | + return ((Float) o).doubleValue(); |
| 365 | + } |
| 366 | + return o; |
| 367 | + }).collect(toList()); |
| 368 | + |
| 369 | + if (dedupAfterFetch()) { |
| 370 | + fromNative = fromNative.stream().distinct().collect(Collectors.toList()); |
| 371 | + } |
| 372 | + /* |
| 373 | + * Doc values sort according to something appropriate to the field |
| 374 | + * and the native fetchers usually don't sort. We're ok with this |
| 375 | + * difference. But we have to convince the test we're ok with it. |
| 376 | + */ |
| 377 | + assertThat("fetching " + value, fromNative, containsInAnyOrder(fromDocValues.toArray())); |
| 378 | + }); |
308 | 379 | }
|
309 | 380 |
|
310 | 381 | @Override
|
|
0 commit comments