|
18 | 18 | import org.apache.lucene.geo.LatLonGeometry; |
19 | 19 | import org.apache.lucene.index.DocValuesType; |
20 | 20 | import org.apache.lucene.index.LeafReaderContext; |
| 21 | +import org.apache.lucene.index.SortedNumericDocValues; |
21 | 22 | import org.apache.lucene.search.IndexOrDocValuesQuery; |
22 | 23 | import org.apache.lucene.search.Query; |
23 | 24 | import org.apache.lucene.util.BytesRef; |
|
34 | 35 | import org.elasticsearch.core.CheckedConsumer; |
35 | 36 | import org.elasticsearch.core.CheckedFunction; |
36 | 37 | import org.elasticsearch.geometry.Point; |
| 38 | +import org.elasticsearch.geometry.utils.WellKnownBinary; |
37 | 39 | import org.elasticsearch.index.IndexMode; |
38 | 40 | import org.elasticsearch.index.IndexVersion; |
39 | 41 | import org.elasticsearch.index.fielddata.FieldDataContext; |
|
62 | 64 |
|
63 | 65 | import java.io.IOException; |
64 | 66 | import java.io.UncheckedIOException; |
| 67 | +import java.nio.ByteOrder; |
65 | 68 | import java.util.Collections; |
66 | 69 | import java.util.List; |
67 | 70 | import java.util.Map; |
|
70 | 73 | import java.util.function.Function; |
71 | 74 |
|
72 | 75 | import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES; |
| 76 | +import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE; |
73 | 77 |
|
74 | 78 | /** |
75 | 79 | * Field Mapper for geo_point types. |
@@ -387,7 +391,7 @@ public static class GeoPointFieldType extends AbstractPointFieldType<GeoPoint> i |
387 | 391 | private final IndexMode indexMode; |
388 | 392 | private final boolean isSyntheticSource; |
389 | 393 |
|
390 | | - private GeoPointFieldType( |
| 394 | + GeoPointFieldType( |
391 | 395 | String name, |
392 | 396 | boolean indexed, |
393 | 397 | boolean stored, |
@@ -545,28 +549,119 @@ public TimeSeriesParams.MetricType getMetricType() { |
545 | 549 |
|
546 | 550 | @Override |
547 | 551 | public BlockLoader blockLoader(BlockLoaderContext blContext) { |
548 | | - if (blContext.fieldExtractPreference() == DOC_VALUES && hasDocValues()) { |
549 | | - return new BlockDocValuesReader.LongsBlockLoader(name()); |
| 552 | + // load from doc values |
| 553 | + if (hasDocValues()) { |
| 554 | + if (blContext.fieldExtractPreference() == DOC_VALUES) { |
| 555 | + return new BlockDocValuesReader.LongsBlockLoader(name()); |
| 556 | + } else if (blContext.fieldExtractPreference() == NONE && isSyntheticSource) { |
| 557 | + // when the preference is not explicitly set to DOC_VALUES, we expect a BytesRef -> see PlannerUtils.toElementType() |
| 558 | + return new BytesRefFromLongsBlockLoader(name()); |
| 559 | + } |
| 560 | + // if we got here, then either synthetic source is not enabled or the preference prohibits us from using doc_values |
550 | 561 | } |
551 | 562 |
|
552 | | - // There are two scenarios possible once we arrive here: |
553 | | - // |
554 | | - // * Stored source - we'll just use blockLoaderFromSource |
555 | | - // * Synthetic source. However, because of the fieldExtractPreference() check above it is still possible that doc_values are |
556 | | - // present here. |
557 | | - // So we have two subcases: |
558 | | - // - doc_values are enabled - _ignored_source field does not exist since we have doc_values. We will use |
559 | | - // blockLoaderFromSource which reads "native" synthetic source. |
560 | | - // - doc_values are disabled - we know that _ignored_source field is present and use a special block loader unless it's a multi |
561 | | - // field. |
| 563 | + // doc_values are disabled, fallback to ignored_source, except for multi fields since then don't have fallback synthetic source |
562 | 564 | if (isSyntheticSource && hasDocValues() == false && blContext.parentField(name()) == null) { |
563 | 565 | return blockLoaderFromFallbackSyntheticSource(blContext); |
564 | 566 | } |
565 | 567 |
|
| 568 | + // otherwise, load from _source (synthetic or otherwise) - very slow |
566 | 569 | return blockLoaderFromSource(blContext); |
567 | 570 | } |
568 | 571 | } |
569 | 572 |
|
| 573 | + /** |
| 574 | + * This is a GeoPoint-specific block loader that helps deal with an edge case where doc_values are available, yet |
| 575 | + * FieldExtractPreference = NONE. When this happens, the BlockLoader sanity checker (see PlannerUtils.toElementType) expects a BytesRef. |
| 576 | + * This implies that we need to load the value from _source. This however is very slow, especially when synthetic source is enabled. |
| 577 | + * We're better off reading from doc_values and converting to BytesRef to satisfy the checker. This is what this block loader is for. |
| 578 | + */ |
| 579 | + static final class BytesRefFromLongsBlockLoader extends BlockDocValuesReader.DocValuesBlockLoader { |
| 580 | + |
| 581 | + private final String fieldName; |
| 582 | + |
| 583 | + BytesRefFromLongsBlockLoader(String fieldName) { |
| 584 | + this.fieldName = fieldName; |
| 585 | + } |
| 586 | + |
| 587 | + @Override |
| 588 | + public Builder builder(BlockFactory factory, int expectedCount) { |
| 589 | + return factory.bytesRefs(expectedCount); |
| 590 | + } |
| 591 | + |
| 592 | + @Override |
| 593 | + public AllReader reader(LeafReaderContext context) throws IOException { |
| 594 | + SortedNumericDocValues docValues = context.reader().getSortedNumericDocValues(fieldName); |
| 595 | + if (docValues != null) { |
| 596 | + return new BytesRefsFromLong(docValues, (geoPointLong) -> { |
| 597 | + GeoPoint gp = new GeoPoint().resetFromEncoded(geoPointLong); |
| 598 | + byte[] wkb = WellKnownBinary.toWKB(new Point(gp.getX(), gp.getY()), ByteOrder.LITTLE_ENDIAN); |
| 599 | + return new BytesRef(wkb); |
| 600 | + }); |
| 601 | + } |
| 602 | + return new ConstantNullsReader(); |
| 603 | + } |
| 604 | + } |
| 605 | + |
| 606 | + private static final class BytesRefsFromLong extends BlockDocValuesReader { |
| 607 | + |
| 608 | + private final SortedNumericDocValues numericDocValues; |
| 609 | + private final Function<Long, BytesRef> longsToBytesRef; |
| 610 | + |
| 611 | + BytesRefsFromLong(SortedNumericDocValues numericDocValues, Function<Long, BytesRef> longsToBytesRef) { |
| 612 | + this.numericDocValues = numericDocValues; |
| 613 | + this.longsToBytesRef = longsToBytesRef; |
| 614 | + } |
| 615 | + |
| 616 | + @Override |
| 617 | + protected int docId() { |
| 618 | + return numericDocValues.docID(); |
| 619 | + } |
| 620 | + |
| 621 | + @Override |
| 622 | + public String toString() { |
| 623 | + return "BlockDocValuesReader.BytesRefsFromLong"; |
| 624 | + } |
| 625 | + |
| 626 | + @Override |
| 627 | + public BlockLoader.Block read(BlockLoader.BlockFactory factory, BlockLoader.Docs docs, int offset, boolean nullsFiltered) |
| 628 | + throws IOException { |
| 629 | + |
| 630 | + try (BlockLoader.BytesRefBuilder builder = factory.bytesRefs(docs.count() - offset)) { |
| 631 | + for (int i = offset; i < docs.count(); i++) { |
| 632 | + int doc = docs.get(i); |
| 633 | + read(doc, builder); |
| 634 | + } |
| 635 | + return builder.build(); |
| 636 | + } |
| 637 | + } |
| 638 | + |
| 639 | + @Override |
| 640 | + public void read(int docId, BlockLoader.StoredFields storedFields, BlockLoader.Builder builder) throws IOException { |
| 641 | + read(docId, (BlockLoader.BytesRefBuilder) builder); |
| 642 | + } |
| 643 | + |
| 644 | + private void read(int doc, BlockLoader.BytesRefBuilder builder) throws IOException { |
| 645 | + // no more values remaining |
| 646 | + if (numericDocValues.advanceExact(doc) == false) { |
| 647 | + builder.appendNull(); |
| 648 | + return; |
| 649 | + } |
| 650 | + int count = numericDocValues.docValueCount(); |
| 651 | + if (count == 1) { |
| 652 | + BytesRef bytesRefValue = longsToBytesRef.apply(numericDocValues.nextValue()); |
| 653 | + builder.appendBytesRef(bytesRefValue); |
| 654 | + return; |
| 655 | + } |
| 656 | + builder.beginPositionEntry(); |
| 657 | + for (int v = 0; v < count; v++) { |
| 658 | + BytesRef bytesRefValue = longsToBytesRef.apply(numericDocValues.nextValue()); |
| 659 | + builder.appendBytesRef(bytesRefValue); |
| 660 | + } |
| 661 | + builder.endPositionEntry(); |
| 662 | + } |
| 663 | + } |
| 664 | + |
570 | 665 | /** GeoPoint parser implementation */ |
571 | 666 | private static class GeoPointParser extends PointParser<GeoPoint> { |
572 | 667 | private final boolean storeMalformedDataForSyntheticSource; |
|
0 commit comments