|
31 | 31 | import org.apache.lucene.util.BytesRef;
|
32 | 32 | import org.elasticsearch.common.bytes.BytesArray;
|
33 | 33 | import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery;
|
| 34 | +import org.elasticsearch.common.settings.Settings; |
| 35 | +import org.elasticsearch.core.Booleans; |
34 | 36 | import org.elasticsearch.index.IndexVersion;
|
35 | 37 | import org.elasticsearch.index.fielddata.BooleanScriptFieldData;
|
36 | 38 | import org.elasticsearch.index.fielddata.ScriptDocValues;
|
|
44 | 46 | import org.elasticsearch.script.ScriptType;
|
45 | 47 | import org.elasticsearch.script.field.BooleanDocValuesField;
|
46 | 48 | import org.elasticsearch.search.MultiValueMode;
|
| 49 | +import org.elasticsearch.search.lookup.SearchLookup; |
47 | 50 | import org.elasticsearch.test.ESTestCase;
|
48 | 51 | import org.elasticsearch.xcontent.XContentParser;
|
49 | 52 | import org.elasticsearch.xcontent.XContentParser.Token;
|
|
52 | 55 |
|
53 | 56 | import java.io.IOException;
|
54 | 57 | import java.util.ArrayList;
|
| 58 | +import java.util.Arrays; |
55 | 59 | import java.util.List;
|
56 | 60 | import java.util.Map;
|
57 | 61 |
|
|
60 | 64 | import static org.hamcrest.Matchers.containsInAnyOrder;
|
61 | 65 | import static org.hamcrest.Matchers.equalTo;
|
62 | 66 | import static org.hamcrest.Matchers.instanceOf;
|
| 67 | +import static org.hamcrest.Matchers.nullValue; |
63 | 68 |
|
64 | 69 | public class BooleanScriptFieldTypeTests extends AbstractNonTextScriptFieldTypeTestCase {
|
65 | 70 |
|
| 71 | + private static final Boolean MALFORMED_BOOLEAN = null; |
| 72 | + private static final Boolean EMPTY_STR_BOOLEAN = false; |
| 73 | + |
66 | 74 | @Override
|
67 | 75 | protected ScriptFactory parseFromSource() {
|
68 | 76 | return BooleanFieldScript.PARSE_FROM_SOURCE;
|
@@ -453,6 +461,137 @@ public void testBlockLoader() throws IOException {
|
453 | 461 | }
|
454 | 462 | }
|
455 | 463 |
|
| 464 | + public void testBlockLoaderSourceOnlyRuntimeField() throws IOException { |
| 465 | + try ( |
| 466 | + Directory directory = newDirectory(); |
| 467 | + RandomIndexWriter iw = new RandomIndexWriter(random(), directory, newIndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE)) |
| 468 | + ) { |
| 469 | + // given |
| 470 | + // try multiple variations of boolean as they're all encoded slightly differently |
| 471 | + iw.addDocuments( |
| 472 | + List.of( |
| 473 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [false]}"))), |
| 474 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [true]}"))), |
| 475 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [\"false\"]}"))), |
| 476 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [\"true\"]}"))), |
| 477 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [\"\"]}"))), |
| 478 | + // ensure a malformed value doesn't crash |
| 479 | + List.of(new StoredField("_source", new BytesRef("{\"test\": [\"potato\"]}"))) |
| 480 | + ) |
| 481 | + ); |
| 482 | + BooleanScriptFieldType fieldType = simpleSourceOnlyMappedFieldType(); |
| 483 | + List<Boolean> expected = Arrays.asList(false, true, false, true, EMPTY_STR_BOOLEAN, MALFORMED_BOOLEAN); |
| 484 | + |
| 485 | + try (DirectoryReader reader = iw.getReader()) { |
| 486 | + // when |
| 487 | + BlockLoader loader = fieldType.blockLoader(blContext(Settings.EMPTY, true)); |
| 488 | + |
| 489 | + // then |
| 490 | + |
| 491 | + // assert loader is of expected instance type |
| 492 | + assertThat(loader, instanceOf(BooleanScriptBlockDocValuesReader.BooleanScriptBlockLoader.class)); |
| 493 | + |
| 494 | + // ignored source doesn't support column at a time loading: |
| 495 | + var columnAtATimeLoader = loader.columnAtATimeReader(reader.leaves().getFirst()); |
| 496 | + assertThat(columnAtATimeLoader, instanceOf(BooleanScriptBlockDocValuesReader.class)); |
| 497 | + |
| 498 | + var rowStrideReader = loader.rowStrideReader(reader.leaves().getFirst()); |
| 499 | + assertThat(rowStrideReader, instanceOf(BooleanScriptBlockDocValuesReader.class)); |
| 500 | + |
| 501 | + // assert values |
| 502 | + assertThat(blockLoaderReadValuesFromColumnAtATimeReader(reader, fieldType, 0), equalTo(expected)); |
| 503 | + assertThat(blockLoaderReadValuesFromRowStrideReader(reader, fieldType), equalTo(expected)); |
| 504 | + } |
| 505 | + } |
| 506 | + } |
| 507 | + |
| 508 | + public void testBlockLoaderSourceOnlyRuntimeFieldWithSyntheticSource() throws IOException { |
| 509 | + try ( |
| 510 | + Directory directory = newDirectory(); |
| 511 | + RandomIndexWriter iw = new RandomIndexWriter(random(), directory, newIndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE)) |
| 512 | + ) { |
| 513 | + // given |
| 514 | + // try multiple variations of boolean as they're all encoded slightly differently |
| 515 | + iw.addDocuments( |
| 516 | + List.of( |
| 517 | + createDocumentWithIgnoredSource("false"), |
| 518 | + createDocumentWithIgnoredSource("true"), |
| 519 | + createDocumentWithIgnoredSource("[false]"), |
| 520 | + createDocumentWithIgnoredSource("[true]"), |
| 521 | + createDocumentWithIgnoredSource("[\"false\"]"), |
| 522 | + createDocumentWithIgnoredSource("[\"true\"]"), |
| 523 | + createDocumentWithIgnoredSource("[\"\"]"), |
| 524 | + // ensure a malformed value doesn't crash |
| 525 | + createDocumentWithIgnoredSource("[\"potato\"]") |
| 526 | + ) |
| 527 | + ); |
| 528 | + |
| 529 | + Settings settings = Settings.builder().put("index.mapping.source.mode", "synthetic").build(); |
| 530 | + BooleanScriptFieldType fieldType = simpleSourceOnlyMappedFieldType(); |
| 531 | + List<Boolean> expected = Arrays.asList(false, true, false, true, false, true, EMPTY_STR_BOOLEAN, MALFORMED_BOOLEAN); |
| 532 | + |
| 533 | + try (DirectoryReader reader = iw.getReader()) { |
| 534 | + // when |
| 535 | + BlockLoader loader = fieldType.blockLoader(blContext(settings, true)); |
| 536 | + |
| 537 | + // then |
| 538 | + |
| 539 | + // assert loader is of expected instance type |
| 540 | + assertThat(loader, instanceOf(FallbackSyntheticSourceBlockLoader.class)); |
| 541 | + |
| 542 | + // ignored source doesn't support column at a time loading: |
| 543 | + var columnAtATimeLoader = loader.columnAtATimeReader(reader.leaves().getFirst()); |
| 544 | + assertThat(columnAtATimeLoader, nullValue()); |
| 545 | + |
| 546 | + var rowStrideReader = loader.rowStrideReader(reader.leaves().getFirst()); |
| 547 | + assertThat( |
| 548 | + rowStrideReader.getClass().getName(), |
| 549 | + equalTo("org.elasticsearch.index.mapper.FallbackSyntheticSourceBlockLoader$IgnoredSourceRowStrideReader") |
| 550 | + ); |
| 551 | + |
| 552 | + // assert values |
| 553 | + assertThat(blockLoaderReadValuesFromRowStrideReader(settings, reader, fieldType, true), equalTo(expected)); |
| 554 | + } |
| 555 | + } |
| 556 | + } |
| 557 | + |
| 558 | + /** |
| 559 | + * Returns a source only mapped field type. This is useful, since the available build() function doesn't override isParsedFromSource() |
| 560 | + */ |
| 561 | + private BooleanScriptFieldType simpleSourceOnlyMappedFieldType() { |
| 562 | + Script script = new Script(ScriptType.INLINE, "test", "", emptyMap()); |
| 563 | + BooleanFieldScript.Factory factory = new BooleanFieldScript.Factory() { |
| 564 | + @Override |
| 565 | + public BooleanFieldScript.LeafFactory newFactory( |
| 566 | + String fieldName, |
| 567 | + Map<String, Object> params, |
| 568 | + SearchLookup searchLookup, |
| 569 | + OnScriptError onScriptError |
| 570 | + ) { |
| 571 | + return ctx -> new BooleanFieldScript(fieldName, params, searchLookup, onScriptError, ctx) { |
| 572 | + @Override |
| 573 | + @SuppressWarnings("unchecked") |
| 574 | + public void execute() { |
| 575 | + Map<String, Object> source = (Map<String, Object>) this.getParams().get("_source"); |
| 576 | + for (Object foo : (List<?>) source.get("test")) { |
| 577 | + try { |
| 578 | + emit(Booleans.parseBoolean(foo.toString(), false)); |
| 579 | + } catch (Exception e) { |
| 580 | + // skip |
| 581 | + } |
| 582 | + } |
| 583 | + } |
| 584 | + }; |
| 585 | + } |
| 586 | + |
| 587 | + @Override |
| 588 | + public boolean isParsedFromSource() { |
| 589 | + return true; |
| 590 | + } |
| 591 | + }; |
| 592 | + return new BooleanScriptFieldType("test", factory, script, emptyMap(), OnScriptError.FAIL); |
| 593 | + } |
| 594 | + |
456 | 595 | private void assertSameCount(IndexSearcher searcher, String source, Object queryDescription, Query scriptedQuery, Query ootbQuery)
|
457 | 596 | throws IOException {
|
458 | 597 | assertThat(
|
|
0 commit comments