From 520387a660308cfff1b2e620afa944278d89e5eb Mon Sep 17 00:00:00 2001 From: panguixin Date: Wed, 8 Jan 2025 20:53:50 +0800 Subject: [PATCH] fix range query Signed-off-by: panguixin --- .../index/mapper/FlatObjectFieldMapper.java | 108 ++++++- .../mapper/FlatObjectFieldTypeTests.java | 306 +++++++++++------- 2 files changed, 275 insertions(+), 139 deletions(-) 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 0998ae53a1137..f4fd2d5ff3514 100644 --- a/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/FlatObjectFieldMapper.java @@ -13,12 +13,19 @@ import org.apache.lucene.document.SortedSetDocValuesField; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.Term; +import org.apache.lucene.search.AutomatonQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.FieldExistsQuery; +import org.apache.lucene.search.IndexOrDocValuesQuery; import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; +import org.apache.lucene.search.TermRangeQuery; import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.automaton.Automaton; +import org.apache.lucene.util.automaton.Operations; +import org.opensearch.OpenSearchException; import org.opensearch.common.Nullable; import org.opensearch.common.lucene.Lucene; import org.opensearch.common.unit.Fuzziness; @@ -51,6 +58,8 @@ import static org.opensearch.index.mapper.FlatObjectFieldMapper.FlatObjectFieldType.getKeywordFieldType; import static org.opensearch.index.mapper.KeywordFieldMapper.normalizeValue; +import static org.opensearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; +import static org.apache.lucene.search.MultiTermQuery.DOC_VALUES_REWRITE; /** * A field mapper for flat_objects. @@ -177,12 +186,12 @@ public FlatObjectFieldType( this.valueAndPathFieldType = valueAndPathFieldType; } - static KeywordFieldType getKeywordFieldType(String fullName, String valueType, boolean isSearchable, boolean hasDocValue) { - return new KeywordFieldType(fullName + valueType, isSearchable, hasDocValue, Collections.emptyMap()) { + static KeywordFieldType getKeywordFieldType(String rootField, String suffix, boolean isSearchable, boolean hasDocValue) { + return new KeywordFieldType(rootField + suffix, isSearchable, hasDocValue, Collections.emptyMap()) { @Override protected String rewriteForDocValue(Object value) { assert value instanceof String; - return fullName + DOT_SYMBOL + value; + return getDVPrefix(rootField) + value; } }; } @@ -257,7 +266,7 @@ public DocValueFormat docValueFormat(@Nullable String format, ZoneId timeZone) { ); } if (rootFieldName != null) { - return new FlatObjectDocValueFormat(rootFieldName + DOT_SYMBOL + name() + EQUAL_SYMBOL); + return new FlatObjectDocValueFormat(getDVPrefix(rootFieldName) + getPathPrefix(name())); } else { throw new IllegalArgumentException( "Field [" + name() + "] of type [" + typeName() + "] does not support doc_value in root field" @@ -320,6 +329,7 @@ public Query termQuery(Object value, @Nullable QueryShardContext context) { @Override public Query termsQuery(List values, QueryShardContext context) { + failIfNotIndexedAndNoDocValues(); List parsedValues = new ArrayList<>(values.size()); for (Object value : values) { parsedValues.add(rewriteSearchValue(value)); @@ -348,7 +358,7 @@ public String rewriteSearchValue(Object value) { if (value instanceof BytesRef) { value = ((BytesRef) value).utf8ToString(); } - return isSubField() ? name() + EQUAL_SYMBOL + value : value.toString(); + return isSubField() ? getPathPrefix(name()) + value : value.toString(); } boolean isSubField() { @@ -357,6 +367,7 @@ boolean isSubField() { @Override public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) { + failIfNotIndexedAndNoDocValues(); return valueFieldType().prefixQuery(rewriteSearchValue(value), method, caseInsensitive, context); } @@ -369,6 +380,7 @@ public Query regexpQuery( @Nullable MultiTermQuery.RewriteMethod method, QueryShardContext context ) { + failIfNotIndexedAndNoDocValues(); return valueFieldType().regexpQuery(rewriteSearchValue(value), syntaxFlags, matchFlags, maxDeterminizedStates, method, context); } @@ -382,6 +394,7 @@ public Query fuzzyQuery( @Nullable MultiTermQuery.RewriteMethod method, QueryShardContext context ) { + failIfNotIndexedAndNoDocValues(); return valueFieldType().fuzzyQuery( rewriteSearchValue(value), fuzziness, @@ -395,13 +408,69 @@ public Query fuzzyQuery( @Override public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, QueryShardContext context) { - return valueFieldType().rangeQuery( - lowerTerm == null ? null : rewriteSearchValue(lowerTerm), - upperTerm == null ? null : rewriteSearchValue(upperTerm), - includeLower, - includeUpper, - context - ); + if (context.allowExpensiveQueries() == false) { + throw new OpenSearchException( + "[range] queries on [text] or [keyword] fields cannot be executed when '" + + ALLOW_EXPENSIVE_QUERIES.getKey() + + "' is set to false." + ); + } + failIfNotIndexedAndNoDocValues(); + + if (lowerTerm != null && upperTerm != null) { + return valueFieldType().rangeQuery( + rewriteSearchValue(lowerTerm), + rewriteSearchValue(upperTerm), + includeLower, + includeUpper, + context + ); + } + + Query indexQuery = null; + Query dvQuery = null; + if (isSearchable()) { + if (isSubField() == false) { + indexQuery = new TermRangeQuery( + getSearchField(), + lowerTerm == null ? null : indexedValueForSearch(lowerTerm), + upperTerm == null ? null : indexedValueForSearch(upperTerm), + includeLower, + includeUpper + ); + } else { + Automaton a1 = PrefixQuery.toAutomaton(indexedValueForSearch(getPathPrefix(name()))); + BytesRef lowerTermBytes = lowerTerm == null ? null : indexedValueForSearch(rewriteSearchValue(lowerTerm)); + BytesRef upperTermBytes = upperTerm == null ? null : indexedValueForSearch(rewriteSearchValue(upperTerm)); + Automaton a2 = TermRangeQuery.toAutomaton(lowerTermBytes, upperTermBytes, includeLower, includeUpper); + Automaton termAutomaton = Operations.intersection(a1, a2); + indexQuery = new AutomatonQuery( + new Term(getSearchField()), + termAutomaton, + Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, + true + ); + } + } + if (hasDocValues()) { + String dvPrefix = isSubField() ? getDVPrefix(rootFieldName) : getDVPrefix(name()); + String prefix = dvPrefix + (isSubField() ? getPathPrefix(name()) : ""); + Automaton a1 = PrefixQuery.toAutomaton(indexedValueForSearch(prefix)); + BytesRef lowerDvBytes = lowerTerm == null ? null : indexedValueForSearch(dvPrefix + rewriteSearchValue(lowerTerm)); + BytesRef upperDvBytes = upperTerm == null ? null : indexedValueForSearch(dvPrefix + rewriteSearchValue(upperTerm)); + Automaton a2 = TermRangeQuery.toAutomaton(lowerDvBytes, upperDvBytes, includeLower, includeUpper); + Automaton dvAutomaton = Operations.intersection(a1, a2); + dvQuery = new AutomatonQuery( + new Term(getSearchField()), + dvAutomaton, + Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, + true, + DOC_VALUES_REWRITE + ); + } + + assert indexQuery != null || dvQuery != null; + return indexQuery == null ? dvQuery : (dvQuery == null ? indexQuery : new IndexOrDocValuesQuery(indexQuery, dvQuery)); } /** @@ -433,6 +502,7 @@ public Query wildcardQuery( boolean caseInsensitve, QueryShardContext context ) { + failIfNotIndexedAndNoDocValues(); return valueFieldType().wildcardQuery(rewriteSearchValue(value), method, caseInsensitve, context); } @@ -543,6 +613,14 @@ private void createPathFields(ParseContext context, HashSet pathParts) { } } + private static String getDVPrefix(String rootFieldName) { + return rootFieldName + DOT_SYMBOL; + } + + private static String getPathPrefix(String path) { + return path + EQUAL_SYMBOL; + } + private void parseToken(XContentParser parser, ParseContext context, Deque path, HashSet pathParts) throws IOException { if (parser.currentToken() == XContentParser.Token.FIELD_NAME) { final String currentFieldName = parser.currentName(); @@ -576,16 +654,16 @@ private void parseToken(XContentParser parser, ParseContext context, Deque ft.termsQuery(Arrays.asList("foo", "bar"), MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -346,7 +348,7 @@ public void testTermsQuery() { () -> ft.termsQuery(Arrays.asList("foo", "bar"), MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -444,7 +446,7 @@ public void testPrefixQuery() { () -> ft.prefixQuery("foo", CONSTANT_SCORE_REWRITE, false, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -462,7 +464,7 @@ public void testPrefixQuery() { () -> ft.prefixQuery("foo", CONSTANT_SCORE_REWRITE, false, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -616,7 +618,7 @@ public void testRegexpQuery() { () -> ft.regexpQuery("foo", 0, 0, 10, null, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -634,7 +636,7 @@ public void testRegexpQuery() { () -> ft.regexpQuery("foo", 0, 0, 10, null, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -753,7 +755,7 @@ public void testFuzzyQuery() { () -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true, null, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -771,132 +773,80 @@ public void testFuzzyQuery() { () -> ft.fuzzyQuery("foo", Fuzziness.fromEdits(2), 1, 50, true, null, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } } public void testRangeQuery() { - // 1.test isSearchable=true, hasDocValues=true, mappedFieldTypeName=null { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field", - null, - true, - true - ); - Query expected = new IndexOrDocValuesQuery( - new TermRangeQuery("field" + VALUE_SUFFIX, new BytesRef("2"), new BytesRef("10"), true, true), - new TermRangeQuery( - "field" + VALUE_SUFFIX, - new BytesRef("field.2"), - new BytesRef("field.10"), - true, - true, - DOC_VALUES_REWRITE - ) - ); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); + for (boolean searchable : new boolean[] { true, false }) { + for (boolean hasDocValue : new boolean[] { true, false }) { + FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( + "field", + null, + searchable, + hasDocValue + ); + + Query indexQuery = new TermRangeQuery("field" + VALUE_SUFFIX, new BytesRef("2"), new BytesRef("10"), true, true); + Query dvQuery = new TermRangeQuery( + "field" + VALUE_SUFFIX, + new BytesRef("field.2"), + new BytesRef("field.10"), + true, + true, + DOC_VALUES_REWRITE + ); + Query expected = searchable == false + ? dvQuery + : (hasDocValue ? new IndexOrDocValuesQuery(indexQuery, dvQuery) : indexQuery); + assertEquals( + expected, + ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) + ); + } + } } // test isSearchable=true, hasDocValues=true, mappedFieldTypeName!=null { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field.field1", - "field", - true, - true - ); - Query expected = new IndexOrDocValuesQuery( - new TermRangeQuery( - "field" + VALUE_AND_PATH_SUFFIX, - new BytesRef("field.field1=2"), - new BytesRef("field.field1=10"), - true, - true - ), - new TermRangeQuery( - "field" + VALUE_AND_PATH_SUFFIX, - new BytesRef("field.field.field1=2"), - new BytesRef("field.field.field1=10"), - true, - true, - DOC_VALUES_REWRITE - ) - ); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); + for (boolean searchable : new boolean[] { true, false }) { + for (boolean hasDocValue : new boolean[] { true, false }) { + FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( + "field.field1", + "field", + searchable, + hasDocValue + ); + Query indexQuery = new TermRangeQuery( + "field" + VALUE_AND_PATH_SUFFIX, + new BytesRef("field.field1=2"), + new BytesRef("field.field1=10"), + true, + true + ); + Query dvQuery = new TermRangeQuery( + "field" + VALUE_AND_PATH_SUFFIX, + new BytesRef("field.field.field1=2"), + new BytesRef("field.field.field1=10"), + true, + true, + DOC_VALUES_REWRITE + ); + Query expected = searchable == false + ? dvQuery + : (hasDocValue ? new IndexOrDocValuesQuery(indexQuery, dvQuery) : indexQuery); + assertEquals( + expected, + ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) + ); + } + } } - // 2.test isSearchable=true, hasDocValues=false, mappedFieldTypeName=null - { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field", - null, - true, - false - ); - Query expected = new TermRangeQuery("field" + VALUE_SUFFIX, new BytesRef("2"), new BytesRef("10"), true, true); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); - } - - // test isSearchable=true, hasDocValues=false, mappedFieldTypeName!=null - { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field.field1", - "field", - true, - false - ); - Query expected = new TermRangeQuery( - "field" + VALUE_AND_PATH_SUFFIX, - new BytesRef("field.field1=2"), - new BytesRef("field.field1=10"), - true, - true - ); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); - } - - // 3.test isSearchable=false, hasDocValues=true, mappedFieldTypeName=null - { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field", - null, - false, - true - ); - Query expected = new TermRangeQuery( - "field" + VALUE_SUFFIX, - new BytesRef("field.2"), - new BytesRef("field.10"), - true, - true, - DOC_VALUES_REWRITE - ); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); - } - - // test isSearchable=false, hasDocValues=true, mappedFieldTypeName!=null - { - FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( - "field.field1", - "field", - false, - true - ); - Query expected = new TermRangeQuery( - "field" + VALUE_AND_PATH_SUFFIX, - new BytesRef("field.field.field1=2"), - new BytesRef("field.field.field1=10"), - true, - true, - DOC_VALUES_REWRITE - ); - assertEquals(expected, ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES)); - } - - // 4.test isSearchable=false, hasDocValues=false, mappedFieldTypeName=null + // test isSearchable=false, hasDocValues=false, mappedFieldTypeName=null { FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( "field", @@ -909,7 +859,7 @@ public void testRangeQuery() { () -> ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -927,10 +877,118 @@ public void testRangeQuery() { () -> ft.rangeQuery(new BytesRef("2"), new BytesRef("10"), true, true, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } + + { + for (boolean searchable : new boolean[] { true, false }) { + for (boolean hasDocValue : new boolean[] { true, false }) { + FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( + "field", + null, + searchable, + hasDocValue + ); + boolean nullLowerTerm = randomBoolean(); + + Automaton a1 = PrefixQuery.toAutomaton(new BytesRef("field.")); + Automaton a2 = TermRangeQuery.toAutomaton( + nullLowerTerm ? null : new BytesRef("field.2"), + nullLowerTerm ? new BytesRef("field.10") : null, + true, + true + ); + Automaton dvAutomaton = Operations.intersection(a1, a2); + Query indexQuery = new TermRangeQuery( + "field" + VALUE_SUFFIX, + nullLowerTerm ? null : new BytesRef("2"), + nullLowerTerm ? new BytesRef("10") : null, + true, + true + ); + Query dvQuery = new AutomatonQuery( + new Term("field" + VALUE_SUFFIX), + dvAutomaton, + Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, + true, + DOC_VALUES_REWRITE + ); + Query expected = searchable == false + ? dvQuery + : (hasDocValue ? new IndexOrDocValuesQuery(indexQuery, dvQuery) : indexQuery); + assertEquals( + expected, + ft.rangeQuery( + nullLowerTerm ? null : new BytesRef("2"), + nullLowerTerm ? new BytesRef("10") : null, + true, + true, + MOCK_QSC_ENABLE_INDEX_DOC_VALUES + ) + ); + } + } + } + + { + for (boolean searchable : new boolean[] { true, false }) { + for (boolean hasDocValue : new boolean[] { true, false }) { + FlatObjectFieldMapper.FlatObjectFieldType ft = (FlatObjectFieldMapper.FlatObjectFieldType) getFlatParentFieldType( + "field.field1", + "field", + searchable, + hasDocValue + ); + boolean nullLowerTerm = randomBoolean(); + + Automaton a1 = PrefixQuery.toAutomaton(new BytesRef("field.field1=")); + Automaton a2 = TermRangeQuery.toAutomaton( + nullLowerTerm ? null : new BytesRef("field.field1=2"), + nullLowerTerm ? new BytesRef("field.field1=10") : null, + true, + true + ); + Automaton termAutomaton = Operations.intersection(a1, a2); + + Automaton dvA1 = PrefixQuery.toAutomaton(new BytesRef("field.field.field1=")); + Automaton dvA2 = TermRangeQuery.toAutomaton( + nullLowerTerm ? null : new BytesRef("field.field.field1=2"), + nullLowerTerm ? new BytesRef("field.field.field1=10") : null, + true, + true + ); + Automaton dvAutomaton = Operations.intersection(dvA1, dvA2); + Query indexQuery = new AutomatonQuery( + new Term("field" + VALUE_AND_PATH_SUFFIX), + termAutomaton, + Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, + true + ); + Query dvQuery = new AutomatonQuery( + new Term("field" + VALUE_AND_PATH_SUFFIX), + dvAutomaton, + Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, + true, + DOC_VALUES_REWRITE + ); + Query expected = searchable == false + ? dvQuery + : (hasDocValue ? new IndexOrDocValuesQuery(indexQuery, dvQuery) : indexQuery); + assertEquals( + expected, + ft.rangeQuery( + nullLowerTerm ? null : new BytesRef("2"), + nullLowerTerm ? new BytesRef("10") : null, + true, + true, + MOCK_QSC_ENABLE_INDEX_DOC_VALUES + ) + ); + } + } + } } public void testWildcardQuery() { @@ -1057,7 +1115,7 @@ public void testWildcardQuery() { () -> ft.wildcardQuery("foo*", null, false, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._value] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); } @@ -1075,7 +1133,7 @@ public void testWildcardQuery() { () -> ft.wildcardQuery("foo*", null, false, MOCK_QSC_ENABLE_INDEX_DOC_VALUES) ); assertEquals( - "Cannot search on field [field._valueAndPath] since it is both not indexed, and does not have doc_values " + "enabled.", + "Cannot search on field [field.field1] since it is both not indexed, and does not have doc_values " + "enabled.", e.getMessage() ); }