Backport of fe9145f Closes #61546
This commit is contained in:
parent
0d5250c99b
commit
a0df0fb074
|
@ -41,6 +41,10 @@ provided `<field>`.
|
|||
(Optional, string) Method used to rewrite the query. For valid values and more
|
||||
information, see the <<query-dsl-multi-term-rewrite, `rewrite` parameter>>.
|
||||
|
||||
`case_insensitive`::
|
||||
(Optional, boolean) allows ASCII case insensitive matching of the
|
||||
value with the indexed field values when set to true. Setting to false is disallowed.
|
||||
|
||||
[[prefix-query-notes]]
|
||||
==== Notes
|
||||
|
||||
|
|
|
@ -62,6 +62,10 @@ Boost values are relative to the default value of `1.0`. A boost value between
|
|||
`0` and `1.0` decreases the relevance score. A value greater than `1.0`
|
||||
increases the relevance score.
|
||||
|
||||
`case_insensitive`::
|
||||
(Optional, boolean) allows ASCII case insensitive matching of the
|
||||
value with the indexed field values when set to true. Setting to false is disallowed.
|
||||
|
||||
[[term-query-notes]]
|
||||
==== Notes
|
||||
|
||||
|
@ -84,7 +88,7 @@ The `term` query does *not* analyze the search term. The `term` query only
|
|||
searches for the *exact* term you provide. This means the `term` query may
|
||||
return poor or no results when searching `text` fields.
|
||||
|
||||
To see the difference in search results, try the following example.
|
||||
To see the difference in search results, try the following example.
|
||||
|
||||
. Create an index with a `text` field called `full_text`.
|
||||
+
|
||||
|
@ -214,4 +218,4 @@ in the results.
|
|||
}
|
||||
----
|
||||
// TESTRESPONSE[s/"took" : 1/"took" : $body.took/]
|
||||
--
|
||||
--
|
||||
|
|
|
@ -52,7 +52,7 @@ This parameter supports two wildcard operators:
|
|||
|
||||
WARNING: Avoid beginning patterns with `*` or `?`. This can increase
|
||||
the iterations needed to find matching terms and slow search performance.
|
||||
--
|
||||
--
|
||||
|
||||
`boost`::
|
||||
(Optional, float) Floating point number used to decrease or increase the
|
||||
|
@ -69,6 +69,10 @@ increases the relevance score.
|
|||
(Optional, string) Method used to rewrite the query. For valid values and more information, see the
|
||||
<<query-dsl-multi-term-rewrite, `rewrite` parameter>>.
|
||||
|
||||
`case_insensitive`::
|
||||
(Optional, boolean) allows case insensitive matching of the
|
||||
pattern with the indexed field values when set to true. Setting to false is disallowed.
|
||||
|
||||
[[wildcard-query-notes]]
|
||||
==== Notes
|
||||
===== Allow expensive queries
|
||||
|
|
|
@ -281,11 +281,11 @@ public class SearchAsYouTypeFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (prefixField == null || prefixField.termLengthWithinBounds(value.length()) == false) {
|
||||
return super.prefixQuery(value, method, context);
|
||||
return super.prefixQuery(value, method, caseInsensitive, context);
|
||||
} else {
|
||||
final Query query = prefixField.prefixQuery(value, method, context);
|
||||
final Query query = prefixField.prefixQuery(value, method, caseInsensitive, context);
|
||||
if (method == null
|
||||
|| method == MultiTermQuery.CONSTANT_SCORE_REWRITE
|
||||
|| method == MultiTermQuery.CONSTANT_SCORE_BOOLEAN_REWRITE) {
|
||||
|
@ -365,8 +365,11 @@ public class SearchAsYouTypeFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (value.length() >= minChars) {
|
||||
if(caseInsensitive) {
|
||||
return super.termQueryCaseInsensitive(value, context);
|
||||
}
|
||||
return super.termQuery(value, context);
|
||||
}
|
||||
List<Automaton> automata = new ArrayList<>();
|
||||
|
@ -507,11 +510,11 @@ public class SearchAsYouTypeFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (prefixFieldType == null || prefixFieldType.termLengthWithinBounds(value.length()) == false) {
|
||||
return super.prefixQuery(value, method, context);
|
||||
return super.prefixQuery(value, method, caseInsensitive, context);
|
||||
} else {
|
||||
final Query query = prefixFieldType.prefixQuery(value, method, context);
|
||||
final Query query = prefixFieldType.prefixQuery(value, method, caseInsensitive, context);
|
||||
if (method == null
|
||||
|| method == MultiTermQuery.CONSTANT_SCORE_REWRITE
|
||||
|| method == MultiTermQuery.CONSTANT_SCORE_BOOLEAN_REWRITE) {
|
||||
|
|
|
@ -136,13 +136,15 @@ public class ICUCollationKeywordFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method,
|
||||
boolean caseInsensitive, QueryShardContext context) {
|
||||
throw new UnsupportedOperationException("[prefix] queries are not supported on [" + CONTENT_TYPE + "] fields.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value,
|
||||
@Nullable MultiTermQuery.RewriteMethod method,
|
||||
boolean caseInsensitive,
|
||||
QueryShardContext context) {
|
||||
throw new UnsupportedOperationException("[wildcard] queries are not supported on [" + CONTENT_TYPE + "] fields.");
|
||||
}
|
||||
|
|
|
@ -879,4 +879,18 @@ public class Strings {
|
|||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static String toLowercaseAscii(String in) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
Iterator<Integer> iter = in.codePoints().iterator();
|
||||
while (iter.hasNext()) {
|
||||
int codepoint = iter.next();
|
||||
if (codepoint > 128) {
|
||||
out.appendCodePoint(codepoint);
|
||||
} else {
|
||||
out.appendCodePoint(Character.toLowerCase(codepoint));
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.common.lucene.search;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.AutomatonQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.automaton.Automata;
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.MinimizationOperations;
|
||||
import org.apache.lucene.util.automaton.Operations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper functions for creating various forms of {@link AutomatonQuery}
|
||||
*/
|
||||
public class AutomatonQueries {
|
||||
|
||||
|
||||
|
||||
/** Build an automaton query accepting all terms with the specified prefix, ASCII case insensitive. */
|
||||
public static Automaton caseInsensitivePrefix(String s) {
|
||||
List<Automaton> list = new ArrayList<>();
|
||||
Iterator<Integer> iter = s.codePoints().iterator();
|
||||
while (iter.hasNext()) {
|
||||
list.add(toCaseInsensitiveChar(iter.next(), Integer.MAX_VALUE));
|
||||
}
|
||||
list.add(Automata.makeAnyString());
|
||||
|
||||
Automaton a = Operations.concatenate(list);
|
||||
a = MinimizationOperations.minimize(a, Integer.MAX_VALUE);
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
/** Build an automaton query accepting all terms with the specified prefix, ASCII case insensitive. */
|
||||
public static AutomatonQuery caseInsensitivePrefixQuery(Term prefix) {
|
||||
return new AutomatonQuery(prefix, caseInsensitivePrefix(prefix.text()));
|
||||
}
|
||||
|
||||
/** Build an automaton accepting all terms ASCII case insensitive. */
|
||||
public static AutomatonQuery caseInsensitiveTermQuery(Term term) {
|
||||
BytesRef prefix = term.bytes();
|
||||
return new AutomatonQuery(term, toCaseInsensitiveString(prefix,Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
|
||||
/** Build an automaton matching a wildcard pattern, ASCII case insensitive. */
|
||||
public static AutomatonQuery caseInsensitiveWildcardQuery(Term wildcardquery) {
|
||||
return new AutomatonQuery(wildcardquery, toCaseInsensitiveWildcardAutomaton(wildcardquery,Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
|
||||
/** String equality with support for wildcards */
|
||||
public static final char WILDCARD_STRING = '*';
|
||||
|
||||
/** Char equality with support for wildcards */
|
||||
public static final char WILDCARD_CHAR = '?';
|
||||
|
||||
/** Escape character */
|
||||
public static final char WILDCARD_ESCAPE = '\\';
|
||||
/**
|
||||
* Convert Lucene wildcard syntax into an automaton.
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
public static Automaton toCaseInsensitiveWildcardAutomaton(Term wildcardquery, int maxDeterminizedStates) {
|
||||
List<Automaton> automata = new ArrayList<>();
|
||||
|
||||
String wildcardText = wildcardquery.text();
|
||||
|
||||
for (int i = 0; i < wildcardText.length();) {
|
||||
final int c = wildcardText.codePointAt(i);
|
||||
int length = Character.charCount(c);
|
||||
switch(c) {
|
||||
case WILDCARD_STRING:
|
||||
automata.add(Automata.makeAnyString());
|
||||
break;
|
||||
case WILDCARD_CHAR:
|
||||
automata.add(Automata.makeAnyChar());
|
||||
break;
|
||||
case WILDCARD_ESCAPE:
|
||||
// add the next codepoint instead, if it exists
|
||||
if (i + length < wildcardText.length()) {
|
||||
final int nextChar = wildcardText.codePointAt(i + length);
|
||||
length += Character.charCount(nextChar);
|
||||
automata.add(Automata.makeChar(nextChar));
|
||||
break;
|
||||
} // else fallthru, lenient parsing with a trailing \
|
||||
default:
|
||||
automata.add(toCaseInsensitiveChar(c, maxDeterminizedStates));
|
||||
}
|
||||
i += length;
|
||||
}
|
||||
|
||||
return Operations.concatenate(automata);
|
||||
}
|
||||
|
||||
protected static Automaton toCaseInsensitiveString(BytesRef br, int maxDeterminizedStates) {
|
||||
return toCaseInsensitiveString(br.utf8ToString(), maxDeterminizedStates);
|
||||
}
|
||||
|
||||
public static Automaton toCaseInsensitiveString(String s, int maxDeterminizedStates) {
|
||||
List<Automaton> list = new ArrayList<>();
|
||||
Iterator<Integer> iter = s.codePoints().iterator();
|
||||
while (iter.hasNext()) {
|
||||
list.add(toCaseInsensitiveChar(iter.next(), maxDeterminizedStates));
|
||||
}
|
||||
|
||||
Automaton a = Operations.concatenate(list);
|
||||
a = MinimizationOperations.minimize(a, maxDeterminizedStates);
|
||||
return a;
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected static Automaton toCaseInsensitiveChar(int codepoint, int maxDeterminizedStates) {
|
||||
Automaton case1 = Automata.makeChar(codepoint);
|
||||
// For now we only work with ASCII characters
|
||||
if (codepoint > 128) {
|
||||
return case1;
|
||||
}
|
||||
int altCase = Character.isLowerCase(codepoint) ? Character.toUpperCase(codepoint) : Character.toLowerCase(codepoint);
|
||||
Automaton result;
|
||||
if (altCase != codepoint) {
|
||||
result = Operations.union(case1, Automata.makeChar(altCase));
|
||||
result = MinimizationOperations.minimize(result, maxDeterminizedStates);
|
||||
} else {
|
||||
result = case1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -79,15 +79,39 @@ public class Regex {
|
|||
* Match a String against the given pattern, supporting the following simple
|
||||
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
|
||||
* arbitrary number of pattern parts), as well as direct equality.
|
||||
* Matching is case sensitive.
|
||||
*
|
||||
* @param pattern the pattern to match against
|
||||
* @param str the String to match
|
||||
* @return whether the String matches the given pattern
|
||||
*/
|
||||
public static boolean simpleMatch(String pattern, String str) {
|
||||
return simpleMatch(pattern, str, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Match a String against the given pattern, supporting the following simple
|
||||
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
|
||||
* arbitrary number of pattern parts), as well as direct equality.
|
||||
*
|
||||
* @param pattern the pattern to match against
|
||||
* @param str the String to match
|
||||
* @param caseInsensitive true if ASCII case differences should be ignored
|
||||
* @return whether the String matches the given pattern
|
||||
*/
|
||||
public static boolean simpleMatch(String pattern, String str, boolean caseInsensitive) {
|
||||
if (pattern == null || str == null) {
|
||||
return false;
|
||||
}
|
||||
if (caseInsensitive) {
|
||||
pattern = Strings.toLowercaseAscii(pattern);
|
||||
str = Strings.toLowercaseAscii(str);
|
||||
}
|
||||
return simpleMatchWithNormalizedStrings(pattern, str);
|
||||
}
|
||||
|
||||
private static boolean simpleMatchWithNormalizedStrings(String pattern, String str) {
|
||||
final int firstIndex = pattern.indexOf('*');
|
||||
if (firstIndex == -1) {
|
||||
return pattern.equals(str);
|
||||
|
@ -102,12 +126,12 @@ public class Regex {
|
|||
return str.regionMatches(str.length() - pattern.length() + 1, pattern, 1, pattern.length() - 1);
|
||||
} else if (nextIndex == 1) {
|
||||
// Double wildcard "**" - skipping the first "*"
|
||||
return simpleMatch(pattern.substring(1), str);
|
||||
return simpleMatchWithNormalizedStrings(pattern.substring(1), str);
|
||||
}
|
||||
final String part = pattern.substring(1, nextIndex);
|
||||
int partIndex = str.indexOf(part);
|
||||
while (partIndex != -1) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
|
||||
if (simpleMatchWithNormalizedStrings(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
|
||||
return true;
|
||||
}
|
||||
partIndex = str.indexOf(part, partIndex + 1);
|
||||
|
@ -116,9 +140,9 @@ public class Regex {
|
|||
}
|
||||
return str.regionMatches(0, pattern, 0, firstIndex)
|
||||
&& (firstIndex == pattern.length() - 1 // only wildcard in pattern is at the end, so no need to look at the rest of the string
|
||||
|| simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
|
||||
}
|
||||
|
||||
|| simpleMatchWithNormalizedStrings(pattern.substring(firstIndex), str.substring(firstIndex)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a String against the given patterns, supporting the following simple
|
||||
* pattern styles: "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an
|
||||
|
|
|
@ -59,7 +59,7 @@ public abstract class ConstantFieldType extends MappedFieldType {
|
|||
* Return whether the constant value of this field matches the provided {@code pattern}
|
||||
* as documented in {@link Regex#simpleMatch}.
|
||||
*/
|
||||
protected abstract boolean matches(String pattern, QueryShardContext context);
|
||||
protected abstract boolean matches(String pattern, boolean caseInsensitive, QueryShardContext context);
|
||||
|
||||
private static String valueToString(Object value) {
|
||||
return value instanceof BytesRef
|
||||
|
@ -70,31 +70,42 @@ public abstract class ConstantFieldType extends MappedFieldType {
|
|||
@Override
|
||||
public final Query termQuery(Object value, QueryShardContext context) {
|
||||
String pattern = valueToString(value);
|
||||
if (matches(pattern, context)) {
|
||||
if (matches(pattern, false, context)) {
|
||||
return Queries.newMatchAllQuery();
|
||||
} else {
|
||||
return new MatchNoDocsQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
String pattern = valueToString(value);
|
||||
if (matches(pattern, true, context)) {
|
||||
return Queries.newMatchAllQuery();
|
||||
} else {
|
||||
return new MatchNoDocsQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query termsQuery(List<?> values, QueryShardContext context) {
|
||||
for (Object value : values) {
|
||||
String pattern = valueToString(value);
|
||||
if (matches(pattern, context)) {
|
||||
if (matches(pattern, false, context)) {
|
||||
// `terms` queries are a disjunction, so one matching term is enough
|
||||
return Queries.newMatchAllQuery();
|
||||
}
|
||||
}
|
||||
return new MatchNoDocsQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query prefixQuery(String prefix,
|
||||
@Nullable MultiTermQuery.RewriteMethod method,
|
||||
boolean caseInsensitive,
|
||||
QueryShardContext context) {
|
||||
String pattern = prefix + "*";
|
||||
if (matches(pattern, context)) {
|
||||
if (matches(pattern, caseInsensitive, context)) {
|
||||
return Queries.newMatchAllQuery();
|
||||
} else {
|
||||
return new MatchNoDocsQuery();
|
||||
|
@ -104,8 +115,9 @@ public abstract class ConstantFieldType extends MappedFieldType {
|
|||
@Override
|
||||
public final Query wildcardQuery(String value,
|
||||
@Nullable MultiTermQuery.RewriteMethod method,
|
||||
boolean caseInsensitive,
|
||||
QueryShardContext context) {
|
||||
if (matches(value, context)) {
|
||||
if (matches(value, caseInsensitive, context)) {
|
||||
return Queries.newMatchAllQuery();
|
||||
} else {
|
||||
return new MatchNoDocsQuery();
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper;
|
|||
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||
import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
@ -52,7 +53,12 @@ public class IndexFieldMapper extends MetadataFieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(String pattern, QueryShardContext context) {
|
||||
protected boolean matches(String pattern, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (caseInsensitive) {
|
||||
// Thankfully, all index names are lower-cased so we don't have to pass a case_insensitive mode flag
|
||||
// down to all the index name-matching logic. We just lower-case the search string
|
||||
pattern = Strings.toLowercaseAscii(pattern);
|
||||
}
|
||||
return context.indexMatches(pattern);
|
||||
}
|
||||
|
||||
|
|
|
@ -176,6 +176,13 @@ public abstract class MappedFieldType {
|
|||
*/
|
||||
// TODO: Standardize exception types
|
||||
public abstract Query termQuery(Object value, @Nullable QueryShardContext context);
|
||||
|
||||
|
||||
// Case insensitive form of term query (not supported by all fields so must be overridden to enable)
|
||||
public Query termQueryCaseInsensitive(Object value, @Nullable QueryShardContext context) {
|
||||
throw new QueryShardException(context, "[" + name + "] field which is of type [" + typeName() +
|
||||
"], does not support case insensitive term queries");
|
||||
}
|
||||
|
||||
/** Build a constant-scoring query that matches all values. The default implementation uses a
|
||||
* {@link ConstantScoreQuery} around a {@link BooleanQuery} whose {@link Occur#SHOULD} clauses
|
||||
|
@ -206,14 +213,27 @@ public abstract class MappedFieldType {
|
|||
+ "] which is of type [" + typeName() + "]");
|
||||
}
|
||||
|
||||
public Query prefixQuery(String value, @Nullable MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
// Case sensitive form of prefix query
|
||||
public final Query prefixQuery(String value, @Nullable MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
return prefixQuery(value, method, false, context);
|
||||
}
|
||||
|
||||
public Query prefixQuery(String value, @Nullable MultiTermQuery.RewriteMethod method, boolean caseInsensitve,
|
||||
QueryShardContext context) {
|
||||
throw new QueryShardException(context, "Can only use prefix queries on keyword, text and wildcard fields - not on [" + name
|
||||
+ "] which is of type [" + typeName() + "]");
|
||||
}
|
||||
|
||||
// Case sensitive form of wildcard query
|
||||
public final Query wildcardQuery(String value,
|
||||
@Nullable MultiTermQuery.RewriteMethod method, QueryShardContext context
|
||||
) {
|
||||
return wildcardQuery(value, method, false, context);
|
||||
}
|
||||
|
||||
public Query wildcardQuery(String value,
|
||||
@Nullable MultiTermQuery.RewriteMethod method,
|
||||
QueryShardContext context) {
|
||||
boolean caseInsensitve, QueryShardContext context) {
|
||||
throw new QueryShardException(context, "Can only use wildcard queries on keyword, text and wildcard fields - not on [" + name
|
||||
+ "] which is of type [" + typeName() + "]");
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper;
|
|||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.AutomatonQuery;
|
||||
import org.apache.lucene.search.FuzzyQuery;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.search.PrefixQuery;
|
||||
|
@ -32,6 +33,7 @@ import org.apache.lucene.util.BytesRef;
|
|||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
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.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.support.QueryParsers;
|
||||
|
@ -68,13 +70,21 @@ public abstract class StringFieldType extends TermBasedFieldType {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (context.allowExpensiveQueries() == false) {
|
||||
throw new ElasticsearchException("[prefix] queries cannot be executed when '" +
|
||||
ALLOW_EXPENSIVE_QUERIES.getKey() + "' is set to false. For optimised prefix queries on text " +
|
||||
"fields please enable [index_prefixes].");
|
||||
}
|
||||
failIfNotIndexed();
|
||||
if (caseInsensitive) {
|
||||
AutomatonQuery query = AutomatonQueries.caseInsensitivePrefixQuery((new Term(name(), indexedValueForSearch(value))));
|
||||
if (method != null) {
|
||||
query.setRewriteMethod(method);
|
||||
}
|
||||
return query;
|
||||
|
||||
}
|
||||
PrefixQuery query = new PrefixQuery(new Term(name(), indexedValueForSearch(value)));
|
||||
if (method != null) {
|
||||
query.setRewriteMethod(method);
|
||||
|
@ -113,7 +123,7 @@ public abstract class StringFieldType extends TermBasedFieldType {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
failIfNotIndexed();
|
||||
if (context.allowExpensiveQueries() == false) {
|
||||
throw new ElasticsearchException("[wildcard] queries cannot be executed when '" +
|
||||
|
@ -127,7 +137,11 @@ public abstract class StringFieldType extends TermBasedFieldType {
|
|||
} else {
|
||||
term = new Term(name(), indexedValueForSearch(value));
|
||||
}
|
||||
|
||||
if (caseInsensitive) {
|
||||
AutomatonQuery query = AutomatonQueries.caseInsensitiveWildcardQuery(term);
|
||||
QueryParsers.setRewriteMethod(query, method);
|
||||
return query;
|
||||
}
|
||||
WildcardQuery query = new WildcardQuery(term);
|
||||
QueryParsers.setRewriteMethod(query, method);
|
||||
return query;
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.lucene.search.TermInSetQuery;
|
|||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.common.lucene.BytesRefs;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -47,6 +48,16 @@ public abstract class TermBasedFieldType extends SimpleMappedFieldType {
|
|||
return BytesRefs.toBytesRef(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
failIfNotIndexed();
|
||||
Query query = AutomatonQueries.caseInsensitiveTermQuery(new Term(name(), indexedValueForSearch(value)));
|
||||
if (boost() != 1f) {
|
||||
query = new BoostQuery(query, boost());
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQuery(Object value, QueryShardContext context) {
|
||||
failIfNotIndexed();
|
||||
|
|
|
@ -60,6 +60,7 @@ import org.apache.lucene.util.automaton.Operations;
|
|||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.collect.Iterators;
|
||||
import org.elasticsearch.common.lucene.Lucene;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||
|
@ -439,12 +440,20 @@ public class TextFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (value.length() >= minChars) {
|
||||
if (caseInsensitive) {
|
||||
return super.termQueryCaseInsensitive(value, context);
|
||||
}
|
||||
return super.termQuery(value, context);
|
||||
}
|
||||
List<Automaton> automata = new ArrayList<>();
|
||||
automata.add(Automata.makeString(value));
|
||||
if (caseInsensitive) {
|
||||
automata.add(AutomatonQueries.toCaseInsensitiveString(value, Integer.MAX_VALUE));
|
||||
} else {
|
||||
automata.add(Automata.makeString(value));
|
||||
}
|
||||
|
||||
for (int i = value.length(); i < minChars; i++) {
|
||||
automata.add(Automata.makeAnyChar());
|
||||
}
|
||||
|
@ -636,11 +645,11 @@ public class TextFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (prefixFieldType == null || prefixFieldType.accept(value.length()) == false) {
|
||||
return super.prefixQuery(value, method, context);
|
||||
return super.prefixQuery(value, method, caseInsensitive,context);
|
||||
}
|
||||
Query tq = prefixFieldType.prefixQuery(value, method, context);
|
||||
Query tq = prefixFieldType.prefixQuery(value, method, caseInsensitive, context);
|
||||
if (method == null || method == MultiTermQuery.CONSTANT_SCORE_REWRITE
|
||||
|| method == MultiTermQuery.CONSTANT_SCORE_BOOLEAN_REWRITE) {
|
||||
return new ConstantScoreQuery(tq);
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.apache.lucene.index.IndexOptions;
|
|||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermStates;
|
||||
import org.apache.lucene.search.AutomatonQuery;
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.ConstantScoreQuery;
|
||||
|
@ -38,6 +39,7 @@ import org.apache.lucene.search.TermQuery;
|
|||
import org.apache.lucene.search.WildcardQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||
import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData;
|
||||
|
@ -156,7 +158,7 @@ public class TypeFieldMapper extends MetadataFieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
Query termQuery = termQuery(value, context);
|
||||
if (termQuery instanceof MatchNoDocsQuery || termQuery instanceof MatchAllDocsQuery) {
|
||||
return termQuery;
|
||||
|
@ -168,6 +170,12 @@ public class TypeFieldMapper extends MetadataFieldMapper {
|
|||
}
|
||||
Term term = MappedFieldType.extractTerm(termQuery);
|
||||
|
||||
if (caseInsensitive) {
|
||||
AutomatonQuery query = AutomatonQueries.caseInsensitiveWildcardQuery(term);
|
||||
QueryParsers.setRewriteMethod(query, method);
|
||||
return query;
|
||||
}
|
||||
|
||||
WildcardQuery query = new WildcardQuery(term);
|
||||
QueryParsers.setRewriteMethod(query, method);
|
||||
return query;
|
||||
|
|
|
@ -152,18 +152,23 @@ public abstract class BaseTermQueryBuilder<QB extends BaseTermQueryBuilder<QB>>
|
|||
builder.startObject(getName());
|
||||
builder.startObject(fieldName);
|
||||
builder.field(VALUE_FIELD.getPreferredName(), maybeConvertToString(this.value));
|
||||
addExtraXContent(builder, params);
|
||||
printBoostAndQueryName(builder);
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
protected void addExtraXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
// Do nothing but allows subclasses to override.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int doHashCode() {
|
||||
protected int doHashCode() {
|
||||
return Objects.hash(fieldName, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean doEquals(QB other) {
|
||||
protected boolean doEquals(QB other) {
|
||||
return Objects.equals(fieldName, other.fieldName) &&
|
||||
Objects.equals(value, other.value);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.search.MatchAllDocsQuery;
|
|||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
|
@ -50,6 +51,11 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
private final String fieldName;
|
||||
|
||||
private final String value;
|
||||
|
||||
public static final boolean DEFAULT_CASE_INSENSITIVITY = false;
|
||||
private static final ParseField CASE_INSENSITIVE_FIELD = new ParseField("case_insensitive");
|
||||
private boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
|
||||
|
||||
private String rewrite;
|
||||
|
||||
|
@ -78,6 +84,9 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
fieldName = in.readString();
|
||||
value = in.readString();
|
||||
rewrite = in.readOptionalString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
caseInsensitive = in.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,6 +94,9 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
out.writeString(fieldName);
|
||||
out.writeString(value);
|
||||
out.writeOptionalString(rewrite);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
out.writeBoolean(caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -95,6 +107,18 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public PrefixQueryBuilder caseInsensitive(boolean caseInsensitive) {
|
||||
if (caseInsensitive == false) {
|
||||
throw new IllegalArgumentException("The case insensitive setting cannot be set to false.");
|
||||
}
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean caseInsensitive() {
|
||||
return this.caseInsensitive;
|
||||
}
|
||||
|
||||
public PrefixQueryBuilder rewrite(String rewrite) {
|
||||
this.rewrite = rewrite;
|
||||
|
@ -113,6 +137,9 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
if (rewrite != null) {
|
||||
builder.field(REWRITE_FIELD.getPreferredName(), rewrite);
|
||||
}
|
||||
if (caseInsensitive != DEFAULT_CASE_INSENSITIVITY) {
|
||||
builder.field(CASE_INSENSITIVE_FIELD.getPreferredName(), caseInsensitive);
|
||||
}
|
||||
printBoostAndQueryName(builder);
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
|
@ -125,6 +152,7 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
|
||||
String queryName = null;
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -145,6 +173,12 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
boost = parser.floatValue();
|
||||
} else if (REWRITE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
rewrite = parser.textOrNull();
|
||||
} else if (CASE_INSENSITIVE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
caseInsensitive = parser.booleanValue();
|
||||
if (caseInsensitive == false) {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"[prefix] query does not support [" + currentFieldName + "] = false");
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"[prefix] query does not support [" + currentFieldName + "]");
|
||||
|
@ -158,10 +192,14 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
}
|
||||
}
|
||||
|
||||
return new PrefixQueryBuilder(fieldName, value)
|
||||
PrefixQueryBuilder result = new PrefixQueryBuilder(fieldName, value)
|
||||
.rewrite(rewrite)
|
||||
.boost(boost)
|
||||
.queryName(queryName);
|
||||
if (caseInsensitive) {
|
||||
result.caseInsensitive(caseInsensitive);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,7 +218,7 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
// This logic is correct for all field types, but by only applying it to constant
|
||||
// fields we also have the guarantee that it doesn't perform I/O, which is important
|
||||
// since rewrites might happen on a network thread.
|
||||
Query query = fieldType.prefixQuery(value, null, context); // the rewrite method doesn't matter
|
||||
Query query = fieldType.prefixQuery(value, null, caseInsensitive, context); // the rewrite method doesn't matter
|
||||
if (query instanceof MatchAllDocsQuery) {
|
||||
return new MatchAllQueryBuilder();
|
||||
} else if (query instanceof MatchNoDocsQuery) {
|
||||
|
@ -202,18 +240,19 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
|
|||
if (fieldType == null) {
|
||||
throw new IllegalStateException("Rewrite first");
|
||||
}
|
||||
return fieldType.prefixQuery(value, method, context);
|
||||
return fieldType.prefixQuery(value, method, caseInsensitive, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final int doHashCode() {
|
||||
return Objects.hash(fieldName, value, rewrite);
|
||||
return Objects.hash(fieldName, value, rewrite, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(PrefixQueryBuilder other) {
|
||||
return Objects.equals(fieldName, other.fieldName) &&
|
||||
Objects.equals(value, other.value) &&
|
||||
Objects.equals(rewrite, other.rewrite);
|
||||
Objects.equals(rewrite, other.rewrite) &&
|
||||
Objects.equals(caseInsensitive, other.caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,20 +22,31 @@ package org.elasticsearch.index.query;
|
|||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContent.Params;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.ConstantFieldType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A Query that matches documents containing a term.
|
||||
*/
|
||||
public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
||||
public static final String NAME = "term";
|
||||
public static final boolean DEFAULT_CASE_INSENSITIVITY = false;
|
||||
private static final ParseField CASE_INSENSITIVE_FIELD = new ParseField("case_insensitive");
|
||||
|
||||
|
||||
private boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
|
||||
|
||||
private static final ParseField TERM_FIELD = new ParseField("term");
|
||||
private static final ParseField VALUE_FIELD = new ParseField("value");
|
||||
|
@ -74,19 +85,43 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
|||
public TermQueryBuilder(String fieldName, Object value) {
|
||||
super(fieldName, value);
|
||||
}
|
||||
|
||||
public TermQueryBuilder caseInsensitive(boolean caseInsensitive) {
|
||||
if (caseInsensitive == false) {
|
||||
throw new IllegalArgumentException("The case insensitive setting cannot be set to false.");
|
||||
}
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean caseInsensitive() {
|
||||
return this.caseInsensitive;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read from a stream.
|
||||
*/
|
||||
public TermQueryBuilder(StreamInput in) throws IOException {
|
||||
super(in);
|
||||
if (in.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
caseInsensitive = in.readBoolean();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
||||
super.doWriteTo(out);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
out.writeBoolean(caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
public static TermQueryBuilder fromXContent(XContentParser parser) throws IOException {
|
||||
String queryName = null;
|
||||
String fieldName = null;
|
||||
Object value = null;
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -107,6 +142,12 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
|||
queryName = parser.text();
|
||||
} else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
boost = parser.floatValue();
|
||||
} else if (CASE_INSENSITIVE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
caseInsensitive = parser.booleanValue();
|
||||
if (caseInsensitive == false) {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"[term] query does not support [" + currentFieldName + "] = false");
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"[term] query does not support [" + currentFieldName + "]");
|
||||
|
@ -127,9 +168,19 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
|||
if (queryName != null) {
|
||||
termQuery.queryName(queryName);
|
||||
}
|
||||
if (caseInsensitive) {
|
||||
termQuery.caseInsensitive(caseInsensitive);
|
||||
}
|
||||
return termQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addExtraXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (caseInsensitive != DEFAULT_CASE_INSENSITIVITY) {
|
||||
builder.field(CASE_INSENSITIVE_FIELD.getPreferredName(), caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
|
||||
QueryShardContext context = queryRewriteContext.convertToShardContext();
|
||||
|
@ -141,7 +192,13 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
|||
// This logic is correct for all field types, but by only applying it to constant
|
||||
// fields we also have the guarantee that it doesn't perform I/O, which is important
|
||||
// since rewrites might happen on a network thread.
|
||||
Query query = fieldType.termQuery(value, context);
|
||||
Query query = null;
|
||||
if (caseInsensitive) {
|
||||
query = fieldType.termQueryCaseInsensitive(value, context);
|
||||
} else {
|
||||
query = fieldType.termQuery(value, context);
|
||||
}
|
||||
|
||||
if (query instanceof MatchAllDocsQuery) {
|
||||
return new MatchAllQueryBuilder();
|
||||
} else if (query instanceof MatchNoDocsQuery) {
|
||||
|
@ -160,11 +217,27 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
|
|||
if (mapper == null) {
|
||||
throw new IllegalStateException("Rewrite first");
|
||||
}
|
||||
return mapper.termQuery(this.value, context);
|
||||
if (caseInsensitive) {
|
||||
return mapper.termQueryCaseInsensitive(value, context);
|
||||
}
|
||||
return mapper.termQuery(value, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected final int doHashCode() {
|
||||
return Objects.hash(super.doHashCode(), caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final boolean doEquals(TermQueryBuilder other) {
|
||||
return super.doEquals(other) &&
|
||||
Objects.equals(caseInsensitive, other.caseInsensitive);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.search.MatchAllDocsQuery;
|
|||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.Strings;
|
||||
|
@ -59,6 +60,10 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
|
||||
private String rewrite;
|
||||
|
||||
public static final boolean DEFAULT_CASE_INSENSITIVITY = false;
|
||||
private static final ParseField CASE_INSENSITIVE_FIELD = new ParseField("case_insensitive");
|
||||
private boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
|
||||
/**
|
||||
* Implements the wildcard search query. Supported wildcards are {@code *}, which
|
||||
* matches any character sequence (including the empty one), and {@code ?},
|
||||
|
@ -89,6 +94,9 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
fieldName = in.readString();
|
||||
value = in.readString();
|
||||
rewrite = in.readOptionalString();
|
||||
if (in.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
caseInsensitive = in.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +104,9 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
out.writeString(fieldName);
|
||||
out.writeString(value);
|
||||
out.writeOptionalString(rewrite);
|
||||
if (out.getVersion().onOrAfter(Version.V_7_10_0)) {
|
||||
out.writeBoolean(caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,6 +126,18 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
public String rewrite() {
|
||||
return this.rewrite;
|
||||
}
|
||||
|
||||
public WildcardQueryBuilder caseInsensitive(boolean caseInsensitive) {
|
||||
if (caseInsensitive == false) {
|
||||
throw new IllegalArgumentException("The case insensitive setting cannot be set to false.");
|
||||
}
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean caseInsensitive() {
|
||||
return this.caseInsensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
|
@ -129,6 +152,9 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
if (rewrite != null) {
|
||||
builder.field(REWRITE_FIELD.getPreferredName(), rewrite);
|
||||
}
|
||||
if (caseInsensitive != DEFAULT_CASE_INSENSITIVITY) {
|
||||
builder.field(CASE_INSENSITIVE_FIELD.getPreferredName(), caseInsensitive);
|
||||
}
|
||||
printBoostAndQueryName(builder);
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
|
@ -139,6 +165,7 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
String rewrite = null;
|
||||
String value = null;
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
boolean caseInsensitive = DEFAULT_CASE_INSENSITIVITY;
|
||||
String queryName = null;
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
@ -160,6 +187,12 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
boost = parser.floatValue();
|
||||
} else if (REWRITE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
rewrite = parser.textOrNull();
|
||||
} else if (CASE_INSENSITIVE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
caseInsensitive = parser.booleanValue();
|
||||
if (caseInsensitive == false) {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"[prefix] query does not support [" + currentFieldName + "] = false");
|
||||
}
|
||||
} else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
queryName = parser.text();
|
||||
} else {
|
||||
|
@ -175,10 +208,14 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
}
|
||||
}
|
||||
|
||||
return new WildcardQueryBuilder(fieldName, value)
|
||||
WildcardQueryBuilder result = new WildcardQueryBuilder(fieldName, value)
|
||||
.rewrite(rewrite)
|
||||
.boost(boost)
|
||||
.queryName(queryName);
|
||||
if (caseInsensitive) {
|
||||
result.caseInsensitive(caseInsensitive);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -192,7 +229,7 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
// This logic is correct for all field types, but by only applying it to constant
|
||||
// fields we also have the guarantee that it doesn't perform I/O, which is important
|
||||
// since rewrites might happen on a network thread.
|
||||
Query query = fieldType.wildcardQuery(value, null, context); // the rewrite method doesn't matter
|
||||
Query query = fieldType.wildcardQuery(value, null, caseInsensitive, context); // the rewrite method doesn't matter
|
||||
if (query instanceof MatchAllDocsQuery) {
|
||||
return new MatchAllQueryBuilder();
|
||||
} else if (query instanceof MatchNoDocsQuery) {
|
||||
|
@ -216,18 +253,19 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
|
|||
|
||||
MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(
|
||||
rewrite, null, LoggingDeprecationHandler.INSTANCE);
|
||||
return fieldType.wildcardQuery(value, method, context);
|
||||
return fieldType.wildcardQuery(value, method, caseInsensitive, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
return Objects.hash(fieldName, value, rewrite);
|
||||
return Objects.hash(fieldName, value, rewrite, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(WildcardQueryBuilder other) {
|
||||
return Objects.equals(fieldName, other.fieldName) &&
|
||||
Objects.equals(value, other.value) &&
|
||||
Objects.equals(rewrite, other.rewrite);
|
||||
Objects.equals(rewrite, other.rewrite)&&
|
||||
Objects.equals(caseInsensitive, other.caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.elasticsearch.common.regex;
|
|||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -54,13 +55,17 @@ public class RegexTests extends ESTestCase {
|
|||
|
||||
public void testDoubleWildcardMatch() {
|
||||
assertTrue(Regex.simpleMatch("ddd", "ddd"));
|
||||
assertTrue(Regex.simpleMatch("ddd", "Ddd", true));
|
||||
assertFalse(Regex.simpleMatch("ddd", "Ddd"));
|
||||
assertTrue(Regex.simpleMatch("d*d*d", "dadd"));
|
||||
assertTrue(Regex.simpleMatch("**ddd", "dddd"));
|
||||
assertTrue(Regex.simpleMatch("**ddD", "dddd", true));
|
||||
assertFalse(Regex.simpleMatch("**ddd", "fff"));
|
||||
assertTrue(Regex.simpleMatch("fff*ddd", "fffabcddd"));
|
||||
assertTrue(Regex.simpleMatch("fff**ddd", "fffabcddd"));
|
||||
assertFalse(Regex.simpleMatch("fff**ddd", "fffabcdd"));
|
||||
assertTrue(Regex.simpleMatch("fff*******ddd", "fffabcddd"));
|
||||
assertTrue(Regex.simpleMatch("fff*******ddd", "FffAbcdDd", true));
|
||||
assertFalse(Regex.simpleMatch("fff******ddd", "fffabcdd"));
|
||||
}
|
||||
|
||||
|
@ -76,6 +81,8 @@ public class RegexTests extends ESTestCase {
|
|||
pattern = pattern.substring(0, shrinkStart) + "*" + pattern.substring(shrinkEnd);
|
||||
}
|
||||
assertTrue("[" + pattern + "] should match [" + matchingString + "]", Regex.simpleMatch(pattern, matchingString));
|
||||
assertTrue("[" + pattern + "] should match [" + matchingString.toUpperCase(Locale.ROOT) + "]",
|
||||
Regex.simpleMatch(pattern, matchingString.toUpperCase(Locale.ROOT), true));
|
||||
|
||||
// construct a pattern that does not match this string by inserting a non-matching character (a digit)
|
||||
final int insertPos = between(0, pattern.length());
|
||||
|
|
|
@ -46,7 +46,9 @@ public class IndexFieldTypeTests extends ESTestCase {
|
|||
MappedFieldType ft = IndexFieldMapper.IndexFieldType.INSTANCE;
|
||||
|
||||
assertEquals(new MatchAllDocsQuery(), ft.wildcardQuery("ind*x", null, createContext()));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.wildcardQuery("iNd*x", null, true, createContext()));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.wildcardQuery("other_ind*x", null, createContext()));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.wildcardQuery("Other_ind*x", null, true, createContext()));
|
||||
}
|
||||
|
||||
public void testRegexpQuery() {
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.elasticsearch.index.IndexSettings;
|
|||
import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
|
||||
import org.elasticsearch.index.mapper.RangeFieldMapper.RangeFieldType;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.index.query.QueryShardException;
|
||||
import org.elasticsearch.test.IndexSettingsModule;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
|
@ -480,4 +481,14 @@ public class RangeFieldTypeTests extends FieldTypeTestCase {
|
|||
assertEquals(getExpectedRangeQuery(relation, value, value, includeLower, includeUpper),
|
||||
ft.termQuery(value, context));
|
||||
}
|
||||
|
||||
public void testCaseInsensitiveQuery() throws Exception {
|
||||
QueryShardContext context = createContext();
|
||||
RangeFieldType ft = createDefaultFieldType();
|
||||
|
||||
Object value = nextFrom();
|
||||
QueryShardException ex = expectThrows(QueryShardException.class,
|
||||
() -> ft.termQueryCaseInsensitive(value, context));
|
||||
assertTrue(ex.getMessage().contains("does not support case insensitive term queries"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.lucene.util.automaton.Automaton;
|
|||
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.index.mapper.TextFieldMapper.TextFieldType;
|
||||
|
||||
|
@ -63,6 +64,7 @@ public class TextFieldTypeTests extends FieldTypeTestCase {
|
|||
public void testTermQuery() {
|
||||
MappedFieldType ft = createFieldType();
|
||||
assertEquals(new TermQuery(new Term("field", "foo")), ft.termQuery("foo", null));
|
||||
assertEquals(AutomatonQueries.caseInsensitiveTermQuery(new Term("field", "fOo")), ft.termQueryCaseInsensitive("fOo", null));
|
||||
|
||||
MappedFieldType unsearchable = new TextFieldType("field", false, Collections.emptyMap());
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
|
@ -132,18 +134,22 @@ public class TextFieldTypeTests extends FieldTypeTestCase {
|
|||
TextFieldType ft = createFieldType();
|
||||
ft.setPrefixFieldType(new TextFieldMapper.PrefixFieldType(ft, "field._index_prefix", 2, 10, true));
|
||||
|
||||
Query q = ft.prefixQuery("goin", CONSTANT_SCORE_REWRITE, randomMockShardContext());
|
||||
Query q = ft.prefixQuery("goin", CONSTANT_SCORE_REWRITE, false, randomMockShardContext());
|
||||
assertEquals(new ConstantScoreQuery(new TermQuery(new Term("field._index_prefix", "goin"))), q);
|
||||
|
||||
q = ft.prefixQuery("internationalisatio", CONSTANT_SCORE_REWRITE, MOCK_QSC);
|
||||
q = ft.prefixQuery("internationalisatio", CONSTANT_SCORE_REWRITE, false, MOCK_QSC);
|
||||
assertEquals(new PrefixQuery(new Term("field", "internationalisatio")), q);
|
||||
|
||||
q = ft.prefixQuery("Internationalisatio", CONSTANT_SCORE_REWRITE, true, MOCK_QSC);
|
||||
assertEquals(AutomatonQueries.caseInsensitivePrefixQuery(new Term("field", "Internationalisatio")), q);
|
||||
|
||||
|
||||
ElasticsearchException ee = expectThrows(ElasticsearchException.class,
|
||||
() -> ft.prefixQuery("internationalisatio", null, MOCK_QSC_DISALLOW_EXPENSIVE));
|
||||
() -> ft.prefixQuery("internationalisatio", null, false, MOCK_QSC_DISALLOW_EXPENSIVE));
|
||||
assertEquals("[prefix] queries cannot be executed when 'search.allow_expensive_queries' is set to false. " +
|
||||
"For optimised prefix queries on text fields please enable [index_prefixes].", ee.getMessage());
|
||||
|
||||
q = ft.prefixQuery("g", CONSTANT_SCORE_REWRITE, randomMockShardContext());
|
||||
q = ft.prefixQuery("g", CONSTANT_SCORE_REWRITE, false, randomMockShardContext());
|
||||
Automaton automaton
|
||||
= Operations.concatenate(Arrays.asList(Automata.makeChar('g'), Automata.makeAnyChar()));
|
||||
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
*/
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||
import org.elasticsearch.common.UUIDs;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
|
@ -61,6 +63,9 @@ public class TypeFieldTypeTests extends ESTestCase {
|
|||
query = ft.termQuery("my_type", context);
|
||||
assertEquals(new MatchAllDocsQuery(), query);
|
||||
|
||||
query = ft.termQueryCaseInsensitive("my_Type", context);
|
||||
assertEquals(AutomatonQueries.caseInsensitiveTermQuery(new Term("_type", "my_Type")), query);
|
||||
|
||||
Mockito.when(mapperService.hasNested()).thenReturn(true);
|
||||
query = ft.termQuery("my_type", context);
|
||||
assertEquals(Queries.newNonNestedFilter(context.indexVersionCreated()), query);
|
||||
|
@ -70,5 +75,9 @@ public class TypeFieldTypeTests extends ESTestCase {
|
|||
Mockito.when(mapperService.documentMapper()).thenReturn(mapper);
|
||||
query = ft.termQuery("my_type", context);
|
||||
assertEquals(new MatchNoDocsQuery(), query);
|
||||
|
||||
query = ft.termQueryCaseInsensitive("other_Type", context);
|
||||
assertEquals(AutomatonQueries.caseInsensitiveTermQuery(new Term("_type", "other_Type")), query);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,8 @@ public class PrefixQueryBuilderTests extends AbstractQueryTestCase<PrefixQueryBu
|
|||
|
||||
public void testFromJson() throws IOException {
|
||||
String json =
|
||||
"{ \"prefix\" : { \"user\" : { \"value\" : \"ki\", \"boost\" : 2.0 } }}";
|
||||
"{ \"prefix\" : { \"user\" : { \"value\" : \"ki\", \"boost\" : 2.0"
|
||||
+ "} }}";
|
||||
|
||||
PrefixQueryBuilder parsed = (PrefixQueryBuilder) parseQuery(json);
|
||||
checkGeneratedJson(json, parsed);
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.index.query;
|
|||
import com.fasterxml.jackson.core.io.JsonStringEncoder;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.AutomatonQuery;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.PointRangeQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
@ -87,12 +88,14 @@ public class TermQueryBuilderTests extends AbstractTermQueryTestCase<TermQueryBu
|
|||
*/
|
||||
@Override
|
||||
protected TermQueryBuilder createQueryBuilder(String fieldName, Object value) {
|
||||
return new TermQueryBuilder(fieldName, value);
|
||||
TermQueryBuilder result = new TermQueryBuilder(fieldName, value);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertLuceneQuery(TermQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
|
||||
assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(PointRangeQuery.class)).or(instanceOf(MatchNoDocsQuery.class)));
|
||||
assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(PointRangeQuery.class)).or(instanceOf(MatchNoDocsQuery.class))
|
||||
.or(instanceOf(AutomatonQuery.class)));
|
||||
MappedFieldType mapper = context.fieldMapper(queryBuilder.fieldName());
|
||||
if (query instanceof TermQuery) {
|
||||
TermQuery termQuery = (TermQuery) query;
|
||||
|
@ -100,14 +103,21 @@ public class TermQueryBuilderTests extends AbstractTermQueryTestCase<TermQueryBu
|
|||
String expectedFieldName = expectedFieldName(queryBuilder.fieldName());
|
||||
assertThat(termQuery.getTerm().field(), equalTo(expectedFieldName));
|
||||
|
||||
Term term = ((TermQuery) mapper.termQuery(queryBuilder.value(), null)).getTerm();
|
||||
Term term = ((TermQuery) termQuery(mapper, queryBuilder.value(), queryBuilder.caseInsensitive())).getTerm();
|
||||
assertThat(termQuery.getTerm(), equalTo(term));
|
||||
} else if (mapper != null) {
|
||||
assertEquals(query, mapper.termQuery(queryBuilder.value(), null));
|
||||
assertEquals(query, termQuery(mapper, queryBuilder.value(), queryBuilder.caseInsensitive()));
|
||||
} else {
|
||||
assertThat(query, instanceOf(MatchNoDocsQuery.class));
|
||||
}
|
||||
}
|
||||
|
||||
private Query termQuery(MappedFieldType mapper, Object value, boolean caseInsensitive) {
|
||||
if (caseInsensitive) {
|
||||
return mapper.termQueryCaseInsensitive(value, null);
|
||||
}
|
||||
return mapper.termQuery(value, null);
|
||||
}
|
||||
|
||||
public void testTermArray() throws IOException {
|
||||
String queryAsString = "{\n" +
|
||||
|
|
|
@ -103,7 +103,8 @@ public class WildcardQueryBuilderTests extends AbstractQueryTestCase<WildcardQue
|
|||
}
|
||||
|
||||
public void testFromJson() throws IOException {
|
||||
String json = "{ \"wildcard\" : { \"user\" : { \"wildcard\" : \"ki*y\", \"boost\" : 2.0 } }}";
|
||||
String json = "{ \"wildcard\" : { \"user\" : { \"wildcard\" : \"ki*y\", \"boost\" : 2.0"
|
||||
+ " } }}";
|
||||
WildcardQueryBuilder parsed = (WildcardQueryBuilder) parseQuery(json);
|
||||
checkGeneratedJson(json, parsed);
|
||||
assertEquals(json, "ki*y", parsed.value());
|
||||
|
|
|
@ -132,11 +132,11 @@ public class ConstantKeywordFieldMapper extends ParametrizedFieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(String pattern, QueryShardContext context) {
|
||||
protected boolean matches(String pattern, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
return Regex.simpleMatch(pattern, value);
|
||||
return Regex.simpleMatch(pattern, value, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,9 +21,12 @@ public class ConstantKeywordFieldTypeTests extends FieldTypeTestCase {
|
|||
public void testTermQuery() {
|
||||
ConstantKeywordFieldType ft = new ConstantKeywordFieldType("f", "foo");
|
||||
assertEquals(new MatchAllDocsQuery(), ft.termQuery("foo", null));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.termQueryCaseInsensitive("fOo", null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.termQuery("bar", null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.termQueryCaseInsensitive("bAr", null));
|
||||
ConstantKeywordFieldType bar = new ConstantKeywordFieldType("f", "bar");
|
||||
assertEquals(new MatchNoDocsQuery(), bar.termQuery("foo", null));
|
||||
assertEquals(new MatchNoDocsQuery(), bar.termQueryCaseInsensitive("fOo", null));
|
||||
}
|
||||
|
||||
public void testTermsQuery() {
|
||||
|
@ -39,18 +42,24 @@ public class ConstantKeywordFieldTypeTests extends FieldTypeTestCase {
|
|||
|
||||
public void testWildcardQuery() {
|
||||
ConstantKeywordFieldType bar = new ConstantKeywordFieldType("f", "bar");
|
||||
assertEquals(new MatchNoDocsQuery(), bar.wildcardQuery("f*o", null, null));
|
||||
assertEquals(new MatchNoDocsQuery(), bar.wildcardQuery("f*o", null, false, null));
|
||||
assertEquals(new MatchNoDocsQuery(), bar.wildcardQuery("F*o", null, true, null));
|
||||
ConstantKeywordFieldType ft = new ConstantKeywordFieldType("f", "foo");
|
||||
assertEquals(new MatchAllDocsQuery(), ft.wildcardQuery("f*o", null, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.wildcardQuery("b*r", null, null));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.wildcardQuery("f*o", null, false, null));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.wildcardQuery("F*o", null, true, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.wildcardQuery("b*r", null, false, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.wildcardQuery("B*r", null, true, null));
|
||||
}
|
||||
|
||||
public void testPrefixQuery() {
|
||||
ConstantKeywordFieldType bar = new ConstantKeywordFieldType("f", "bar");
|
||||
assertEquals(new MatchNoDocsQuery(), bar.prefixQuery("fo", null, null));
|
||||
assertEquals(new MatchNoDocsQuery(), bar.prefixQuery("fo", null, false, null));
|
||||
assertEquals(new MatchNoDocsQuery(), bar.prefixQuery("fO", null, true, null));
|
||||
ConstantKeywordFieldType ft = new ConstantKeywordFieldType("f", "foo");
|
||||
assertEquals(new MatchAllDocsQuery(), ft.prefixQuery("fo", null, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.prefixQuery("ba", null, null));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.prefixQuery("fo", null, false, null));
|
||||
assertEquals(new MatchAllDocsQuery(), ft.prefixQuery("fO", null, true, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.prefixQuery("ba", null, false, null));
|
||||
assertEquals(new MatchNoDocsQuery(), ft.prefixQuery("Ba", null, true, null));
|
||||
}
|
||||
|
||||
public void testExistsQuery() {
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.apache.lucene.index.IndexOptions;
|
|||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.OrdinalMap;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.BoostQuery;
|
||||
import org.apache.lucene.search.DocValuesFieldExistsQuery;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.search.PrefixQuery;
|
||||
|
@ -20,6 +21,7 @@ import org.apache.lucene.search.SortField;
|
|||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.common.lucene.Lucene;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -292,11 +294,23 @@ public final class FlatObjectFieldMapper extends DynamicKeyFieldMapper {
|
|||
@Override
|
||||
public Query wildcardQuery(String value,
|
||||
MultiTermQuery.RewriteMethod method,
|
||||
boolean caseInsensitive,
|
||||
QueryShardContext context) {
|
||||
throw new UnsupportedOperationException("[wildcard] queries are not currently supported on keyed " +
|
||||
"[" + CONTENT_TYPE + "] fields.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
Query query = AutomatonQueries.caseInsensitiveTermQuery(new Term(name(), indexedValueForSearch(value)));
|
||||
if (boost() != 1f) {
|
||||
query = new BoostQuery(query, boost());
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytesRef indexedValueForSearch(Object value) {
|
||||
if (value == null) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.apache.lucene.search.TermQuery;
|
|||
import org.apache.lucene.search.TermRangeQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.index.mapper.FieldTypeTestCase;
|
||||
import org.elasticsearch.xpack.flattened.mapper.FlatObjectFieldMapper.KeyedFlatObjectFieldType;
|
||||
|
@ -48,6 +49,11 @@ public class KeyedFlatObjectFieldTypeTests extends FieldTypeTestCase {
|
|||
Query expected = new TermQuery(new Term("field", "key\0value"));
|
||||
assertEquals(expected, ft.termQuery("value", null));
|
||||
|
||||
expected = AutomatonQueries.caseInsensitiveTermQuery(new Term("field", "key\0value"));
|
||||
assertEquals(expected, ft.termQueryCaseInsensitive("value", null));
|
||||
|
||||
|
||||
|
||||
KeyedFlatObjectFieldType unsearchable = new KeyedFlatObjectFieldType("field", false, true, "key",
|
||||
false, Collections.emptyMap());
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
|
@ -81,10 +87,14 @@ public class KeyedFlatObjectFieldTypeTests extends FieldTypeTestCase {
|
|||
KeyedFlatObjectFieldType ft = createFieldType();
|
||||
|
||||
Query expected = new PrefixQuery(new Term("field", "key\0val"));
|
||||
assertEquals(expected, ft.prefixQuery("val", MultiTermQuery.CONSTANT_SCORE_REWRITE, MOCK_QSC));
|
||||
assertEquals(expected, ft.prefixQuery("val", MultiTermQuery.CONSTANT_SCORE_REWRITE, false, MOCK_QSC));
|
||||
|
||||
expected = AutomatonQueries.caseInsensitivePrefixQuery(new Term("field", "key\0vAl"));
|
||||
assertEquals(expected, ft.prefixQuery("vAl", MultiTermQuery.CONSTANT_SCORE_REWRITE, true, MOCK_QSC));
|
||||
|
||||
|
||||
ElasticsearchException ee = expectThrows(ElasticsearchException.class,
|
||||
() -> ft.prefixQuery("val", MultiTermQuery.CONSTANT_SCORE_REWRITE, MOCK_QSC_DISALLOW_EXPENSIVE));
|
||||
() -> ft.prefixQuery("val", MultiTermQuery.CONSTANT_SCORE_REWRITE, false, MOCK_QSC_DISALLOW_EXPENSIVE));
|
||||
assertEquals("[prefix] queries cannot be executed when 'search.allow_expensive_queries' is set to false. " +
|
||||
"For optimised prefix queries on text fields please enable [index_prefixes].", ee.getMessage());
|
||||
}
|
||||
|
@ -138,7 +148,7 @@ public class KeyedFlatObjectFieldTypeTests extends FieldTypeTestCase {
|
|||
KeyedFlatObjectFieldType ft = createFieldType();
|
||||
|
||||
UnsupportedOperationException e = expectThrows(UnsupportedOperationException.class,
|
||||
() -> ft.wildcardQuery("valu*", null, randomMockShardContext()));
|
||||
() -> ft.wildcardQuery("valu*", null, false, randomMockShardContext()));
|
||||
assertEquals("[wildcard] queries are not currently supported on keyed [flattened] fields.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.apache.lucene.search.TermRangeQuery;
|
|||
import org.apache.lucene.search.WildcardQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
|
||||
import org.elasticsearch.index.mapper.FieldTypeTestCase;
|
||||
|
@ -43,6 +44,10 @@ public class RootFlatObjectFieldTypeTests extends FieldTypeTestCase {
|
|||
Query expected = new TermQuery(new Term("field", "value"));
|
||||
assertEquals(expected, ft.termQuery("value", null));
|
||||
|
||||
expected = AutomatonQueries.caseInsensitiveTermQuery(new Term("field", "Value"));
|
||||
assertEquals(expected, ft.termQueryCaseInsensitive("Value", null));
|
||||
|
||||
|
||||
RootFlatObjectFieldType unsearchable = new RootFlatObjectFieldType("field", false, true,
|
||||
Collections.emptyMap(), false);
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
|
|
|
@ -133,12 +133,12 @@ abstract class AbstractScriptMappedFieldType<LeafFactory> extends MappedFieldTyp
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
throw new IllegalArgumentException(unsupported("prefix", "keyword, text and wildcard"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
throw new IllegalArgumentException(unsupported("wildcard", "keyword, text and wildcard"));
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.lucene.search.MatchNoDocsQuery;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.index.mapper.BooleanFieldMapper;
|
||||
|
@ -138,10 +139,16 @@ public class BooleanScriptMappedFieldType extends AbstractScriptMappedFieldType<
|
|||
return termsQuery(trueAllowed, falseAllowed, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new BooleanScriptFieldTermQuery(script, leafFactory(context.lookup()), name(), toBoolean(value, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQuery(Object value, QueryShardContext context) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new BooleanScriptFieldTermQuery(script, leafFactory(context), name(), toBoolean(value));
|
||||
return new BooleanScriptFieldTermQuery(script, leafFactory(context), name(), toBoolean(value, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -152,7 +159,7 @@ public class BooleanScriptMappedFieldType extends AbstractScriptMappedFieldType<
|
|||
boolean trueAllowed = false;
|
||||
boolean falseAllowed = false;
|
||||
for (Object value : values) {
|
||||
if (toBoolean(value)) {
|
||||
if (toBoolean(value, false)) {
|
||||
trueAllowed = true;
|
||||
} else {
|
||||
falseAllowed = true;
|
||||
|
@ -177,10 +184,14 @@ public class BooleanScriptMappedFieldType extends AbstractScriptMappedFieldType<
|
|||
return new MatchNoDocsQuery("neither true nor false allowed");
|
||||
}
|
||||
|
||||
private static boolean toBoolean(Object value) {
|
||||
return toBoolean(value, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the term into a boolean. Inspired by {@link BooleanFieldMapper.BooleanFieldType#indexedValueForSearch(Object)}.
|
||||
*/
|
||||
private static boolean toBoolean(Object value) {
|
||||
private static boolean toBoolean(Object value, boolean caseInsensitive) {
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -193,6 +204,9 @@ public class BooleanScriptMappedFieldType extends AbstractScriptMappedFieldType<
|
|||
} else {
|
||||
sValue = value.toString();
|
||||
}
|
||||
if (caseInsensitive) {
|
||||
sValue = Strings.toLowercaseAscii(sValue);
|
||||
}
|
||||
return Booleans.parseBoolean(sValue);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,9 +88,14 @@ public final class KeywordScriptMappedFieldType extends AbstractScriptMappedFiel
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, RewriteMethod method, org.elasticsearch.index.query.QueryShardContext context) {
|
||||
public Query prefixQuery(
|
||||
String value,
|
||||
RewriteMethod method,
|
||||
boolean caseInsensitive,
|
||||
org.elasticsearch.index.query.QueryShardContext context
|
||||
) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new StringScriptFieldPrefixQuery(script, leafFactory(context), name(), value);
|
||||
return new StringScriptFieldPrefixQuery(script, leafFactory(context), name(), value, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -128,13 +133,39 @@ public final class KeywordScriptMappedFieldType extends AbstractScriptMappedFiel
|
|||
if (matchFlags != 0) {
|
||||
throw new IllegalArgumentException("Match flags not yet implemented [" + matchFlags + "]");
|
||||
}
|
||||
return new StringScriptFieldRegexpQuery(script, leafFactory(context), name(), value, syntaxFlags, maxDeterminizedStates);
|
||||
return new StringScriptFieldRegexpQuery(
|
||||
script,
|
||||
leafFactory(context),
|
||||
name(),
|
||||
value,
|
||||
syntaxFlags,
|
||||
matchFlags,
|
||||
maxDeterminizedStates
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new StringScriptFieldTermQuery(
|
||||
script,
|
||||
leafFactory(context),
|
||||
name(),
|
||||
BytesRefs.toString(Objects.requireNonNull(value)),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQuery(Object value, QueryShardContext context) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new StringScriptFieldTermQuery(script, leafFactory(context), name(), BytesRefs.toString(Objects.requireNonNull(value)));
|
||||
return new StringScriptFieldTermQuery(
|
||||
script,
|
||||
leafFactory(context),
|
||||
name(),
|
||||
BytesRefs.toString(Objects.requireNonNull(value)),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,8 +176,8 @@ public final class KeywordScriptMappedFieldType extends AbstractScriptMappedFiel
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value, RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String value, RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
checkAllowExpensiveQueries(context);
|
||||
return new StringScriptFieldWildcardQuery(script, leafFactory(context), name(), value);
|
||||
return new StringScriptFieldWildcardQuery(script, leafFactory(context), name(), value, caseInsensitive);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ package org.elasticsearch.xpack.runtimefields.query;
|
|||
import org.apache.lucene.search.PrefixQuery;
|
||||
import org.apache.lucene.search.QueryVisitor;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.ByteRunAutomaton;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.xpack.runtimefields.mapper.StringFieldScript;
|
||||
|
||||
|
@ -18,26 +20,63 @@ import java.util.Objects;
|
|||
|
||||
public class StringScriptFieldPrefixQuery extends AbstractStringScriptFieldQuery {
|
||||
private final String prefix;
|
||||
private final boolean caseInsensitive;
|
||||
|
||||
public StringScriptFieldPrefixQuery(Script script, StringFieldScript.LeafFactory leafFactory, String fieldName, String prefix) {
|
||||
public StringScriptFieldPrefixQuery(
|
||||
Script script,
|
||||
StringFieldScript.LeafFactory leafFactory,
|
||||
String fieldName,
|
||||
String prefix,
|
||||
boolean caseInsensitive
|
||||
) {
|
||||
super(script, leafFactory, fieldName);
|
||||
this.prefix = Objects.requireNonNull(prefix);
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(List<String> values) {
|
||||
for (String value : values) {
|
||||
if (value != null && value.startsWith(prefix)) {
|
||||
if (startsWith(value, prefix, caseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Check if a String starts with a specified prefix (optionally case insensitive).</p>
|
||||
*
|
||||
* @see java.lang.String#startsWith(String)
|
||||
* @param str the String to check, may be null
|
||||
* @param prefix the prefix to find, may be null
|
||||
* @param ignoreCase inidicates whether the compare should ignore case
|
||||
* (case insensitive) or not.
|
||||
* @return <code>true</code> if the String starts with the prefix or
|
||||
* both <code>null</code>
|
||||
*/
|
||||
private static boolean startsWith(String str, String prefix, boolean ignoreCase) {
|
||||
if (str == null || prefix == null) {
|
||||
return (str == null && prefix == null);
|
||||
}
|
||||
if (prefix.length() > str.length()) {
|
||||
return false;
|
||||
}
|
||||
return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(QueryVisitor visitor) {
|
||||
if (visitor.acceptField(fieldName())) {
|
||||
visitor.consumeTermsMatching(this, fieldName(), () -> new ByteRunAutomaton(PrefixQuery.toAutomaton(new BytesRef(prefix))));
|
||||
visitor.consumeTermsMatching(this, fieldName(), () -> new ByteRunAutomaton(buildAutomaton(new BytesRef(prefix))));
|
||||
}
|
||||
}
|
||||
|
||||
Automaton buildAutomaton(BytesRef prefix) {
|
||||
if (caseInsensitive) {
|
||||
return AutomatonQueries.caseInsensitivePrefix(prefix.utf8ToString());
|
||||
} else {
|
||||
return PrefixQuery.toAutomaton(prefix);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +90,7 @@ public class StringScriptFieldPrefixQuery extends AbstractStringScriptFieldQuery
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), prefix);
|
||||
return Objects.hash(super.hashCode(), prefix, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,10 +99,14 @@ public class StringScriptFieldPrefixQuery extends AbstractStringScriptFieldQuery
|
|||
return false;
|
||||
}
|
||||
StringScriptFieldPrefixQuery other = (StringScriptFieldPrefixQuery) obj;
|
||||
return prefix.equals(other.prefix);
|
||||
return prefix.equals(other.prefix) && caseInsensitive == other.caseInsensitive;
|
||||
}
|
||||
|
||||
String prefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
boolean caseInsensitive() {
|
||||
return caseInsensitive;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,24 +15,27 @@ import java.util.Objects;
|
|||
|
||||
public class StringScriptFieldRegexpQuery extends AbstractStringScriptFieldAutomatonQuery {
|
||||
private final String pattern;
|
||||
private final int flags;
|
||||
private final int syntaxFlags;
|
||||
private final int matchFlags;
|
||||
|
||||
public StringScriptFieldRegexpQuery(
|
||||
Script script,
|
||||
StringFieldScript.LeafFactory leafFactory,
|
||||
String fieldName,
|
||||
String pattern,
|
||||
int flags,
|
||||
int syntaxFlags,
|
||||
int matchFlags,
|
||||
int maxDeterminizedStates
|
||||
) {
|
||||
super(
|
||||
script,
|
||||
leafFactory,
|
||||
fieldName,
|
||||
new ByteRunAutomaton(new RegExp(Objects.requireNonNull(pattern), flags).toAutomaton(maxDeterminizedStates))
|
||||
new ByteRunAutomaton(new RegExp(Objects.requireNonNull(pattern), syntaxFlags, matchFlags).toAutomaton(maxDeterminizedStates))
|
||||
);
|
||||
this.pattern = pattern;
|
||||
this.flags = flags;
|
||||
this.syntaxFlags = syntaxFlags;
|
||||
this.matchFlags = matchFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,7 +49,7 @@ public class StringScriptFieldRegexpQuery extends AbstractStringScriptFieldAutom
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), pattern, flags);
|
||||
return Objects.hash(super.hashCode(), pattern, syntaxFlags, matchFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,14 +58,18 @@ public class StringScriptFieldRegexpQuery extends AbstractStringScriptFieldAutom
|
|||
return false;
|
||||
}
|
||||
StringScriptFieldRegexpQuery other = (StringScriptFieldRegexpQuery) obj;
|
||||
return pattern.equals(other.pattern) && flags == other.flags;
|
||||
return pattern.equals(other.pattern) && syntaxFlags == other.syntaxFlags && matchFlags == other.matchFlags;
|
||||
}
|
||||
|
||||
String pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
int flags() {
|
||||
return flags;
|
||||
int syntaxFlags() {
|
||||
return syntaxFlags;
|
||||
}
|
||||
|
||||
int matchFlags() {
|
||||
return matchFlags;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,16 +16,28 @@ import java.util.Objects;
|
|||
|
||||
public class StringScriptFieldTermQuery extends AbstractStringScriptFieldQuery {
|
||||
private final String term;
|
||||
private final boolean caseInsensitive;
|
||||
|
||||
public StringScriptFieldTermQuery(Script script, StringFieldScript.LeafFactory leafFactory, String fieldName, String term) {
|
||||
public StringScriptFieldTermQuery(
|
||||
Script script,
|
||||
StringFieldScript.LeafFactory leafFactory,
|
||||
String fieldName,
|
||||
String term,
|
||||
boolean caseInsensitive
|
||||
) {
|
||||
super(script, leafFactory, fieldName);
|
||||
this.term = Objects.requireNonNull(term);
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(List<String> values) {
|
||||
for (String value : values) {
|
||||
if (term.equals(value)) {
|
||||
if (caseInsensitive) {
|
||||
if (term.equalsIgnoreCase(value)) {
|
||||
return true;
|
||||
}
|
||||
} else if (term.equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +59,7 @@ public class StringScriptFieldTermQuery extends AbstractStringScriptFieldQuery {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), term);
|
||||
return Objects.hash(super.hashCode(), term, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -56,10 +68,14 @@ public class StringScriptFieldTermQuery extends AbstractStringScriptFieldQuery {
|
|||
return false;
|
||||
}
|
||||
StringScriptFieldTermQuery other = (StringScriptFieldTermQuery) obj;
|
||||
return term.equals(other.term);
|
||||
return term.equals(other.term) && caseInsensitive == other.caseInsensitive;
|
||||
}
|
||||
|
||||
String term() {
|
||||
return term;
|
||||
}
|
||||
|
||||
boolean caseInsensitive() {
|
||||
return caseInsensitive;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ package org.elasticsearch.xpack.runtimefields.query;
|
|||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.WildcardQuery;
|
||||
import org.apache.lucene.util.automaton.Automaton;
|
||||
import org.apache.lucene.util.automaton.ByteRunAutomaton;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.xpack.runtimefields.mapper.StringFieldScript;
|
||||
|
||||
|
@ -16,15 +18,30 @@ import java.util.Objects;
|
|||
|
||||
public class StringScriptFieldWildcardQuery extends AbstractStringScriptFieldAutomatonQuery {
|
||||
private final String pattern;
|
||||
private final boolean caseInsensitive;
|
||||
|
||||
public StringScriptFieldWildcardQuery(Script script, StringFieldScript.LeafFactory leafFactory, String fieldName, String pattern) {
|
||||
public StringScriptFieldWildcardQuery(
|
||||
Script script,
|
||||
StringFieldScript.LeafFactory leafFactory,
|
||||
String fieldName,
|
||||
String pattern,
|
||||
boolean caseInsensitive
|
||||
) {
|
||||
super(
|
||||
script,
|
||||
leafFactory,
|
||||
fieldName,
|
||||
new ByteRunAutomaton(WildcardQuery.toAutomaton(new Term(fieldName, Objects.requireNonNull(pattern))))
|
||||
new ByteRunAutomaton(buildAutomaton(new Term(fieldName, Objects.requireNonNull(pattern)), caseInsensitive))
|
||||
);
|
||||
this.pattern = pattern;
|
||||
this.caseInsensitive = caseInsensitive;
|
||||
}
|
||||
|
||||
private static Automaton buildAutomaton(Term term, boolean caseInsensitive) {
|
||||
if (caseInsensitive) {
|
||||
return AutomatonQueries.toCaseInsensitiveWildcardAutomaton(term, Integer.MAX_VALUE);
|
||||
}
|
||||
return WildcardQuery.toAutomaton(term);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,7 +54,7 @@ public class StringScriptFieldWildcardQuery extends AbstractStringScriptFieldAut
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), pattern);
|
||||
return Objects.hash(super.hashCode(), pattern, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -46,10 +63,14 @@ public class StringScriptFieldWildcardQuery extends AbstractStringScriptFieldAut
|
|||
return false;
|
||||
}
|
||||
StringScriptFieldWildcardQuery other = (StringScriptFieldWildcardQuery) obj;
|
||||
return pattern.equals(other.pattern);
|
||||
return pattern.equals(other.pattern) && caseInsensitive == other.caseInsensitive;
|
||||
}
|
||||
|
||||
String pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
boolean caseInsensitive() {
|
||||
return caseInsensitive;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -272,7 +272,7 @@ public class KeywordScriptMappedFieldTypeTests extends AbstractScriptMappedField
|
|||
}
|
||||
|
||||
private Query randomRegexpQuery(MappedFieldType ft, QueryShardContext ctx) {
|
||||
return ft.regexpQuery(randomAlphaOfLengthBetween(1, 1000), randomInt(0xFFFF), 0, Integer.MAX_VALUE, null, ctx);
|
||||
return ft.regexpQuery(randomAlphaOfLengthBetween(1, 1000), randomInt(0xFF), 0, Integer.MAX_VALUE, null, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,12 +16,18 @@ import static org.hamcrest.Matchers.is;
|
|||
public class StringScriptFieldPrefixQueryTests extends AbstractStringScriptFieldQueryTestCase<StringScriptFieldPrefixQuery> {
|
||||
@Override
|
||||
protected StringScriptFieldPrefixQuery createTestInstance() {
|
||||
return new StringScriptFieldPrefixQuery(randomScript(), leafFactory, randomAlphaOfLength(5), randomAlphaOfLength(6));
|
||||
return new StringScriptFieldPrefixQuery(
|
||||
randomScript(),
|
||||
leafFactory,
|
||||
randomAlphaOfLength(5),
|
||||
randomAlphaOfLength(6),
|
||||
randomBoolean()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringScriptFieldPrefixQuery copy(StringScriptFieldPrefixQuery orig) {
|
||||
return new StringScriptFieldPrefixQuery(orig.script(), leafFactory, orig.fieldName(), orig.prefix());
|
||||
return new StringScriptFieldPrefixQuery(orig.script(), leafFactory, orig.fieldName(), orig.prefix(), orig.caseInsensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,6 +35,7 @@ public class StringScriptFieldPrefixQueryTests extends AbstractStringScriptField
|
|||
Script script = orig.script();
|
||||
String fieldName = orig.fieldName();
|
||||
String prefix = orig.prefix();
|
||||
boolean caseInsensitive = orig.caseInsensitive();
|
||||
switch (randomInt(2)) {
|
||||
case 0:
|
||||
script = randomValueOtherThan(script, this::randomScript);
|
||||
|
@ -39,19 +46,30 @@ public class StringScriptFieldPrefixQueryTests extends AbstractStringScriptField
|
|||
case 2:
|
||||
prefix += "modified";
|
||||
break;
|
||||
case 3:
|
||||
caseInsensitive = !caseInsensitive;
|
||||
break;
|
||||
default:
|
||||
fail();
|
||||
}
|
||||
return new StringScriptFieldPrefixQuery(script, leafFactory, fieldName, prefix);
|
||||
return new StringScriptFieldPrefixQuery(script, leafFactory, fieldName, prefix, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testMatches() {
|
||||
StringScriptFieldPrefixQuery query = new StringScriptFieldPrefixQuery(randomScript(), leafFactory, "test", "foo");
|
||||
StringScriptFieldPrefixQuery query = new StringScriptFieldPrefixQuery(randomScript(), leafFactory, "test", "foo", false);
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("foo")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("Foo")));
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("foooo")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("Foooo")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("fo")));
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("fo", "foo")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("Fo", "fOo")));
|
||||
|
||||
StringScriptFieldPrefixQuery ciQuery = new StringScriptFieldPrefixQuery(randomScript(), leafFactory, "test", "foo", true);
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("fOo")));
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("Foooo")));
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("fo", "foO")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,12 +18,14 @@ import static org.hamcrest.Matchers.is;
|
|||
public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptFieldQueryTestCase<StringScriptFieldRegexpQuery> {
|
||||
@Override
|
||||
protected StringScriptFieldRegexpQuery createTestInstance() {
|
||||
int matchFlags = randomBoolean() ? 0 : RegExp.ASCII_CASE_INSENSITIVE;
|
||||
return new StringScriptFieldRegexpQuery(
|
||||
randomScript(),
|
||||
leafFactory,
|
||||
randomAlphaOfLength(5),
|
||||
randomAlphaOfLength(6),
|
||||
randomInt(RegExp.ALL),
|
||||
matchFlags,
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
}
|
||||
|
@ -35,7 +37,8 @@ public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptField
|
|||
leafFactory,
|
||||
orig.fieldName(),
|
||||
orig.pattern(),
|
||||
orig.flags(),
|
||||
orig.syntaxFlags(),
|
||||
orig.matchFlags(),
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
}
|
||||
|
@ -45,8 +48,9 @@ public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptField
|
|||
Script script = orig.script();
|
||||
String fieldName = orig.fieldName();
|
||||
String pattern = orig.pattern();
|
||||
int flags = orig.flags();
|
||||
switch (randomInt(3)) {
|
||||
int syntaxFlags = orig.syntaxFlags();
|
||||
int matchFlags = orig.matchFlags();
|
||||
switch (randomInt(4)) {
|
||||
case 0:
|
||||
script = randomValueOtherThan(script, this::randomScript);
|
||||
break;
|
||||
|
@ -57,12 +61,23 @@ public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptField
|
|||
pattern += "modified";
|
||||
break;
|
||||
case 3:
|
||||
flags = randomValueOtherThan(flags, () -> randomInt(RegExp.ALL));
|
||||
syntaxFlags = randomValueOtherThan(syntaxFlags, () -> randomInt(RegExp.ALL));
|
||||
break;
|
||||
case 4:
|
||||
matchFlags = (matchFlags & RegExp.ASCII_CASE_INSENSITIVE) != 0 ? 0 : RegExp.ASCII_CASE_INSENSITIVE;
|
||||
break;
|
||||
default:
|
||||
fail();
|
||||
}
|
||||
return new StringScriptFieldRegexpQuery(script, leafFactory, fieldName, pattern, flags, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
|
||||
return new StringScriptFieldRegexpQuery(
|
||||
script,
|
||||
leafFactory,
|
||||
fieldName,
|
||||
pattern,
|
||||
syntaxFlags,
|
||||
matchFlags,
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,14 +88,28 @@ public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptField
|
|||
"test",
|
||||
"a.+b",
|
||||
0,
|
||||
0,
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("astuffb")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("astuffB")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("fffff")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("ab")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("aasdf")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("dsfb")));
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("astuffb", "fffff")));
|
||||
|
||||
StringScriptFieldRegexpQuery ciQuery = new StringScriptFieldRegexpQuery(
|
||||
randomScript(),
|
||||
leafFactory,
|
||||
"test",
|
||||
"a.+b",
|
||||
0,
|
||||
RegExp.ASCII_CASE_INSENSITIVE,
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("astuffB")));
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("Astuffb", "fffff")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +125,7 @@ public class StringScriptFieldRegexpQueryTests extends AbstractStringScriptField
|
|||
"test",
|
||||
"a.+b",
|
||||
0,
|
||||
0,
|
||||
Operations.DEFAULT_MAX_DETERMINIZED_STATES
|
||||
);
|
||||
ByteRunAutomaton automaton = visitForSingleAutomata(query);
|
||||
|
|
|
@ -22,12 +22,12 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
public class StringScriptFieldTermQueryTests extends AbstractStringScriptFieldQueryTestCase<StringScriptFieldTermQuery> {
|
||||
@Override
|
||||
protected StringScriptFieldTermQuery createTestInstance() {
|
||||
return new StringScriptFieldTermQuery(randomScript(), leafFactory, randomAlphaOfLength(5), randomAlphaOfLength(6));
|
||||
return new StringScriptFieldTermQuery(randomScript(), leafFactory, randomAlphaOfLength(5), randomAlphaOfLength(6), randomBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringScriptFieldTermQuery copy(StringScriptFieldTermQuery orig) {
|
||||
return new StringScriptFieldTermQuery(orig.script(), leafFactory, orig.fieldName(), orig.term());
|
||||
return new StringScriptFieldTermQuery(orig.script(), leafFactory, orig.fieldName(), orig.term(), orig.caseInsensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,7 +35,8 @@ public class StringScriptFieldTermQueryTests extends AbstractStringScriptFieldQu
|
|||
Script script = orig.script();
|
||||
String fieldName = orig.fieldName();
|
||||
String term = orig.term();
|
||||
switch (randomInt(2)) {
|
||||
boolean caseInsensitive = orig.caseInsensitive();
|
||||
switch (randomInt(3)) {
|
||||
case 0:
|
||||
script = randomValueOtherThan(script, this::randomScript);
|
||||
break;
|
||||
|
@ -45,18 +46,26 @@ public class StringScriptFieldTermQueryTests extends AbstractStringScriptFieldQu
|
|||
case 2:
|
||||
term += "modified";
|
||||
break;
|
||||
case 3:
|
||||
caseInsensitive = !caseInsensitive;
|
||||
break;
|
||||
default:
|
||||
fail();
|
||||
}
|
||||
return new StringScriptFieldTermQuery(script, leafFactory, fieldName, term);
|
||||
return new StringScriptFieldTermQuery(script, leafFactory, fieldName, term, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testMatches() {
|
||||
StringScriptFieldTermQuery query = new StringScriptFieldTermQuery(randomScript(), leafFactory, "test", "foo");
|
||||
StringScriptFieldTermQuery query = new StringScriptFieldTermQuery(randomScript(), leafFactory, "test", "foo", false);
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("foo")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("foO")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("bar")));
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("foo", "bar")));
|
||||
|
||||
StringScriptFieldTermQuery ciQuery = new StringScriptFieldTermQuery(randomScript(), leafFactory, "test", "foo", true);
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("Foo")));
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("fOo", "bar")));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,12 +15,18 @@ import static org.hamcrest.Matchers.equalTo;
|
|||
public class StringScriptFieldWildcardQueryTests extends AbstractStringScriptFieldQueryTestCase<StringScriptFieldWildcardQuery> {
|
||||
@Override
|
||||
protected StringScriptFieldWildcardQuery createTestInstance() {
|
||||
return new StringScriptFieldWildcardQuery(randomScript(), leafFactory, randomAlphaOfLength(5), randomAlphaOfLength(6));
|
||||
return new StringScriptFieldWildcardQuery(
|
||||
randomScript(),
|
||||
leafFactory,
|
||||
randomAlphaOfLength(5),
|
||||
randomAlphaOfLength(6),
|
||||
randomBoolean()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringScriptFieldWildcardQuery copy(StringScriptFieldWildcardQuery orig) {
|
||||
return new StringScriptFieldWildcardQuery(orig.script(), leafFactory, orig.fieldName(), orig.pattern());
|
||||
return new StringScriptFieldWildcardQuery(orig.script(), leafFactory, orig.fieldName(), orig.pattern(), orig.caseInsensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,7 +34,8 @@ public class StringScriptFieldWildcardQueryTests extends AbstractStringScriptFie
|
|||
Script script = orig.script();
|
||||
String fieldName = orig.fieldName();
|
||||
String pattern = orig.pattern();
|
||||
switch (randomInt(2)) {
|
||||
boolean caseInsensitive = orig.caseInsensitive();
|
||||
switch (randomInt(3)) {
|
||||
case 0:
|
||||
script = randomValueOtherThan(script, this::randomScript);
|
||||
break;
|
||||
|
@ -38,22 +45,31 @@ public class StringScriptFieldWildcardQueryTests extends AbstractStringScriptFie
|
|||
case 2:
|
||||
pattern += "modified";
|
||||
break;
|
||||
case 3:
|
||||
caseInsensitive = !caseInsensitive;
|
||||
break;
|
||||
default:
|
||||
fail();
|
||||
}
|
||||
return new StringScriptFieldWildcardQuery(script, leafFactory, fieldName, pattern);
|
||||
return new StringScriptFieldWildcardQuery(script, leafFactory, fieldName, pattern, caseInsensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testMatches() {
|
||||
StringScriptFieldWildcardQuery query = new StringScriptFieldWildcardQuery(randomScript(), leafFactory, "test", "a*b");
|
||||
StringScriptFieldWildcardQuery query = new StringScriptFieldWildcardQuery(randomScript(), leafFactory, "test", "a*b", false);
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("astuffb")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("Astuffb")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("fffff")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("a")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("b")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("aasdf")));
|
||||
assertFalse(query.matches(org.elasticsearch.common.collect.List.of("dsfb")));
|
||||
assertTrue(query.matches(org.elasticsearch.common.collect.List.of("astuffb", "fffff")));
|
||||
|
||||
StringScriptFieldWildcardQuery ciQuery = new StringScriptFieldWildcardQuery(randomScript(), leafFactory, "test", "a*b", true);
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("Astuffb")));
|
||||
assertTrue(ciQuery.matches(org.elasticsearch.common.collect.List.of("astuffB", "fffff")));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +79,7 @@ public class StringScriptFieldWildcardQueryTests extends AbstractStringScriptFie
|
|||
|
||||
@Override
|
||||
public void testVisit() {
|
||||
StringScriptFieldWildcardQuery query = new StringScriptFieldWildcardQuery(randomScript(), leafFactory, "test", "a*b");
|
||||
StringScriptFieldWildcardQuery query = new StringScriptFieldWildcardQuery(randomScript(), leafFactory, "test", "a*b", false);
|
||||
ByteRunAutomaton automaton = visitForSingleAutomata(query);
|
||||
BytesRef term = new BytesRef("astuffb");
|
||||
assertTrue(automaton.run(term.bytes, term.offset, term.length));
|
||||
|
|
|
@ -138,8 +138,8 @@ public class VersionStringFieldMapper extends ParametrizedFieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
return wildcardQuery(value + "*", method, context);
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
return wildcardQuery(value + "*", method, caseInsensitive, context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -237,7 +237,7 @@ public class VersionStringFieldMapper extends ParametrizedFieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
if (context.allowExpensiveQueries() == false) {
|
||||
throw new ElasticsearchException(
|
||||
"[wildcard] queries cannot be executed when '" + ALLOW_EXPENSIVE_QUERIES.getKey() + "' is set to false."
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.elasticsearch.ElasticsearchParseException;
|
|||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.lucene.BytesRefs;
|
||||
import org.elasticsearch.common.lucene.Lucene;
|
||||
import org.elasticsearch.common.lucene.search.AutomatonQueries;
|
||||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.unit.Fuzziness;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -218,7 +219,7 @@ public class WildcardFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query wildcardQuery(String wildcardPattern, RewriteMethod method, QueryShardContext context) {
|
||||
public Query wildcardQuery(String wildcardPattern, RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
|
||||
String ngramIndexPattern = addLineEndChars(toLowerCase(wildcardPattern));
|
||||
|
||||
|
@ -276,7 +277,11 @@ public class WildcardFieldMapper extends FieldMapper {
|
|||
clauseCount++;
|
||||
}
|
||||
Supplier<Automaton> deferredAutomatonSupplier = () -> {
|
||||
return WildcardQuery.toAutomaton(new Term(name(), wildcardPattern));
|
||||
if(caseInsensitive) {
|
||||
return AutomatonQueries.toCaseInsensitiveWildcardAutomaton(new Term(name(), wildcardPattern), Integer.MAX_VALUE);
|
||||
} else {
|
||||
return WildcardQuery.toAutomaton(new Term(name(), wildcardPattern));
|
||||
}
|
||||
};
|
||||
AutomatonQueryOnBinaryDv verifyingQuery = new AutomatonQueryOnBinaryDv(name(), wildcardPattern, deferredAutomatonSupplier);
|
||||
if (clauseCount > 0) {
|
||||
|
@ -845,7 +850,7 @@ public class WildcardFieldMapper extends FieldMapper {
|
|||
@Override
|
||||
public Query termQuery(Object value, QueryShardContext context) {
|
||||
String searchTerm = BytesRefs.toString(value);
|
||||
return wildcardQuery(escapeWildcardSyntax(searchTerm), MultiTermQuery.CONSTANT_SCORE_REWRITE, context);
|
||||
return wildcardQuery(escapeWildcardSyntax(searchTerm), MultiTermQuery.CONSTANT_SCORE_REWRITE, false, context);
|
||||
}
|
||||
|
||||
private String escapeWildcardSyntax(String term) {
|
||||
|
@ -862,10 +867,16 @@ public class WildcardFieldMapper extends FieldMapper {
|
|||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query termQueryCaseInsensitive(Object value, QueryShardContext context) {
|
||||
String searchTerm = BytesRefs.toString(value);
|
||||
return wildcardQuery(escapeWildcardSyntax(searchTerm), MultiTermQuery.CONSTANT_SCORE_REWRITE, true, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, QueryShardContext context) {
|
||||
return wildcardQuery(escapeWildcardSyntax(value) + "*", method, context);
|
||||
public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, QueryShardContext context) {
|
||||
return wildcardQuery(escapeWildcardSyntax(value) + "*", method, caseInsensitive, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -876,7 +887,7 @@ public class WildcardFieldMapper extends FieldMapper {
|
|||
}
|
||||
return new ConstantScoreQuery(bq.build());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
|
||||
failIfNoDocValues();
|
||||
|
|
|
@ -263,18 +263,21 @@ public class WildcardFieldMapperTests extends ESTestCase {
|
|||
switch (randomInt(4)) {
|
||||
case 0:
|
||||
pattern = getRandomWildcardPattern();
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().wildcardQuery(pattern, null, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().wildcardQuery(pattern, null, MOCK_QSC);
|
||||
boolean caseInsensitive = randomBoolean();
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().wildcardQuery(pattern, null, caseInsensitive, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().wildcardQuery(pattern, null, caseInsensitive, MOCK_QSC);
|
||||
break;
|
||||
case 1:
|
||||
pattern = getRandomRegexPattern(values);
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().regexpQuery(pattern, RegExp.ALL, 0, 20000, null, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().regexpQuery(pattern, RegExp.ALL, 0,20000, null, MOCK_QSC);
|
||||
int matchFlags = randomBoolean()? 0 : RegExp.ASCII_CASE_INSENSITIVE;
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().regexpQuery(pattern, RegExp.ALL, matchFlags, 20000, null, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().regexpQuery(pattern, RegExp.ALL, matchFlags,20000, null, MOCK_QSC);
|
||||
break;
|
||||
case 2:
|
||||
pattern = randomABString(5);
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().prefixQuery(pattern, null, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().prefixQuery(pattern, null, MOCK_QSC);
|
||||
boolean caseInsensitivePrefix = randomBoolean();
|
||||
wildcardFieldQuery = wildcardFieldType.fieldType().prefixQuery(pattern, null, caseInsensitivePrefix, MOCK_QSC);
|
||||
keywordFieldQuery = keywordFieldType.fieldType().prefixQuery(pattern, null, caseInsensitivePrefix, MOCK_QSC);
|
||||
break;
|
||||
case 3:
|
||||
int edits = randomInt(2);
|
||||
|
|
Loading…
Reference in New Issue