From 8a187d92db4752e2ad705685d6db1bbe6cb6ff83 Mon Sep 17 00:00:00 2001 From: Ioana Tagirta Date: Thu, 23 Oct 2025 13:31:03 +0200 Subject: [PATCH 1/3] Improve value loading for match_only_text mapping --- .../extras/MatchOnlyTextFieldMapper.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java index 628e84173496f..e37b537483d08 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldMapper.java @@ -604,6 +604,28 @@ protected String delegatingTo() { } } + /* + * TODO: This duplicates code from TextFieldMapper + * If this is a sub-text field try and return the parent's loader. Text + * fields will always be slow to load and if the parent is exact then we + * should use that instead. + */ + String parentField = blContext.parentField(name()); + if (parentField != null) { + MappedFieldType parent = blContext.lookup().fieldType(parentField); + if (parent.typeName().equals(KeywordFieldMapper.CONTENT_TYPE)) { + KeywordFieldMapper.KeywordFieldType kwd = (KeywordFieldMapper.KeywordFieldType) parent; + if (kwd.hasNormalizer() == false && (kwd.hasDocValues() || kwd.isStored())) { + return new BlockLoader.Delegating(kwd.blockLoader(blContext)) { + @Override + protected String delegatingTo() { + return kwd.name(); + } + }; + } + } + } + // fallback to _source (synthetic or not) SourceValueFetcher fetcher = SourceValueFetcher.toString(blContext.sourcePaths(name()), blContext.indexSettings()); // MatchOnlyText never has norms, so we have to use the field names field From 44957f4cb4c2eef7d31a4126a7139be500b6ac68 Mon Sep 17 00:00:00 2001 From: Ioana Tagirta Date: Mon, 27 Oct 2025 16:39:16 +0100 Subject: [PATCH 2/3] Add test --- .../extras/MatchOnlyTextFieldTypeTests.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldTypeTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldTypeTests.java index 88f14aa5ecc90..42ee978239294 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldTypeTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/MatchOnlyTextFieldTypeTests.java @@ -49,6 +49,7 @@ import org.elasticsearch.index.mapper.TextSearchInfo; import org.elasticsearch.index.mapper.extras.MatchOnlyTextFieldMapper.MatchOnlyTextFieldType; import org.elasticsearch.script.ScriptCompiler; +import org.elasticsearch.search.lookup.SearchLookup; import org.hamcrest.Matchers; import java.io.IOException; @@ -372,4 +373,36 @@ public void testBlockLoaderDoesNotUseSyntheticSourceDelegateWhenIgnoreAboveIsSet // verify that we don't delegate anything assertThat(blockLoader, Matchers.not(Matchers.instanceOf(BlockLoader.Delegating.class))); } + + public void testBlockLoaderDelegateToKeywordFieldWhenSyntheticSourceIsDisabled() { + String parentFieldName = "foo"; + String childFieldName = "foo.bar"; + // given + KeywordFieldMapper.KeywordFieldType keywordFieldType = new KeywordFieldMapper.KeywordFieldType( + parentFieldName, + true, + true, + Collections.emptyMap() + ); + + MatchOnlyTextFieldMapper.MatchOnlyTextFieldType ft = new MatchOnlyTextFieldMapper.MatchOnlyTextFieldType( + childFieldName, + new TextSearchInfo(TextFieldMapper.Defaults.FIELD_TYPE, null, Lucene.STANDARD_ANALYZER, Lucene.STANDARD_ANALYZER), + mock(NamedAnalyzer.class), + false, + Collections.emptyMap(), + true, + false, + keywordFieldType + ); + + var mockedSearchLookup = mock(SearchLookup.class); + when(mockedSearchLookup.fieldType(parentFieldName)).thenReturn(keywordFieldType); + + var mockedBlockLoaderContext = mock(MappedFieldType.BlockLoaderContext.class); + when(mockedBlockLoaderContext.parentField(childFieldName)).thenReturn(parentFieldName); + when(mockedBlockLoaderContext.lookup()).thenReturn(mockedSearchLookup); + BlockLoader blockLoader = ft.blockLoader(mockedBlockLoaderContext); + assertThat(blockLoader, Matchers.instanceOf(BlockLoader.Delegating.class)); + } } From 6adf6567bb6651e98d596025559e60bf6b47bf5c Mon Sep 17 00:00:00 2001 From: Ioana Tagirta Date: Mon, 27 Oct 2025 19:43:38 +0100 Subject: [PATCH 3/3] Update docs/changelog/137026.yaml --- docs/changelog/137026.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/changelog/137026.yaml diff --git a/docs/changelog/137026.yaml b/docs/changelog/137026.yaml new file mode 100644 index 0000000000000..b884d863e2498 --- /dev/null +++ b/docs/changelog/137026.yaml @@ -0,0 +1,5 @@ +pr: 137026 +summary: Improve value loading for `match_only_text` mapping +area: ES|QL +type: enhancement +issues: []