Allow to customize quote analyzer to be used when quoting text in a query_string, closes #1931.

This commit is contained in:
Shay Banon 2012-05-10 11:51:51 +03:00
parent 379976b5ce
commit acbd7b686a
13 changed files with 333 additions and 9 deletions

View File

@ -64,12 +64,17 @@ public class MapperQueryParser extends QueryParser {
private final QueryParseContext parseContext;
private Analyzer quoteAnalyzer;
private boolean forcedAnalyzer;
private boolean forcedQuoteAnalyzer;
private FieldMapper currentMapper;
private boolean analyzeWildcard;
private String quoteFieldSuffix;
public MapperQueryParser(QueryParseContext parseContext) {
super(Lucene.QUERYPARSER_VERSION, null, null);
this.parseContext = parseContext;
@ -85,6 +90,17 @@ public class MapperQueryParser extends QueryParser {
this.field = settings.defaultField();
this.forcedAnalyzer = settings.forcedAnalyzer() != null;
this.analyzer = forcedAnalyzer ? settings.forcedAnalyzer() : settings.defaultAnalyzer();
if (settings.forcedQuoteAnalyzer() != null) {
this.forcedQuoteAnalyzer = true;
this.quoteAnalyzer = settings.forcedQuoteAnalyzer();
} else if (forcedAnalyzer) {
this.forcedQuoteAnalyzer = true;
this.quoteAnalyzer = settings.forcedAnalyzer();
} else {
this.forcedAnalyzer = false;
this.quoteAnalyzer = settings.defaultQuoteAnalyzer();
}
this.quoteFieldSuffix = settings.quoteFieldSuffix();
setMultiTermRewriteMethod(settings.rewriteMethod());
setEnablePositionIncrements(settings.enablePositionIncrements());
setAutoGeneratePhraseQueries(settings.autoGeneratePhraseQueries());
@ -122,10 +138,25 @@ public class MapperQueryParser extends QueryParser {
currentMapper = null;
Analyzer oldAnalyzer = analyzer;
try {
MapperService.SmartNameFieldMappers fieldMappers = parseContext.smartFieldMappers(field);
MapperService.SmartNameFieldMappers fieldMappers = null;
if (quoted) {
analyzer = quoteAnalyzer;
if (quoteFieldSuffix != null) {
fieldMappers = parseContext.smartFieldMappers(field + quoteFieldSuffix);
}
}
if (fieldMappers == null) {
fieldMappers = parseContext.smartFieldMappers(field);
}
if (fieldMappers != null) {
if (!forcedAnalyzer) {
analyzer = fieldMappers.searchAnalyzer();
if (quoted) {
if (!forcedQuoteAnalyzer) {
analyzer = fieldMappers.searchQuoteAnalyzer();
}
} else {
if (!forcedAnalyzer) {
analyzer = fieldMappers.searchAnalyzer();
}
}
currentMapper = fieldMappers.fieldMappers().mapper();
if (currentMapper != null) {

View File

@ -45,7 +45,10 @@ public class QueryParserSettings {
private boolean analyzeWildcard = DEFAULT_ANALYZE_WILDCARD;
private boolean escape = false;
private Analyzer defaultAnalyzer = null;
private Analyzer defaultQuoteAnalyzer = null;
private Analyzer forcedAnalyzer = null;
private Analyzer forcedQuoteAnalyzer = null;
private String quoteFieldSuffix = null;
private MultiTermQuery.RewriteMethod rewriteMethod = MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
private String minimumShouldMatch;
@ -153,6 +156,14 @@ public class QueryParserSettings {
this.defaultAnalyzer = defaultAnalyzer;
}
public Analyzer defaultQuoteAnalyzer() {
return defaultQuoteAnalyzer;
}
public void defaultQuoteAnalyzer(Analyzer defaultAnalyzer) {
this.defaultQuoteAnalyzer = defaultAnalyzer;
}
public Analyzer forcedAnalyzer() {
return forcedAnalyzer;
}
@ -161,6 +172,14 @@ public class QueryParserSettings {
this.forcedAnalyzer = forcedAnalyzer;
}
public Analyzer forcedQuoteAnalyzer() {
return forcedQuoteAnalyzer;
}
public void forcedQuoteAnalyzer(Analyzer forcedAnalyzer) {
this.forcedQuoteAnalyzer = forcedAnalyzer;
}
public boolean analyzeWildcard() {
return this.analyzeWildcard;
}
@ -185,6 +204,14 @@ public class QueryParserSettings {
this.minimumShouldMatch = minimumShouldMatch;
}
public void quoteFieldSuffix(String quoteFieldSuffix) {
this.quoteFieldSuffix = quoteFieldSuffix;
}
public String quoteFieldSuffix() {
return this.quoteFieldSuffix;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
@ -204,8 +231,12 @@ public class QueryParserSettings {
if (phraseSlop != that.phraseSlop) return false;
if (defaultAnalyzer != null ? !defaultAnalyzer.equals(that.defaultAnalyzer) : that.defaultAnalyzer != null)
return false;
if (defaultQuoteAnalyzer != null ? !defaultQuoteAnalyzer.equals(that.defaultQuoteAnalyzer) : that.defaultQuoteAnalyzer != null)
return false;
if (forcedAnalyzer != null ? !forcedAnalyzer.equals(that.forcedAnalyzer) : that.forcedAnalyzer != null)
return false;
if (forcedQuoteAnalyzer != null ? !forcedQuoteAnalyzer.equals(that.forcedQuoteAnalyzer) : that.forcedQuoteAnalyzer != null)
return false;
if (defaultField != null ? !defaultField.equals(that.defaultField) : that.defaultField != null) return false;
if (defaultOperator != that.defaultOperator) return false;
if (queryString != null ? !queryString.equals(that.queryString) : that.queryString != null) return false;
@ -213,6 +244,8 @@ public class QueryParserSettings {
return false;
if (minimumShouldMatch != null ? !minimumShouldMatch.equals(that.minimumShouldMatch) : that.minimumShouldMatch != null)
return false;
if (quoteFieldSuffix != null ? !quoteFieldSuffix.equals(that.quoteFieldSuffix) : that.quoteFieldSuffix != null)
return false;
return true;
}
@ -232,7 +265,9 @@ public class QueryParserSettings {
result = 31 * result + fuzzyPrefixLength;
result = 31 * result + (escape ? 1 : 0);
result = 31 * result + (defaultAnalyzer != null ? defaultAnalyzer.hashCode() : 0);
result = 31 * result + (defaultQuoteAnalyzer != null ? defaultQuoteAnalyzer.hashCode() : 0);
result = 31 * result + (forcedAnalyzer != null ? forcedAnalyzer.hashCode() : 0);
result = 31 * result + (forcedQuoteAnalyzer != null ? forcedQuoteAnalyzer.hashCode() : 0);
result = 31 * result + (analyzeWildcard ? 1 : 0);
return result;
}

View File

@ -51,6 +51,7 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
private final NamedAnalyzer defaultAnalyzer;
private final NamedAnalyzer defaultIndexAnalyzer;
private final NamedAnalyzer defaultSearchAnalyzer;
private final NamedAnalyzer defaultSearchQuoteAnalyzer;
public AnalysisService(Index index) {
this(index, ImmutableSettings.Builder.EMPTY_SETTINGS, null, null, null, null, null);
@ -209,6 +210,9 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
if (!analyzerProviders.containsKey("default_search")) {
analyzerProviders.put("default_search", analyzerProviders.get("default"));
}
if (!analyzerProviders.containsKey("default_search_quoted")) {
analyzerProviders.put("default_search_quoted", analyzerProviders.get("default_search"));
}
Map<String, NamedAnalyzer> analyzers = newHashMap();
for (AnalyzerProvider analyzerFactory : analyzerProviders.values()) {
@ -233,6 +237,7 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
defaultAnalyzer = analyzers.get("default");
defaultIndexAnalyzer = analyzers.containsKey("default_index") ? analyzers.get("default_index") : analyzers.get("default");
defaultSearchAnalyzer = analyzers.containsKey("default_search") ? analyzers.get("default_search") : analyzers.get("default");
defaultSearchQuoteAnalyzer = analyzers.containsKey("default_search_quote") ? analyzers.get("default_search_quote") : defaultSearchAnalyzer;
this.analyzers = ImmutableMap.copyOf(analyzers);
}
@ -268,6 +273,10 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
return defaultSearchAnalyzer;
}
public NamedAnalyzer defaultSearchQuoteAnalyzer() {
return defaultSearchQuoteAnalyzer;
}
public TokenizerFactory tokenizer(String name) {
return tokenizers.get(name);
}

View File

@ -42,6 +42,7 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
private final FieldNameAnalyzer indexAnalyzer;
private final FieldNameAnalyzer searchAnalyzer;
private final FieldNameAnalyzer searchQuoteAnalyzer;
public DocumentFieldMappers(DocumentMapper docMapper, Iterable<FieldMapper> fieldMappers) {
final Map<String, FieldMappers> tempNameFieldMappers = newHashMap();
@ -50,6 +51,7 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
final Map<String, Analyzer> indexAnalyzers = newHashMap();
final Map<String, Analyzer> searchAnalyzers = newHashMap();
final Map<String, Analyzer> searchQuoteAnalyzers = newHashMap();
for (FieldMapper fieldMapper : fieldMappers) {
FieldMappers mappers = tempNameFieldMappers.get(fieldMapper.names().name());
@ -82,6 +84,9 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
if (fieldMapper.searchAnalyzer() != null) {
searchAnalyzers.put(fieldMapper.names().indexName(), fieldMapper.searchAnalyzer());
}
if (fieldMapper.searchQuoteAnalyzer() != null) {
searchQuoteAnalyzers.put(fieldMapper.names().indexName(), fieldMapper.searchQuoteAnalyzer());
}
}
this.fieldMappers = ImmutableList.copyOf(fieldMappers);
this.nameFieldMappers = ImmutableMap.copyOf(tempNameFieldMappers);
@ -90,6 +95,7 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
this.indexAnalyzer = new FieldNameAnalyzer(indexAnalyzers, docMapper.indexAnalyzer());
this.searchAnalyzer = new FieldNameAnalyzer(searchAnalyzers, docMapper.searchAnalyzer());
this.searchQuoteAnalyzer = new FieldNameAnalyzer(searchQuoteAnalyzers, docMapper.searchQuotedAnalyzer());
}
@Override
@ -179,6 +185,10 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
return this.searchAnalyzer;
}
public Analyzer searchQuoteAnalyzer() {
return this.searchQuoteAnalyzer;
}
public DocumentFieldMappers concat(DocumentMapper docMapper, FieldMapper... fieldMappers) {
return concat(docMapper, newArrayList(fieldMappers));
}

View File

@ -132,6 +132,8 @@ public class DocumentMapper implements ToXContent {
private NamedAnalyzer searchAnalyzer;
private NamedAnalyzer searchQuoteAnalyzer;
private final String index;
@Nullable
@ -193,6 +195,14 @@ public class DocumentMapper implements ToXContent {
public Builder searchAnalyzer(NamedAnalyzer searchAnalyzer) {
this.searchAnalyzer = searchAnalyzer;
if (this.searchQuoteAnalyzer == null) {
this.searchQuoteAnalyzer = searchAnalyzer;
}
return this;
}
public Builder searchQuoteAnalyzer(NamedAnalyzer searchQuoteAnalyzer) {
this.searchQuoteAnalyzer = searchQuoteAnalyzer;
return this;
}
@ -200,10 +210,14 @@ public class DocumentMapper implements ToXContent {
return searchAnalyzer != null;
}
public boolean hasSearchQuoteAnalyzer() {
return searchQuoteAnalyzer != null;
}
public DocumentMapper build(DocumentMapperParser docMapperParser) {
Preconditions.checkNotNull(rootObjectMapper, "Mapper builder must have the root object mapper set");
return new DocumentMapper(index, indexSettings, docMapperParser, rootObjectMapper, meta,
indexAnalyzer, searchAnalyzer,
indexAnalyzer, searchAnalyzer, searchQuoteAnalyzer,
rootMappers);
}
}
@ -237,6 +251,7 @@ public class DocumentMapper implements ToXContent {
private final NamedAnalyzer indexAnalyzer;
private final NamedAnalyzer searchAnalyzer;
private final NamedAnalyzer searchQuoteAnalyzer;
private volatile DocumentFieldMappers fieldMappers;
@ -257,7 +272,7 @@ public class DocumentMapper implements ToXContent {
public DocumentMapper(String index, @Nullable Settings indexSettings, DocumentMapperParser docMapperParser,
RootObjectMapper rootObjectMapper,
ImmutableMap<String, Object> meta,
NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer,
NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer, NamedAnalyzer searchQuoteAnalyzer,
Map<Class<? extends RootMapper>, RootMapper> rootMappers) {
this.index = index;
this.indexSettings = indexSettings;
@ -278,6 +293,7 @@ public class DocumentMapper implements ToXContent {
this.indexAnalyzer = indexAnalyzer;
this.searchAnalyzer = searchAnalyzer;
this.searchQuoteAnalyzer = searchQuoteAnalyzer != null ? searchQuoteAnalyzer : searchAnalyzer;
this.typeFilter = typeMapper().fieldFilter(type, null);
@ -389,6 +405,10 @@ public class DocumentMapper implements ToXContent {
return this.searchAnalyzer;
}
public Analyzer searchQuotedAnalyzer() {
return this.searchQuoteAnalyzer;
}
public Filter typeFilter() {
return this.typeFilter;
}

View File

@ -177,6 +177,12 @@ public class DocumentMapperParser extends AbstractIndexComponent {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for search_analyzer setting on root type [" + type + "]");
}
docBuilder.searchAnalyzer(analyzer);
} else if ("search_quote_analyzer".equals(fieldName)) {
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for search_analyzer setting on root type [" + type + "]");
}
docBuilder.searchQuoteAnalyzer(analyzer);
} else if ("analyzer".equals(fieldName)) {
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
@ -198,6 +204,9 @@ public class DocumentMapperParser extends AbstractIndexComponent {
if (!docBuilder.hasSearchAnalyzer()) {
docBuilder.searchAnalyzer(analysisService.defaultSearchAnalyzer());
}
if (!docBuilder.hasSearchQuoteAnalyzer()) {
docBuilder.searchAnalyzer(analysisService.defaultSearchQuoteAnalyzer());
}
ImmutableMap<String, Object> attributes = ImmutableMap.of();
if (mapping.containsKey("_meta")) {

View File

@ -147,6 +147,11 @@ public interface FieldMapper<T> {
*/
Analyzer searchAnalyzer();
/**
* The analyzer that will be used for quoted search on the field.
*/
Analyzer searchQuoteAnalyzer();
/**
* Returns the value that will be used as a result for search. Can be only of specific types... .
*/

View File

@ -95,6 +95,7 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
private final InternalObjectMapperListener objectMapperListener = new InternalObjectMapperListener();
private final SmartIndexNameSearchAnalyzer searchAnalyzer;
private final SmartIndexNameSearchQuoteAnalyzer searchQuoteAnalyzer;
@Inject
public MapperService(Index index, @IndexSettings Settings indexSettings, Environment environment, AnalysisService analysisService) {
@ -102,6 +103,7 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
this.analysisService = analysisService;
this.documentParser = new DocumentMapperParser(index, indexSettings, analysisService);
this.searchAnalyzer = new SmartIndexNameSearchAnalyzer(analysisService.defaultSearchAnalyzer());
this.searchQuoteAnalyzer = new SmartIndexNameSearchQuoteAnalyzer(analysisService.defaultSearchQuoteAnalyzer());
this.dynamic = componentSettings.getAsBoolean("dynamic", true);
String defaultMappingLocation = componentSettings.get("default_mapping_location");
@ -665,6 +667,10 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
return this.searchAnalyzer;
}
public Analyzer searchQuoteAnalyzer() {
return this.searchQuoteAnalyzer;
}
public static class SmartNameObjectMapper {
private final ObjectMapper mapper;
private final DocumentMapper docMapper;
@ -767,6 +773,19 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
}
return mapperService.searchAnalyzer();
}
public Analyzer searchQuoteAnalyzer() {
if (hasMapper()) {
Analyzer analyzer = mapper().searchQuoteAnalyzer();
if (analyzer != null) {
return analyzer;
}
}
if (docMapper != null && docMapper.searchQuotedAnalyzer() != null) {
return docMapper.searchQuotedAnalyzer();
}
return mapperService.searchQuoteAnalyzer();
}
}
final class SmartIndexNameSearchAnalyzer extends Analyzer {
@ -867,6 +886,104 @@ public class MapperService extends AbstractIndexComponent implements Iterable<Do
}
}
final class SmartIndexNameSearchQuoteAnalyzer extends Analyzer {
private final Analyzer defaultAnalyzer;
SmartIndexNameSearchQuoteAnalyzer(Analyzer defaultAnalyzer) {
this.defaultAnalyzer = defaultAnalyzer;
}
@Override
public int getPositionIncrementGap(String fieldName) {
int dotIndex = fieldName.indexOf('.');
if (dotIndex != -1) {
String possibleType = fieldName.substring(0, dotIndex);
DocumentMapper possibleDocMapper = mappers.get(possibleType);
if (possibleDocMapper != null) {
return possibleDocMapper.mappers().searchQuoteAnalyzer().getPositionIncrementGap(fieldName);
}
}
FieldMappers mappers = fullNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().getPositionIncrementGap(fieldName);
}
mappers = indexNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().getPositionIncrementGap(fieldName);
}
return defaultAnalyzer.getPositionIncrementGap(fieldName);
}
@Override
public int getOffsetGap(Fieldable field) {
String fieldName = field.name();
int dotIndex = fieldName.indexOf('.');
if (dotIndex != -1) {
String possibleType = fieldName.substring(0, dotIndex);
DocumentMapper possibleDocMapper = mappers.get(possibleType);
if (possibleDocMapper != null) {
return possibleDocMapper.mappers().searchQuoteAnalyzer().getOffsetGap(field);
}
}
FieldMappers mappers = fullNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().getOffsetGap(field);
}
mappers = indexNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().getOffsetGap(field);
}
return defaultAnalyzer.getOffsetGap(field);
}
@Override
public final TokenStream tokenStream(String fieldName, Reader reader) {
int dotIndex = fieldName.indexOf('.');
if (dotIndex != -1) {
String possibleType = fieldName.substring(0, dotIndex);
DocumentMapper possibleDocMapper = mappers.get(possibleType);
if (possibleDocMapper != null) {
return possibleDocMapper.mappers().searchQuoteAnalyzer().tokenStream(fieldName, reader);
}
}
FieldMappers mappers = fullNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().tokenStream(fieldName, reader);
}
mappers = indexNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().tokenStream(fieldName, reader);
}
return defaultAnalyzer.tokenStream(fieldName, reader);
}
@Override
public final TokenStream reusableTokenStream(String fieldName, Reader reader) throws IOException {
int dotIndex = fieldName.indexOf('.');
if (dotIndex != -1) {
String possibleType = fieldName.substring(0, dotIndex);
DocumentMapper possibleDocMapper = mappers.get(possibleType);
if (possibleDocMapper != null) {
return possibleDocMapper.mappers().searchQuoteAnalyzer().reusableTokenStream(fieldName, reader);
}
}
FieldMappers mappers = fullNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().reusableTokenStream(fieldName, reader);
}
mappers = indexNameFieldMappers.get(fieldName);
if (mappers != null && mappers.mapper() != null && mappers.mapper().searchAnalyzer() != null) {
return mappers.mapper().searchQuoteAnalyzer().reusableTokenStream(fieldName, reader);
}
return defaultAnalyzer.reusableTokenStream(fieldName, reader);
}
}
class InternalFieldMapperListener implements FieldMapperListener {
@Override
public void fieldMapper(FieldMapper fieldMapper) {

View File

@ -301,6 +301,11 @@ public abstract class AbstractFieldMapper<T> implements FieldMapper<T>, Mapper {
return this.searchAnalyzer;
}
@Override
public Analyzer searchQuoteAnalyzer() {
return this.searchAnalyzer;
}
@Override
public void parse(ParseContext context) throws IOException {
try {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.mapper.core;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Fieldable;
import org.elasticsearch.common.Strings;
@ -55,6 +56,8 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
protected int positionOffsetGap = Defaults.POSITION_OFFSET_GAP;
protected NamedAnalyzer searchQuotedAnalyzer;
public Builder(String name) {
super(name);
builder = this;
@ -71,20 +74,35 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
return this;
}
@Override
public Builder searchAnalyzer(NamedAnalyzer searchAnalyzer) {
super.searchAnalyzer(searchAnalyzer);
if (searchQuotedAnalyzer == null) {
searchQuotedAnalyzer = searchAnalyzer;
}
return this;
}
public Builder positionOffsetGap(int positionOffsetGap) {
this.positionOffsetGap = positionOffsetGap;
return this;
}
public Builder searchQuotedAnalyzer(NamedAnalyzer analyzer) {
this.searchQuotedAnalyzer = analyzer;
return builder;
}
@Override
public StringFieldMapper build(BuilderContext context) {
if (positionOffsetGap > 0) {
indexAnalyzer = new NamedCustomAnalyzer(indexAnalyzer, positionOffsetGap);
searchAnalyzer = new NamedCustomAnalyzer(searchAnalyzer, positionOffsetGap);
searchQuotedAnalyzer = new NamedCustomAnalyzer(searchQuotedAnalyzer, positionOffsetGap);
}
StringFieldMapper fieldMapper = new StringFieldMapper(buildNames(context),
index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, nullValue,
indexAnalyzer, searchAnalyzer, positionOffsetGap);
indexAnalyzer, searchAnalyzer, searchQuotedAnalyzer, positionOffsetGap);
fieldMapper.includeInAll(includeInAll);
return fieldMapper;
}
@ -100,6 +118,12 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
Object propNode = entry.getValue();
if (propName.equals("null_value")) {
builder.nullValue(propNode.toString());
} else if (propName.equals("search_quote_analyzer")) {
NamedAnalyzer analyzer = parserContext.analysisService().analyzer(propNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + propNode.toString() + "] not found for field [" + name + "]");
}
builder.searchQuotedAnalyzer(analyzer);
} else if (propName.equals("position_offset_gap")) {
builder.positionOffsetGap(XContentMapValues.nodeIntegerValue(propNode, -1));
// we need to update to actual analyzers if they are not set in this case...
@ -110,6 +134,9 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
if (builder.searchAnalyzer == null) {
builder.searchAnalyzer = parserContext.analysisService().defaultSearchAnalyzer();
}
if (builder.searchQuotedAnalyzer == null) {
builder.searchQuotedAnalyzer = parserContext.analysisService().defaultSearchQuoteAnalyzer();
}
}
}
return builder;
@ -122,18 +149,21 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
private int positionOffsetGap;
private NamedAnalyzer searchQuotedAnalyzer;
protected StringFieldMapper(Names names, Field.Index index, Field.Store store, Field.TermVector termVector,
float boost, boolean omitNorms, boolean omitTermFreqAndPositions,
String nullValue, NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer) {
this(names, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, nullValue, indexAnalyzer, searchAnalyzer, Defaults.POSITION_OFFSET_GAP);
this(names, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, nullValue, indexAnalyzer, searchAnalyzer, searchAnalyzer, Defaults.POSITION_OFFSET_GAP);
}
protected StringFieldMapper(Names names, Field.Index index, Field.Store store, Field.TermVector termVector,
float boost, boolean omitNorms, boolean omitTermFreqAndPositions,
String nullValue, NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer, int positionOffsetGap) {
String nullValue, NamedAnalyzer indexAnalyzer, NamedAnalyzer searchAnalyzer, NamedAnalyzer searchQuotedAnalyzer, int positionOffsetGap) {
super(names, index, store, termVector, boost, omitNorms, omitTermFreqAndPositions, indexAnalyzer, searchAnalyzer);
this.nullValue = nullValue;
this.positionOffsetGap = positionOffsetGap;
this.searchQuotedAnalyzer = searchQuotedAnalyzer != null ? searchQuotedAnalyzer : searchAnalyzer;
}
@Override
@ -179,6 +209,11 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
return this.positionOffsetGap;
}
@Override
public Analyzer searchQuoteAnalyzer() {
return this.searchQuotedAnalyzer;
}
@Override
protected Field parseCreateField(ParseContext context) throws IOException {
String value = nullValue;
@ -266,5 +301,8 @@ public class StringFieldMapper extends AbstractFieldMapper<String> implements Al
if (positionOffsetGap != Defaults.POSITION_OFFSET_GAP) {
builder.field("position_offset_gap", positionOffsetGap);
}
if (searchQuotedAnalyzer != null && searchAnalyzer != searchQuotedAnalyzer) {
builder.field("search_quote_analyzer", searchQuotedAnalyzer.name());
}
}
}

View File

@ -99,6 +99,12 @@ public class FieldQueryParser implements QueryParser {
throw new QueryParsingException(parseContext.index(), "[query_string] analyzer [" + parser.text() + "] not found");
}
qpSettings.forcedAnalyzer(analyzer);
} else if ("quote_analyzer".equals(currentFieldName) || "quoteAnalyzer".equals(currentFieldName)) {
NamedAnalyzer analyzer = parseContext.analysisService().analyzer(parser.text());
if (analyzer == null) {
throw new QueryParsingException(parseContext.index(), "[query_string] analyzer [" + parser.text() + "] not found");
}
qpSettings.forcedQuoteAnalyzer(analyzer);
} else if ("default_operator".equals(currentFieldName) || "defaultOperator".equals(currentFieldName)) {
String op = parser.text();
if ("or".equalsIgnoreCase(op)) {
@ -120,6 +126,8 @@ public class FieldQueryParser implements QueryParser {
qpSettings.rewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull()));
} else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
qpSettings.minimumShouldMatch(parser.textOrNull());
} else if ("quote_field_suffix".equals(currentFieldName) || "quoteFieldSuffix".equals(currentFieldName)) {
qpSettings.quoteFieldSuffix(parser.textOrNull());
} else {
throw new QueryParsingException(parseContext.index(), "[field] query does not support [" + currentFieldName + "]");
}
@ -133,6 +141,7 @@ public class FieldQueryParser implements QueryParser {
}
qpSettings.defaultAnalyzer(parseContext.mapperService().searchAnalyzer());
qpSettings.defaultQuoteAnalyzer(parseContext.mapperService().searchQuoteAnalyzer());
if (qpSettings.queryString() == null) {
throw new QueryParsingException(parseContext.index(), "No value specified for term query");

View File

@ -34,7 +34,7 @@ import static com.google.common.collect.Lists.newArrayList;
* will use the {@link #defaultField(String)} set. The second, when one or more fields are added
* (using {@link #field(String)}), will run the parsed query against the provided fields, and combine
* them either using DisMax or a plain boolean query (see {@link #useDisMax(boolean)}).
*
* <p/>
* (shay.baon)
*/
public class QueryStringQueryBuilder extends BaseQueryBuilder {
@ -51,6 +51,9 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
private Operator defaultOperator;
private String analyzer;
private String quoteAnalyzer;
private String quoteFieldSuffix;
private Boolean autoGeneratePhraseQueries;
@ -163,6 +166,16 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
return this;
}
/**
* The optional analyzer used to analyze the query string for phrase searches. Note, if a field has search (quote) analyzer
* defined for it, then it will be used automatically. Defaults to the smart search analyzer.
*/
public QueryStringQueryBuilder quoteAnalyzer(String analyzer) {
this.quoteAnalyzer = analyzer;
return this;
}
/**
* Set to true if phrase queries will be automatically generated
* when the analyzer returns more than one term from whitespace
@ -258,6 +271,14 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
return this;
}
/**
* An optional field name suffix to automatically try and add to the field searched when using quoted text.
*/
public QueryStringQueryBuilder quoteFieldSuffix(String quoteFieldSuffix) {
this.quoteFieldSuffix = quoteFieldSuffix;
return this;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(QueryStringQueryParser.NAME);
@ -291,6 +312,9 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
if (analyzer != null) {
builder.field("analyzer", analyzer);
}
if (quoteAnalyzer != null) {
builder.field("quote_analyzer", quoteAnalyzer);
}
if (autoGeneratePhraseQueries != null) {
builder.field("auto_generate_phrase_queries", autoGeneratePhraseQueries);
}
@ -324,6 +348,9 @@ public class QueryStringQueryBuilder extends BaseQueryBuilder {
if (minimumShouldMatch != null) {
builder.field("minimum_should_match", minimumShouldMatch);
}
if (quoteFieldSuffix != null) {
builder.field("quote_field_suffix", quoteFieldSuffix);
}
builder.endObject();
}
}

View File

@ -142,6 +142,12 @@ public class QueryStringQueryParser implements QueryParser {
throw new QueryParsingException(parseContext.index(), "[query_string] analyzer [" + parser.text() + "] not found");
}
qpSettings.forcedAnalyzer(analyzer);
} else if ("quote_analyzer".equals(currentFieldName) || "quoteAnalyzer".equals(currentFieldName)) {
NamedAnalyzer analyzer = parseContext.analysisService().analyzer(parser.text());
if (analyzer == null) {
throw new QueryParsingException(parseContext.index(), "[query_string] quote_analyzer [" + parser.text() + "] not found");
}
qpSettings.forcedQuoteAnalyzer(analyzer);
} else if ("allow_leading_wildcard".equals(currentFieldName) || "allowLeadingWildcard".equals(currentFieldName)) {
qpSettings.allowLeadingWildcard(parser.booleanValue());
} else if ("auto_generate_phrase_queries".equals(currentFieldName) || "autoGeneratePhraseQueries".equals(currentFieldName)) {
@ -170,6 +176,8 @@ public class QueryStringQueryParser implements QueryParser {
qpSettings.rewriteMethod(QueryParsers.parseRewriteMethod(parser.textOrNull()));
} else if ("minimum_should_match".equals(currentFieldName) || "minimumShouldMatch".equals(currentFieldName)) {
qpSettings.minimumShouldMatch(parser.textOrNull());
} else if ("quote_field_suffix".equals(currentFieldName) || "quoteFieldSuffix".equals(currentFieldName)) {
qpSettings.quoteFieldSuffix(parser.textOrNull());
} else {
throw new QueryParsingException(parseContext.index(), "[query_string] query does not support [" + currentFieldName + "]");
}
@ -179,6 +187,7 @@ public class QueryStringQueryParser implements QueryParser {
throw new QueryParsingException(parseContext.index(), "query_string must be provided with a [query]");
}
qpSettings.defaultAnalyzer(parseContext.mapperService().searchAnalyzer());
qpSettings.defaultQuoteAnalyzer(parseContext.mapperService().searchQuoteAnalyzer());
if (qpSettings.escape()) {
qpSettings.queryString(org.apache.lucene.queryParser.QueryParser.escape(qpSettings.queryString()));