From 6338b59b5fa043dc0359aecaaefffd1f99b715e8 Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 3 Jul 2025 11:47:05 +0300 Subject: [PATCH 1/3] adding check for isIndexed in text fields when generating field exists queries to avoid ISE when field is stored but not indexed or with doc_values --- .../test/search/160_exists_query.yml | 29 +++++++++++++++++++ .../index/mapper/MappedFieldType.java | 2 +- .../action/search/SearchCapabilities.java | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml index 40ea75b81d59e..a795f945a92ad 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml @@ -45,6 +45,10 @@ setup: type: keyword text: type: text + text_stored_not_indexed: + type: text + store: true + index: false - do: headers: @@ -70,6 +74,7 @@ setup: inner1: "foo" inner2: "bar" text: "foo bar" + text_stored_not_indexed: "foo bar" - do: headers: @@ -94,6 +99,7 @@ setup: object: inner1: "foo" text: "foo bar" + text_stored_not_indexed: "foo bar" - do: headers: @@ -119,6 +125,7 @@ setup: object: inner2: "bar" text: "foo bar" + text_stored_not_indexed: "foo bar" - do: index: @@ -1268,3 +1275,25 @@ setup: field: text - match: {hits.total: 1} + +--- +"Test exists query on text field with no dv, that is stored but not indexed": + - requires: + capabilities: + - method: POST + path: /_search + capabilities: [ field_exists_query_for_text_fields_no_index_or_dv ] + test_runner_features: capabilities + reason: "Before the fix, this query would throw an ISE because the field is not indexed and has no doc values." + + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + exists: + field: text_stored_not_indexed + + # this should not throw, but rather return 0 hits, as the field is not indexed nor it has doc values + - match: {hits.total: 0} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index a9e67be4085dd..04e4975370e35 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -371,7 +371,7 @@ public Query regexpQuery( } public Query existsQuery(SearchExecutionContext context) { - if (hasDocValues() || getTextSearchInfo().hasNorms()) { + if (hasDocValues() || (isIndexed() && getTextSearchInfo().hasNorms())) { return new FieldExistsQuery(name()); } else { return new TermQuery(new Term(FieldNamesFieldMapper.NAME, name())); diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java b/server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java index ecbd092b455c7..f1495e06ef51d 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/SearchCapabilities.java @@ -52,6 +52,7 @@ private SearchCapabilities() {} private static final String SIGNIFICANT_TERMS_ON_NESTED_FIELDS = "significant_terms_on_nested_fields"; private static final String EXCLUDE_VECTORS_PARAM = "exclude_vectors_param"; private static final String DENSE_VECTOR_UPDATABLE_BBQ = "dense_vector_updatable_bbq"; + private static final String FIELD_EXISTS_QUERY_FOR_TEXT_FIELDS_NO_INDEX_OR_DV = "field_exists_query_for_text_fields_no_index_or_dv"; public static final Set CAPABILITIES; static { @@ -75,6 +76,7 @@ private SearchCapabilities() {} capabilities.add(SIGNIFICANT_TERMS_ON_NESTED_FIELDS); capabilities.add(EXCLUDE_VECTORS_PARAM); capabilities.add(DENSE_VECTOR_UPDATABLE_BBQ); + capabilities.add(FIELD_EXISTS_QUERY_FOR_TEXT_FIELDS_NO_INDEX_OR_DV); CAPABILITIES = Set.copyOf(capabilities); } } From 93a97bb5cc4ff369ec102b46cd08b2467bf41fd8 Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 3 Jul 2025 11:49:28 +0300 Subject: [PATCH 2/3] Update docs/changelog/130531.yaml --- docs/changelog/130531.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/changelog/130531.yaml diff --git a/docs/changelog/130531.yaml b/docs/changelog/130531.yaml new file mode 100644 index 0000000000000..42a4de6dc7231 --- /dev/null +++ b/docs/changelog/130531.yaml @@ -0,0 +1,6 @@ +pr: 130531 +summary: Adding check for `isIndexed` in text fields when generating field exists + queries to avoid ISE when field is stored but not indexed or with `doc_values` +area: Analysis +type: bug +issues: [] From 7d05df9df3bdc8e6192f979b0f394c8a6314c697 Mon Sep 17 00:00:00 2001 From: Panagiotis Bailis Date: Thu, 3 Jul 2025 15:23:30 +0300 Subject: [PATCH 3/3] updating test --- .../test/search/160_exists_query.yml | 32 +++++++++++++++++++ .../index/mapper/KeywordFieldTypeTests.java | 4 ++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml index a795f945a92ad..1b46b5c64885f 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search/160_exists_query.yml @@ -191,6 +191,12 @@ setup: doc_values: false text: type: text + keyword_stored_norms_not_indexed: + type: keyword + doc_values: false + index: false + store: true + norms: true - do: headers: @@ -216,6 +222,7 @@ setup: inner1: "foo" inner2: "bar" text: "foo bar" + keyword_stored_norms_not_indexed: "foo bar" - do: headers: @@ -240,6 +247,7 @@ setup: object: inner1: "foo" text: "foo bar" + keyword_stored_norms_not_indexed: "foo bar" - do: headers: @@ -265,6 +273,7 @@ setup: object: inner2: "bar" text: "foo bar" + keyword_stored_norms_not_indexed: "foo bar" - do: index: @@ -1297,3 +1306,26 @@ setup: # this should not throw, but rather return 0 hits, as the field is not indexed nor it has doc values - match: {hits.total: 0} + + +--- +"Test exists query on keyword field with no dv, that is stored, with norms, but not indexed": + - requires: + capabilities: + - method: POST + path: /_search + capabilities: [ field_exists_query_for_text_fields_no_index_or_dv ] + test_runner_features: capabilities + reason: "Before the fix, this query would throw an ISE because the field is not indexed and has no doc values." + + - do: + search: + rest_total_hits_as_int: true + index: test-no-dv + body: + query: + exists: + field: keyword_stored_norms_not_indexed + + # this should not throw, but rather return 0 hits, as the field is not indexed nor it has doc values + - match: {hits.total: 0} diff --git a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java index 092d9a1210815..05a6c24a2b743 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/KeywordFieldTypeTests.java @@ -137,7 +137,9 @@ public void testExistsQuery() { FieldType fieldType = new FieldType(); fieldType.setOmitNorms(false); KeywordFieldType ft = new KeywordFieldType("field", fieldType); - assertEquals(new FieldExistsQuery("field"), ft.existsQuery(MOCK_CONTEXT)); + // updated in #130531 so that a field that is neither indexed nor has doc values will generate a TermQuery + // to avoid ISE from FieldExistsQuery + assertEquals(new TermQuery(new Term(FieldNamesFieldMapper.NAME, "field")), ft.existsQuery(MOCK_CONTEXT)); } { KeywordFieldType ft = new KeywordFieldType("field", true, false, Collections.emptyMap());