Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.7] Fix NPE thrown by prefix and regex query in strange scenarios (#94369) #94399

Merged
merged 1 commit into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/94369.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 94369
summary: Fix NPE thrown by prefix query in strange scenarios
area: Search
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,13 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
MultiTermQuery.RewriteMethod rewriteMethod
) {
// Disable scoring
return new ConstantScoreQuery(super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context));
return new ConstantScoreQuery(
super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context, rewriteMethod)
);
}

@Override
Expand All @@ -276,8 +279,14 @@ public IntervalsSource fuzzyIntervals(
boolean transpositions,
SearchExecutionContext context
) {
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term(name(), term), maxDistance, prefixLength, 128, transpositions);
fuzzyQuery.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_REWRITE);
FuzzyQuery fuzzyQuery = new FuzzyQuery(
new Term(name(), term),
maxDistance,
prefixLength,
128,
transpositions,
MultiTermQuery.CONSTANT_SCORE_REWRITE
);
IntervalsSource fuzzyIntervals = Intervals.multiterm(fuzzyQuery.getAutomata(), term);
return toIntervalsSource(fuzzyIntervals, fuzzyQuery, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRew
SpanMultiTermQueryWrapper<?> spanMulti = new SpanMultiTermQueryWrapper<>(
new PrefixQuery(new Term(name(), indexedValueForSearch(value)))
);
spanMulti.setRewriteMethod(method);
if (method != null) {
spanMulti.setRewriteMethod(method);
}
return spanMulti;
}
}
Expand Down Expand Up @@ -464,8 +466,9 @@ public Query prefixQuery(
automata.add(Automata.makeAnyChar());
}
Automaton automaton = Operations.concatenate(automata);
AutomatonQuery query = new AutomatonQuery(new Term(name(), value + "*"), automaton);
query.setRewriteMethod(method);
AutomatonQuery query = method == null
? new AutomatonQuery(new Term(name(), value + "*"), automaton, Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, false)
: new AutomatonQuery(new Term(name(), value + "*"), automaton, Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, false, method);
return new BooleanQuery.Builder().add(query, BooleanClause.Occur.SHOULD)
.add(new TermQuery(new Term(parentField, value)), BooleanClause.Occur.SHOULD)
.build();
Expand Down Expand Up @@ -649,7 +652,9 @@ public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRew
SpanMultiTermQueryWrapper<?> spanMulti = new SpanMultiTermQueryWrapper<>(
new PrefixQuery(new Term(name(), indexedValueForSearch(value)))
);
spanMulti.setRewriteMethod(method);
if (method != null) {
spanMulti.setRewriteMethod(method);
}
return spanMulti;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public void testPrefixQuery() {
// this term should be a length that can be rewriteable to a term query on the prefix field
final String withinBoundsTerm = "foo";
assertThat(
fieldType.prefixQuery(withinBoundsTerm, CONSTANT_SCORE_REWRITE, randomMockContext()),
fieldType.prefixQuery(withinBoundsTerm, randomBoolean() ? CONSTANT_SCORE_REWRITE : null, randomMockContext()),
equalTo(new ConstantScoreQuery(new TermQuery(new Term(NAME + "._index_prefix", withinBoundsTerm))))
);

Expand All @@ -118,13 +118,13 @@ public void testPrefixQuery() {
// this term should be too long to be rewriteable to a term query on the prefix field
final String longTerm = "toolongforourprefixfieldthistermis";
assertThat(
fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_CONTEXT),
fieldType.prefixQuery(longTerm, randomBoolean() ? null : CONSTANT_SCORE_REWRITE, MOCK_CONTEXT),
equalTo(new PrefixQuery(new Term(NAME, longTerm)))
);

ElasticsearchException ee = expectThrows(
ElasticsearchException.class,
() -> fieldType.prefixQuery(longTerm, CONSTANT_SCORE_REWRITE, MOCK_CONTEXT_DISALLOW_EXPENSIVE)
() -> fieldType.prefixQuery(longTerm, randomBoolean() ? null : CONSTANT_SCORE_REWRITE, MOCK_CONTEXT_DISALLOW_EXPENSIVE)
);
assertEquals(
"[prefix] queries cannot be executed when 'search.allow_expensive_queries' is set to false. "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
throw new UnsupportedOperationException("[fuzzy] queries are not supported on [" + CONTENT_TYPE + "] fields.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.script.CompositeFieldScript;
import org.elasticsearch.script.Script;
Expand Down Expand Up @@ -103,7 +104,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
throw new IllegalArgumentException(unsupported("fuzzy", "keyword and text"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,12 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
failIfNotIndexedNorDocValuesFallback(context);
if (isIndexed()) {
return super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context);
return super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context, rewriteMethod);
} else {
return StringScriptFieldFuzzyQuery.build(
new Script(""),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

package org.elasticsearch.index.mapper;

import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.MultiTermQuery.RewriteMethod;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.fielddata.FieldDataContext;
import org.elasticsearch.index.fielddata.StringScriptFieldData;
import org.elasticsearch.index.query.SearchExecutionContext;
Expand Down Expand Up @@ -124,7 +126,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
applyScriptContext(context);
return StringScriptFieldFuzzyQuery.build(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,13 +248,25 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
throw new IllegalArgumentException(
"Can only use fuzzy queries on keyword and text fields - not on [" + name + "] which is of type [" + typeName() + "]"
);
}

public Query fuzzyQuery(
Object value,
Fuzziness fuzziness,
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
) {
return fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context, null);
}

// Case sensitive form of prefix query
public final Query prefixQuery(String value, @Nullable MultiTermQuery.RewriteMethod method, SearchExecutionContext context) {
return prefixQuery(value, method, false, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
throw new QueryShardException(context, fail("fuzzy query"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.lucene.search.AutomatonQueries;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.index.query.support.QueryParsers;

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.elasticsearch.common.lucene.search.AutomatonQueries.caseInsensitivePrefix;
import static org.elasticsearch.common.lucene.search.AutomatonQueries.toCaseInsensitiveWildcardAutomaton;
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;

/** Base class for {@link MappedFieldType} implementations that use the same
Expand Down Expand Up @@ -59,7 +61,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
if (context.allowExpensiveQueries() == false) {
throw new ElasticsearchException(
Expand Down Expand Up @@ -87,19 +90,19 @@ public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, bool
);
}
failIfNotIndexed();
Term prefix = new Term(name(), indexedValueForSearch(value));
if (caseInsensitive) {
AutomatonQuery query = AutomatonQueries.caseInsensitivePrefixQuery((new Term(name(), indexedValueForSearch(value))));
if (method != null) {
query.setRewriteMethod(method);
}
return query;

return method == null
? new AutomatonQuery(prefix, caseInsensitivePrefix(prefix.text()), Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, false)
: new AutomatonQuery(
prefix,
caseInsensitivePrefix(prefix.text()),
Operations.DEFAULT_DETERMINIZE_WORK_LIMIT,
false,
method
);
}
PrefixQuery query = new PrefixQuery(new Term(name(), indexedValueForSearch(value)));
if (method != null) {
query.setRewriteMethod(method);
}
return query;
return method == null ? new PrefixQuery(prefix) : new PrefixQuery(prefix, method);
}

public static final String normalizeWildcardPattern(String fieldname, String value, Analyzer normalizer) {
Expand Down Expand Up @@ -164,13 +167,17 @@ protected Query wildcardQuery(
term = new Term(name(), indexedValueForSearch(value));
}
if (caseInsensitive) {
AutomatonQuery query = AutomatonQueries.caseInsensitiveWildcardQuery(term);
QueryParsers.setRewriteMethod(query, method);
return query;
return method == null
? new AutomatonQuery(term, toCaseInsensitiveWildcardAutomaton(term, Integer.MAX_VALUE))
: new AutomatonQuery(
term,
toCaseInsensitiveWildcardAutomaton(term, Integer.MAX_VALUE),
Operations.DEFAULT_DETERMINIZE_WORK_LIMIT,
false,
method
);
}
WildcardQuery query = new WildcardQuery(term);
QueryParsers.setRewriteMethod(query, method);
return query;
return method == null ? new WildcardQuery(term) : new WildcardQuery(term, Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, method);
}

@Override
Expand All @@ -188,11 +195,16 @@ public Query regexpQuery(
);
}
failIfNotIndexed();
RegexpQuery query = new RegexpQuery(new Term(name(), indexedValueForSearch(value)), syntaxFlags, matchFlags, maxDeterminizedStates);
if (method != null) {
query.setRewriteMethod(method);
}
return query;
return method == null
? new RegexpQuery(new Term(name(), indexedValueForSearch(value)), syntaxFlags, matchFlags, maxDeterminizedStates)
: new RegexpQuery(
new Term(name(), indexedValueForSearch(value)),
syntaxFlags,
matchFlags,
RegexpQuery.DEFAULT_PROVIDER,
maxDeterminizedStates,
method
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.analysis.AnalyzerScope;
import org.elasticsearch.index.analysis.IndexAnalyzers;
import org.elasticsearch.index.analysis.NamedAnalyzer;
Expand Down Expand Up @@ -618,8 +619,9 @@ public Query prefixQuery(
automata.add(Automata.makeAnyChar());
}
Automaton automaton = Operations.concatenate(automata);
AutomatonQuery query = new AutomatonQuery(new Term(name(), value + "*"), automaton);
query.setRewriteMethod(method);
AutomatonQuery query = method == null
? new AutomatonQuery(new Term(name(), value + "*"), automaton, Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, false)
: new AutomatonQuery(new Term(name(), value + "*"), automaton, Operations.DEFAULT_DETERMINIZE_WORK_LIMIT, false, method);
return new BooleanQuery.Builder().add(query, BooleanClause.Occur.SHOULD)
.add(new TermQuery(new Term(parentField.name(), value)), BooleanClause.Occur.SHOULD)
.build();
Expand Down Expand Up @@ -809,7 +811,9 @@ public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRew
SpanMultiTermQueryWrapper<?> spanMulti = new SpanMultiTermQueryWrapper<>(
new PrefixQuery(new Term(name(), indexedValueForSearch(value)))
);
spanMulti.setRewriteMethod(method);
if (method != null) {
spanMulti.setRewriteMethod(method);
}
return spanMulti;
}
}
Expand Down Expand Up @@ -1064,10 +1068,13 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
// Disable scoring
return new ConstantScoreQuery(super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context));
return new ConstantScoreQuery(
super.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context, rewriteMethod)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.elasticsearch.common.lucene.search.AutomatonQueries;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.index.fielddata.FieldData;
import org.elasticsearch.index.fielddata.FieldDataContext;
Expand Down Expand Up @@ -272,7 +273,8 @@ public Query fuzzyQuery(
int prefixLength,
int maxExpansions,
boolean transpositions,
SearchExecutionContext context
SearchExecutionContext context,
@Nullable MultiTermQuery.RewriteMethod rewriteMethod
) {
throw new UnsupportedOperationException(
"[fuzzy] queries are not currently supported on keyed " + "[" + CONTENT_TYPE + "] fields."
Expand Down
Loading