Query: add option for analyze wildcard/prefix also to simple_query_string query

The query_string query has an option for analyzing wildcard/prefix (#787) by a best effort approach.

This adds `analyze_wildcard` option also to simple_query_string.

The default is set to `false` so the existing behavior of simple_query_string is unchanged.
This commit is contained in:
Jörg Prante 2014-11-10 13:31:27 +01:00 committed by Lee Hinman
parent 85fba3636a
commit 8aa64c6b76
4 changed files with 101 additions and 4 deletions

View File

@ -19,9 +19,14 @@
package org.elasticsearch.index.query;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -109,7 +114,7 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp
/**
* Dispatches to Lucene's SimpleQueryParser's newPrefixQuery, optionally
* lowercasing the term first
* lowercasing the term first or trying to analyze terms
*/
@Override
public Query newPrefixQuery(String text) {
@ -119,9 +124,15 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp
BooleanQuery bq = new BooleanQuery(true);
for (Map.Entry<String,Float> entry : weights.entrySet()) {
try {
PrefixQuery prefix = new PrefixQuery(new Term(entry.getKey(), text));
prefix.setBoost(entry.getValue());
bq.add(prefix, BooleanClause.Occur.SHOULD);
if (settings.analyzeWildcard()) {
Query analyzedQuery = newPossiblyAnalyzedQuery(entry.getKey(), text);
analyzedQuery.setBoost(entry.getValue());
bq.add(analyzedQuery, BooleanClause.Occur.SHOULD);
} else {
PrefixQuery prefix = new PrefixQuery(new Term(entry.getKey(), text));
prefix.setBoost(entry.getValue());
bq.add(prefix, BooleanClause.Occur.SHOULD);
}
} catch (RuntimeException e) {
return rethrowUnlessLenient(e);
}
@ -129,6 +140,43 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp
return super.simplify(bq);
}
private Query newPossiblyAnalyzedQuery(String field, String termStr) {
TokenStream source;
try {
source = getAnalyzer().tokenStream(field, termStr);
source.reset();
} catch (IOException e) {
return new PrefixQuery(new Term(field, termStr));
}
List<String> tlist = new ArrayList<>();
CharTermAttribute termAtt = source.addAttribute(CharTermAttribute.class);
while (true) {
try {
if (!source.incrementToken()) {
break;
}
} catch (IOException e) {
break;
}
tlist.add(termAtt.toString());
}
try {
source.close();
} catch (IOException e) {
// ignore
}
if (tlist.size() == 1) {
return new PrefixQuery(new Term(field, tlist.get(0)));
} else {
// build a boolean query with prefix on each one...
BooleanQuery bq = new BooleanQuery();
for (String token : tlist) {
bq.add(new BooleanClause(new PrefixQuery(new Term(field, token)), BooleanClause.Occur.SHOULD));
}
return bq;
}
}
/**
* Class encapsulating the settings for the SimpleQueryString query, with
* their default values
@ -137,6 +185,7 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp
private Locale locale = Locale.ROOT;
private boolean lowercaseExpandedTerms = true;
private boolean lenient = false;
private boolean analyzeWildcard = false;
public Settings() {
@ -165,5 +214,13 @@ public class SimpleQueryParser extends org.apache.lucene.queryparser.simple.Simp
public boolean lenient() {
return this.lenient;
}
public void analyzeWildcard(boolean analyzeWildcard) {
this.analyzeWildcard = analyzeWildcard;
}
public boolean analyzeWildcard() {
return analyzeWildcard;
}
}
}

View File

@ -39,6 +39,7 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
private int flags = -1;
private Boolean lowercaseExpandedTerms;
private Boolean lenient;
private Boolean analyzeWildcard;
private Locale locale;
/**
@ -128,6 +129,11 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
return this;
}
public SimpleQueryStringBuilder analyzeWildcard(boolean analyzeWildcard) {
this.analyzeWildcard = analyzeWildcard;
return this;
}
@Override
public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(SimpleQueryStringParser.NAME);
@ -168,6 +174,10 @@ public class SimpleQueryStringBuilder extends BaseQueryBuilder {
builder.field("lenient", lenient);
}
if (analyzeWildcard != null) {
builder.field("analyze_wildcard", analyzeWildcard);
}
if (locale != null) {
builder.field("locale", locale.toString());
}

View File

@ -178,6 +178,8 @@ public class SimpleQueryStringParser implements QueryParser {
sqsSettings.lowercaseExpandedTerms(parser.booleanValue());
} else if ("lenient".equals(currentFieldName)) {
sqsSettings.lenient(parser.booleanValue());
} else if ("analyze_wildcard".equals(currentFieldName)) {
sqsSettings.analyzeWildcard(parser.booleanValue());
} else if ("_name".equals(currentFieldName)) {
queryName = parser.text();
} else {

View File

@ -19,7 +19,9 @@
package org.elasticsearch.search.query;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.SimpleQueryStringBuilder;
import org.elasticsearch.index.query.SimpleQueryStringFlag;
@ -266,4 +268,30 @@ public class SimpleQueryStringTests extends ElasticsearchIntegrationTest {
assertHitCount(resp, 1);
assertSearchHits(resp, "1");
}
@Test
public void testSimpleQueryStringAnalyzeWildcard() throws ExecutionException, InterruptedException, IOException {
String mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject("type1")
.startObject("properties")
.startObject("location")
.field("type", "string")
.field("analyzer", "german")
.endObject()
.endObject()
.endObject()
.endObject().string();
CreateIndexRequestBuilder mappingRequest = client().admin().indices().prepareCreate("test1").addMapping("type1", mapping);
mappingRequest.execute().actionGet();
indexRandom(true, client().prepareIndex("test1", "type1", "1").setSource("location", "Köln"));
refresh();
SearchResponse searchResponse = client().prepareSearch().setQuery(simpleQueryString("Köln*").analyzeWildcard(true).field("location")).get();
assertNoFailures(searchResponse);
assertHitCount(searchResponse, 1l);
assertSearchHits(searchResponse, "1");
}
}