refactor SimpleQueryParser settings into separate class, add "lenient" option

Fixes #5011
This commit is contained in:
Lee Hinman 2014-02-18 15:46:25 -07:00
parent 90e57c15e8
commit 5429019920
4 changed files with 119 additions and 16 deletions

View File

@ -38,14 +38,31 @@ import static org.elasticsearch.index.query.support.QueryParsers.wrapSmartNameQu
*/
public class SimpleQueryParser extends XSimpleQueryParser {
private final boolean lowercaseExpandedTerms;
private final Locale locale;
private final Settings settings;
/** Creates a new parser with custom flags used to enable/disable certain features. */
public SimpleQueryParser(Analyzer analyzer, Map<String, Float> weights, int flags, Locale locale, boolean lowercaseExpandedTerms) {
public SimpleQueryParser(Analyzer analyzer, Map<String, Float> weights, int flags, Settings settings) {
super(analyzer, weights, flags);
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
this.locale = locale;
this.settings = settings;
}
/**
* Rethrow the runtime exception, unless the lenient flag has been set, returns null
*/
private Query rethrowUnlessLenient(RuntimeException e) {
if (settings.lenient()) {
return null;
}
throw e;
}
@Override
public Query newDefaultQuery(String text) {
try {
return super.newDefaultQuery(text);
} catch (RuntimeException e) {
return rethrowUnlessLenient(e);
}
}
/**
@ -54,10 +71,23 @@ public class SimpleQueryParser extends XSimpleQueryParser {
*/
@Override
public Query newFuzzyQuery(String text, int fuzziness) {
if (lowercaseExpandedTerms) {
text = text.toLowerCase(locale);
if (settings.lowercaseExpandedTerms()) {
text = text.toLowerCase(settings.locale());
}
try {
return super.newFuzzyQuery(text, fuzziness);
} catch (RuntimeException e) {
return rethrowUnlessLenient(e);
}
}
@Override
public Query newPhraseQuery(String text, int slop) {
try {
return super.newPhraseQuery(text, slop);
} catch (RuntimeException e) {
return rethrowUnlessLenient(e);
}
return super.newFuzzyQuery(text, fuzziness);
}
/**
@ -66,9 +96,51 @@ public class SimpleQueryParser extends XSimpleQueryParser {
*/
@Override
public Query newPrefixQuery(String text) {
if (lowercaseExpandedTerms) {
text = text.toLowerCase(locale);
if (settings.lowercaseExpandedTerms()) {
text = text.toLowerCase(settings.locale());
}
try {
return super.newPrefixQuery(text);
} catch (RuntimeException e) {
return rethrowUnlessLenient(e);
}
}
/**
* Class encapsulating the settings for the SimpleQueryString query, with
* their default values
*/
public static class Settings {
private Locale locale = Locale.ROOT;
private boolean lowercaseExpandedTerms = true;
private boolean lenient = false;
public Settings() {
}
public void locale(Locale locale) {
this.locale = locale;
}
public Locale locale() {
return this.locale;
}
public void lowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
this.lowercaseExpandedTerms = lowercaseExpandedTerms;
}
public boolean lowercaseExpandedTerms() {
return this.lowercaseExpandedTerms;
}
public void lenient(boolean lenient) {
this.lenient = lenient;
}
public boolean lenient() {
return this.lenient;
}
return super.newPrefixQuery(text);
}
}

View File

@ -37,6 +37,7 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
private final String queryText;
private int flags = -1;
private Boolean lowercaseExpandedTerms;
private Boolean lenient;
private Locale locale;
/**
@ -113,6 +114,11 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
return this;
}
public SimpleQueryStringBuilder lenient(boolean lenient) {
this.lenient = lenient;
return this;
}
@Override
public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(SimpleQueryStringParser.NAME);
@ -149,6 +155,10 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
builder.field("lowercase_expanded_terms", lowercaseExpandedTerms);
}
if (lenient != null) {
builder.field("lenient", lenient);
}
if (locale != null) {
builder.field("locale", locale.toString());
}

View File

@ -92,9 +92,8 @@ public class SimpleQueryStringParser implements QueryParser {
Map<String, Float> fieldsAndWeights = null;
BooleanClause.Occur defaultOperator = null;
Analyzer analyzer = null;
boolean lowercaseExpandedTerms = true;
Locale locale = Locale.ROOT;
int flags = -1;
SimpleQueryParser.Settings sqsSettings = new SimpleQueryParser.Settings();
XContentParser.Token token;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
@ -173,9 +172,12 @@ public class SimpleQueryStringParser implements QueryParser {
}
} else if ("locale".equals(currentFieldName)) {
String localeStr = parser.text();
locale = LocaleUtils.parse(localeStr);
Locale locale = LocaleUtils.parse(localeStr);
sqsSettings.locale(locale);
} else if ("lowercase_expanded_terms".equals(currentFieldName)) {
lowercaseExpandedTerms = parser.booleanValue();
sqsSettings.lowercaseExpandedTerms(parser.booleanValue());
} else if ("lenient".equals(currentFieldName)) {
sqsSettings.lenient(parser.booleanValue());
} else {
throw new QueryParsingException(parseContext.index(), "[" + NAME + "] unsupported field [" + parser.currentName() + "]");
}
@ -205,7 +207,7 @@ public class SimpleQueryStringParser implements QueryParser {
if (fieldsAndWeights == null) {
fieldsAndWeights = Collections.singletonMap(field, 1.0F);
}
SimpleQueryParser sqp = new SimpleQueryParser(analyzer, fieldsAndWeights, flags, locale, lowercaseExpandedTerms);
SimpleQueryParser sqp = new SimpleQueryParser(analyzer, fieldsAndWeights, flags, sqsSettings);
if (defaultOperator != null) {
sqp.setDefaultOperator(defaultOperator);

View File

@ -2120,6 +2120,25 @@ public class SimpleQueryTests extends ElasticsearchIntegrationTest {
assertFirstHit(searchResponse, hasId("4"));
}
@Test
public void testSimpleQueryStringLenient() {
assertAcked(client().admin().indices().prepareCreate("test1").setSettings(SETTING_NUMBER_OF_SHARDS, 1));
assertAcked(client().admin().indices().prepareCreate("test2").setSettings(SETTING_NUMBER_OF_SHARDS, 1));
client().prepareIndex("test1", "type1", "1").setSource("field", "foo").get();
client().prepareIndex("test2", "type1", "10").setSource("field", 5).get();
refresh();
SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryString("foo").field("field")).get();
assertFailures(searchResponse);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, "1");
searchResponse = client().prepareSearch().setQuery(simpleQueryString("foo").field("field").lenient(true)).get();
assertNoFailures(searchResponse);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, "1");
}
@Test
public void testRangeFilterNoCacheWithNow() throws Exception {
assertAcked(client().admin().indices().prepareCreate("test")