From c6dfc65ea0bccc9cfe66bc4248d09b42d7430d0e Mon Sep 17 00:00:00 2001 From: kkewwei Date: Fri, 24 Jan 2025 02:35:02 +0800 Subject: [PATCH] Fix exists queries on nested flat_object fields throw exception (#16803) Signed-off-by: kkewwei Signed-off-by: kkewwei --- CHANGELOG.md | 1 + .../test/index/90_flat_object.yml | 48 +++++++++++++++++-- .../index/mapper/FlatObjectFieldMapper.java | 9 +++- .../mapper/FlatObjectFieldMapperTests.java | 12 +++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 499405ac508e4..5e022a3909731 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Fix multi-value sort for unsigned long ([#16732](https://github.com/opensearch-project/OpenSearch/pull/16732)) - The `phone-search` analyzer no longer emits the tel/sip prefix, international calling code, extension numbers and unformatted input as a token ([#16993](https://github.com/opensearch-project/OpenSearch/pull/16993)) - Fix GRPC AUX_TRANSPORT_PORT and SETTING_GRPC_PORT settings and remove lingering HTTP terminology ([#17037](https://github.com/opensearch-project/OpenSearch/pull/17037)) +- Fix exists queries on nested flat_object fields throws exception ([#16803](https://github.com/opensearch-project/OpenSearch/pull/16803)) ### Security diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml index 2a469aa5ff04d..3966b59c4a045 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/index/90_flat_object.yml @@ -16,7 +16,10 @@ setup: type : "flat_object" required_matches: type : "long" - + infos: + properties: + info: + type: "flat_object" - do: index: index: test @@ -60,7 +63,12 @@ setup: "review": [["bad",30.41],["ok",80.0]], "publishDate": "2016-01-01" }, - "required_matches": 1 + "required_matches": 1, + "infos": { + "info":{ + "name": "name1" + } + } } # Do index refresh - do: @@ -73,6 +81,40 @@ teardown: - do: indices.delete: index: test + +--- +"Exist query in root field": + - skip: + version: "- 2.99.99" + reason: "the query would throw exception prior to 2.99.99" + + - do: + search: + body: { + _source: true, + size: 10, + query: { + exists: { + field: "catalog" + } + } + } + - length: { hits.hits: 2 } + + - do: + search: + body: { + _source: true, + size: 10, + query: { + exists: { + field: "infos" + } + } + } + - length: { hits.hits: 1 } + - match: { hits.hits.0._source.infos.info.name: "name1" } + --- "Invalid docs": - skip: @@ -135,7 +177,7 @@ teardown: - match: { test.mappings.properties.catalog.type: flat_object } - match: { test.mappings.properties.required_matches.type: long } # https://github.com/opensearch-project/OpenSearch/tree/main/rest-api-spec/src/main/resources/rest-api-spec/test#length - - length: { test.mappings.properties: 3 } + - length: { test.mappings.properties: 4 } - length: { test.mappings.properties.catalog: 1 } --- diff --git a/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java b/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java index b921afb3157a8..4425e4e5b0b39 100644 --- a/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java @@ -93,7 +93,12 @@ public static class Defaults { @Override public MappedFieldType keyedFieldType(String key) { - return new FlatObjectFieldType(this.name() + DOT_SYMBOL + key, this.name(), valueFieldType, valueAndPathFieldType); + return new FlatObjectFieldType( + Strings.isNullOrEmpty(key) ? this.name() : (this.name() + DOT_SYMBOL + key), + this.name(), + valueFieldType, + valueAndPathFieldType + ); } /** @@ -177,7 +182,7 @@ public FlatObjectFieldType( new TextSearchInfo(Defaults.FIELD_TYPE, null, Lucene.KEYWORD_ANALYZER, Lucene.KEYWORD_ANALYZER), Collections.emptyMap() ); - assert rootFieldName == null || (name.length() > rootFieldName.length() && name.startsWith(rootFieldName)); + assert rootFieldName == null || (name.length() >= rootFieldName.length() && name.startsWith(rootFieldName)); this.ignoreAbove = Integer.MAX_VALUE; this.nullValue = null; this.rootFieldName = rootFieldName; diff --git a/server/src/test/java/org/opensearch/index/mapper/FlatObjectFieldMapperTests.java b/server/src/test/java/org/opensearch/index/mapper/FlatObjectFieldMapperTests.java index 118f58cf5e855..adb2c8b1ffe2a 100644 --- a/server/src/test/java/org/opensearch/index/mapper/FlatObjectFieldMapperTests.java +++ b/server/src/test/java/org/opensearch/index/mapper/FlatObjectFieldMapperTests.java @@ -17,6 +17,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.opensearch.common.TriFunction; +import org.opensearch.common.util.set.Sets; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.core.xcontent.ToXContent; @@ -27,6 +28,7 @@ import java.io.IOException; import java.util.List; +import java.util.Set; import static org.opensearch.index.mapper.FlatObjectFieldMapper.CONTENT_TYPE; import static org.opensearch.index.mapper.FlatObjectFieldMapper.VALUE_AND_PATH_SUFFIX; @@ -397,7 +399,17 @@ public void testFetchDocValues() throws IOException { Throwable throwable = assertThrows(IllegalArgumentException.class, () -> ft.docValueFormat(null, null)); assertEquals("Field [field] of type [flat_object] does not support doc_value in root field", throwable.getMessage()); } + } + + public void testPatternMatch() throws IOException { + MapperService mapperService = createMapperService( + fieldMapping(b -> b.startObject("properties").startObject("foo").field("type", "flat_object").endObject().endObject()) + ); + QueryShardContext queryShardContext = createQueryShardContext(mapperService); + Set fields = queryShardContext.simpleMatchToIndexNames("field.*"); + assertEquals(1, fields.size()); + assertEquals(Sets.newHashSet("field.foo"), fields); } @Override