scope internal methods in suggest builders

This commit is contained in:
Areek Zillur 2016-03-11 13:30:11 -05:00
parent 31dcb3e18b
commit c9f30f2f3f
6 changed files with 196 additions and 193 deletions

View File

@ -29,7 +29,7 @@ import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
public abstract class SuggestBuilders {
/**
* Creates a term suggestion lookup query with the provided <code>fieldname</code>
* Creates a term suggestion lookup query with the provided <code>field</code>
*
* @return a {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder}
* instance
@ -39,7 +39,7 @@ public abstract class SuggestBuilders {
}
/**
* Creates a phrase suggestion lookup query with the provided <code>fieldname</code>
* Creates a phrase suggestion lookup query with the provided <code>field</code>
*
* @return a {@link org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder}
* instance
@ -49,7 +49,7 @@ public abstract class SuggestBuilders {
}
/**
* Creates a completion suggestion lookup query with the provided <code>fieldname</code>
* Creates a completion suggestion lookup query with the provided <code>field</code>
*
* @return a {@link org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder}
* instance

View File

@ -20,6 +20,7 @@
package org.elasticsearch.search.suggest;
import org.apache.lucene.analysis.Analyzer;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
@ -44,7 +45,7 @@ import java.util.Objects;
*/
public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends ToXContentToBytes implements NamedWriteable<T> {
protected final String fieldname;
protected final String field;
protected String text;
protected String prefix;
protected String regex;
@ -62,21 +63,21 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
/**
* Creates a new suggestion.
* @param fieldname field to fetch the candidate suggestions from
* @param field field to execute suggestions on
*/
public SuggestionBuilder(String fieldname) {
Objects.requireNonNull(fieldname, "suggestion requires a field name");
if (fieldname.isEmpty()) {
protected SuggestionBuilder(String field) {
Objects.requireNonNull(field, "suggestion requires a field name");
if (field.isEmpty()) {
throw new IllegalArgumentException("suggestion field name is empty");
}
this.fieldname = fieldname;
this.field = field;
}
/**
* internal copy constructor that copies over all class fields from second SuggestionBuilder except field name.
*/
protected SuggestionBuilder(String fieldname, SuggestionBuilder<?> in) {
this(fieldname);
protected SuggestionBuilder(String field, SuggestionBuilder<?> in) {
this(field);
text = in.text;
prefix = in.prefix;
regex = in.regex;
@ -127,157 +128,11 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
return this.regex;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (text != null) {
builder.field(TEXT_FIELD.getPreferredName(), text);
}
if (prefix != null) {
builder.field(PREFIX_FIELD.getPreferredName(), prefix);
}
if (regex != null) {
builder.field(REGEX_FIELD.getPreferredName(), regex);
}
builder.startObject(getSuggesterName());
if (analyzer != null) {
builder.field(ANALYZER_FIELD.getPreferredName(), analyzer);
}
builder.field(FIELDNAME_FIELD.getPreferredName(), fieldname);
if (size != null) {
builder.field(SIZE_FIELD.getPreferredName(), size);
}
if (shardSize != null) {
builder.field(SHARDSIZE_FIELD.getPreferredName(), shardSize);
}
builder = innerToXContent(builder, params);
builder.endObject();
return builder;
}
protected abstract XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException;
public static SuggestionBuilder<?> fromXContent(QueryParseContext parseContext, Suggesters suggesters)
throws IOException {
XContentParser parser = parseContext.parser();
ParseFieldMatcher parsefieldMatcher = parseContext.parseFieldMatcher();
XContentParser.Token token;
String currentFieldName = null;
String suggestText = null;
String prefix = null;
String regex = null;
SuggestionBuilder<?> suggestionBuilder = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (parsefieldMatcher.match(currentFieldName, TEXT_FIELD)) {
suggestText = parser.text();
} else if (parsefieldMatcher.match(currentFieldName, PREFIX_FIELD)) {
prefix = parser.text();
} else if (parsefieldMatcher.match(currentFieldName, REGEX_FIELD)) {
regex = parser.text();
} else {
throw new ParsingException(parser.getTokenLocation(), "suggestion does not support [" + currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
SuggestionBuilder<?> suggestParser = suggesters.getSuggestionPrototype(currentFieldName);
if (suggestParser == null) {
throw new ParsingException(parser.getTokenLocation(), "suggestion [" + currentFieldName + "] not supported");
}
suggestionBuilder = suggestParser.innerFromXContent(parseContext);
}
}
if (suggestText != null) {
suggestionBuilder.text(suggestText);
}
if (prefix != null) {
suggestionBuilder.prefix(prefix);
}
if (regex != null) {
suggestionBuilder.regex(regex);
}
return suggestionBuilder;
}
protected abstract SuggestionBuilder<T> innerFromXContent(QueryParseContext parseContext) throws IOException;
public SuggestionContext build(QueryShardContext context) throws IOException {
SuggestionContext suggestionContext = innerBuild(context);
return suggestionContext;
}
protected abstract SuggestionContext innerBuild(QueryShardContext context) throws IOException;
/**
* Transfers the text, prefix, regex, analyzer, fieldname, size and shard size settings from the
* original {@link SuggestionBuilder} to the target {@link SuggestionContext}
*/
protected void populateCommonFields(MapperService mapperService,
SuggestionSearchContext.SuggestionContext suggestionContext) throws IOException {
Objects.requireNonNull(fieldname, "fieldname must not be null");
MappedFieldType fieldType = mapperService.fullName(fieldname);
if (fieldType == null) {
throw new IllegalArgumentException("no mapping found for field [" + fieldname + "]");
} else if (analyzer == null) {
// no analyzer name passed in, so try the field's analyzer, or the default analyzer
if (fieldType.searchAnalyzer() == null) {
suggestionContext.setAnalyzer(mapperService.searchAnalyzer());
} else {
suggestionContext.setAnalyzer(fieldType.searchAnalyzer());
}
} else {
Analyzer luceneAnalyzer = mapperService.analysisService().analyzer(analyzer);
if (luceneAnalyzer == null) {
throw new IllegalArgumentException("analyzer [" + analyzer + "] doesn't exists");
}
suggestionContext.setAnalyzer(luceneAnalyzer);
}
suggestionContext.setField(fieldname);
if (size != null) {
suggestionContext.setSize(size);
}
if (shardSize != null) {
suggestionContext.setShardSize(shardSize);
} else {
// if no shard size is set in builder, use size (or at least 5)
suggestionContext.setShardSize(Math.max(suggestionContext.getSize(), 5));
}
if (text != null) {
suggestionContext.setText(BytesRefs.toBytesRef(text));
}
if (prefix != null) {
suggestionContext.setPrefix(BytesRefs.toBytesRef(prefix));
}
if (regex != null) {
suggestionContext.setRegex(BytesRefs.toBytesRef(regex));
}
if (text != null && prefix == null) {
suggestionContext.setPrefix(BytesRefs.toBytesRef(text));
} else if (text == null && prefix != null) {
suggestionContext.setText(BytesRefs.toBytesRef(prefix));
} else if (text == null && regex != null) {
suggestionContext.setText(BytesRefs.toBytesRef(regex));
}
}
private String getSuggesterName() {
//default impl returns the same as writeable name, but we keep the distinction between the two just to make sure
return getWriteableName();
}
/**
* get the {@link #field()} parameter
*/
public String field() {
return this.fieldname;
return this.field;
}
/**
@ -341,11 +196,154 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
return this.shardSize;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (text != null) {
builder.field(TEXT_FIELD.getPreferredName(), text);
}
if (prefix != null) {
builder.field(PREFIX_FIELD.getPreferredName(), prefix);
}
if (regex != null) {
builder.field(REGEX_FIELD.getPreferredName(), regex);
}
builder.startObject(getSuggesterName());
if (analyzer != null) {
builder.field(ANALYZER_FIELD.getPreferredName(), analyzer);
}
builder.field(FIELDNAME_FIELD.getPreferredName(), field);
if (size != null) {
builder.field(SIZE_FIELD.getPreferredName(), size);
}
if (shardSize != null) {
builder.field(SHARDSIZE_FIELD.getPreferredName(), shardSize);
}
builder = innerToXContent(builder, params);
builder.endObject();
return builder;
}
protected abstract XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException;
static SuggestionBuilder<?> fromXContent(QueryParseContext parseContext, Suggesters suggesters)
throws IOException {
XContentParser parser = parseContext.parser();
ParseFieldMatcher parsefieldMatcher = parseContext.parseFieldMatcher();
XContentParser.Token token;
String currentFieldName = null;
String suggestText = null;
String prefix = null;
String regex = null;
SuggestionBuilder<?> suggestionBuilder = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (parsefieldMatcher.match(currentFieldName, TEXT_FIELD)) {
suggestText = parser.text();
} else if (parsefieldMatcher.match(currentFieldName, PREFIX_FIELD)) {
prefix = parser.text();
} else if (parsefieldMatcher.match(currentFieldName, REGEX_FIELD)) {
regex = parser.text();
} else {
throw new ParsingException(parser.getTokenLocation(), "suggestion does not support [" + currentFieldName + "]");
}
} else if (token == XContentParser.Token.START_OBJECT) {
SuggestionBuilder<?> suggestParser = suggesters.getSuggestionPrototype(currentFieldName);
if (suggestParser == null) {
throw new ParsingException(parser.getTokenLocation(), "suggestion [" + currentFieldName + "] not supported");
}
suggestionBuilder = suggestParser.innerFromXContent(parseContext);
}
}
if (suggestionBuilder == null) {
throw new ElasticsearchParseException("missing suggestion object");
}
if (suggestText != null) {
suggestionBuilder.text(suggestText);
}
if (prefix != null) {
suggestionBuilder.prefix(prefix);
}
if (regex != null) {
suggestionBuilder.regex(regex);
}
return suggestionBuilder;
}
protected abstract SuggestionBuilder<T> innerFromXContent(QueryParseContext parseContext) throws IOException;
protected abstract SuggestionContext build(QueryShardContext context) throws IOException;
/**
* Transfers the text, prefix, regex, analyzer, field, size and shard size settings from the
* original {@link SuggestionBuilder} to the target {@link SuggestionContext}
*/
protected void populateCommonFields(MapperService mapperService,
SuggestionSearchContext.SuggestionContext suggestionContext) throws IOException {
Objects.requireNonNull(field, "field must not be null");
MappedFieldType fieldType = mapperService.fullName(field);
if (fieldType == null) {
throw new IllegalArgumentException("no mapping found for field [" + field + "]");
} else if (analyzer == null) {
// no analyzer name passed in, so try the field's analyzer, or the default analyzer
if (fieldType.searchAnalyzer() == null) {
suggestionContext.setAnalyzer(mapperService.searchAnalyzer());
} else {
suggestionContext.setAnalyzer(fieldType.searchAnalyzer());
}
} else {
Analyzer luceneAnalyzer = mapperService.analysisService().analyzer(analyzer);
if (luceneAnalyzer == null) {
throw new IllegalArgumentException("analyzer [" + analyzer + "] doesn't exists");
}
suggestionContext.setAnalyzer(luceneAnalyzer);
}
suggestionContext.setField(field);
if (size != null) {
suggestionContext.setSize(size);
}
if (shardSize != null) {
suggestionContext.setShardSize(shardSize);
} else {
// if no shard size is set in builder, use size (or at least 5)
suggestionContext.setShardSize(Math.max(suggestionContext.getSize(), 5));
}
if (text != null) {
suggestionContext.setText(BytesRefs.toBytesRef(text));
}
if (prefix != null) {
suggestionContext.setPrefix(BytesRefs.toBytesRef(prefix));
}
if (regex != null) {
suggestionContext.setRegex(BytesRefs.toBytesRef(regex));
}
if (text != null && prefix == null) {
suggestionContext.setPrefix(BytesRefs.toBytesRef(text));
} else if (text == null && prefix != null) {
suggestionContext.setText(BytesRefs.toBytesRef(prefix));
} else if (text == null && regex != null) {
suggestionContext.setText(BytesRefs.toBytesRef(regex));
}
}
private String getSuggesterName() {
//default impl returns the same as writeable name, but we keep the distinction between the two just to make sure
return getWriteableName();
}
@Override
public final T readFrom(StreamInput in) throws IOException {
String fieldname = in.readString();
T suggestionBuilder = doReadFrom(in, fieldname);
String field = in.readString();
T suggestionBuilder = doReadFrom(in, field);
suggestionBuilder.text = in.readOptionalString();
suggestionBuilder.prefix = in.readOptionalString();
suggestionBuilder.regex = in.readOptionalString();
@ -358,13 +356,13 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
/**
* Subclass should return a new instance, reading itself from the input string
* @param in the input string to read from
* @param fieldname the fieldname needed for ctor or concrete suggestion
* @param field the field needed for ctor or concrete suggestion
*/
protected abstract T doReadFrom(StreamInput in, String fieldname) throws IOException;
protected abstract T doReadFrom(StreamInput in, String field) throws IOException;
@Override
public final void writeTo(StreamOutput out) throws IOException {
out.writeString(fieldname);
out.writeString(field);
doWriteTo(out);
out.writeOptionalString(text);
out.writeOptionalString(prefix);
@ -389,7 +387,7 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
return Objects.equals(text, other.text()) &&
Objects.equals(prefix, other.prefix()) &&
Objects.equals(regex, other.regex()) &&
Objects.equals(fieldname, other.field()) &&
Objects.equals(field, other.field()) &&
Objects.equals(analyzer, other.analyzer()) &&
Objects.equals(size, other.size()) &&
Objects.equals(shardSize, other.shardSize()) &&
@ -403,7 +401,7 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
@Override
public final int hashCode() {
return Objects.hash(text, prefix, regex, fieldname, analyzer, size, shardSize, doHashCode());
return Objects.hash(text, prefix, regex, field, analyzer, size, shardSize, doHashCode());
}
/**

View File

@ -98,12 +98,12 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
protected BytesReference contextBytes = null;
protected List<String> payloadFields = Collections.emptyList();
public CompletionSuggestionBuilder(String fieldname) {
super(fieldname);
public CompletionSuggestionBuilder(String field) {
super(field);
}
/**
* internal copy constructor that copies over all class fields except for the fieldname which is
* internal copy constructor that copies over all class fields except for the field which is
* set to the one provided in the first argument
*/
private CompletionSuggestionBuilder(String fieldname, CompletionSuggestionBuilder in) {
@ -205,8 +205,8 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
super("_na_");
}
private InnerBuilder field(String fieldName) {
this.field = fieldName;
private InnerBuilder field(String field) {
this.field = field;
return this;
}
}
@ -242,13 +242,13 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
// now we should have field name, check and copy fields over to the suggestion builder we return
if (field == null) {
throw new ElasticsearchParseException(
"required field [" + SuggestUtils.Fields.FIELD.getPreferredName() + "] is missing");
"the required field option [" + SuggestUtils.Fields.FIELD.getPreferredName() + "] is missing");
}
return new CompletionSuggestionBuilder(field, builder);
}
@Override
protected SuggestionContext innerBuild(QueryShardContext context) throws IOException {
public SuggestionContext build(QueryShardContext context) throws IOException {
CompletionSuggestionContext suggestionContext = new CompletionSuggestionContext(context);
// copy over common settings to each suggestion builder
final MapperService mapperService = context.getMapperService();
@ -301,8 +301,8 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
}
@Override
public CompletionSuggestionBuilder doReadFrom(StreamInput in, String fieldname) throws IOException {
CompletionSuggestionBuilder completionSuggestionBuilder = new CompletionSuggestionBuilder(fieldname);
public CompletionSuggestionBuilder doReadFrom(StreamInput in, String field) throws IOException {
CompletionSuggestionBuilder completionSuggestionBuilder = new CompletionSuggestionBuilder(field);
if (in.readBoolean()) {
int numPayloadField = in.readVInt();
List<String> payloadFields = new ArrayList<>(numPayloadField);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search.suggest.phrase;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
@ -94,12 +95,12 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
private SmoothingModel model;
private final Map<String, List<CandidateGenerator>> generators = new HashMap<>();
public PhraseSuggestionBuilder(String fieldname) {
super(fieldname);
public PhraseSuggestionBuilder(String field) {
super(field);
}
/**
* internal copy constructor that copies over all class fields except for the fieldname which is
* internal copy constructor that copies over all class fields except for the field which is
* set to the one provided in the first argument
*/
private PhraseSuggestionBuilder(String fieldname, PhraseSuggestionBuilder in) {
@ -529,14 +530,15 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
// now we should have field name, check and copy fields over to the suggestion builder we return
if (fieldname == null) {
throw new ParsingException(parser.getTokenLocation(), "the required field option is missing");
throw new ElasticsearchParseException(
"the required field option [" + SuggestUtils.Fields.FIELD.getPreferredName() + "] is missing");
}
return new PhraseSuggestionBuilder(fieldname, tmpSuggestion);
}
@Override
public SuggestionContext innerBuild(QueryShardContext context) throws IOException {
public SuggestionContext build(QueryShardContext context) throws IOException {
PhraseSuggestionContext suggestionContext = new PhraseSuggestionContext(context);
MapperService mapperService = context.getMapperService();
// copy over common settings to each suggestion builder
@ -657,8 +659,8 @@ public class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionB
}
@Override
public PhraseSuggestionBuilder doReadFrom(StreamInput in, String fieldname) throws IOException {
PhraseSuggestionBuilder builder = new PhraseSuggestionBuilder(fieldname);
public PhraseSuggestionBuilder doReadFrom(StreamInput in, String field) throws IOException {
PhraseSuggestionBuilder builder = new PhraseSuggestionBuilder(field);
builder.maxErrors = in.readFloat();
builder.realWordErrorLikelihood = in.readFloat();
builder.confidence = in.readFloat();

View File

@ -25,6 +25,7 @@ import org.apache.lucene.search.spell.LevensteinDistance;
import org.apache.lucene.search.spell.LuceneLevenshteinDistance;
import org.apache.lucene.search.spell.NGramDistance;
import org.apache.lucene.search.spell.StringDistance;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput;
@ -36,6 +37,7 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.search.suggest.DirectSpellcheckerSettings;
import org.elasticsearch.search.suggest.SortBy;
import org.elasticsearch.search.suggest.SuggestUtils;
import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.elasticsearch.search.suggest.SuggestionSearchContext.SuggestionContext;
@ -82,15 +84,15 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
private int minWordLength = DEFAULT_MIN_WORD_LENGTH;
private float minDocFreq = DEFAULT_MIN_DOC_FREQ;
public TermSuggestionBuilder(String fieldname) {
super(fieldname);
public TermSuggestionBuilder(String field) {
super(field);
}
/**
* internal copy constructor that copies over all class field except fieldname.
* internal copy constructor that copies over all class field except field.
*/
private TermSuggestionBuilder(String fieldname, TermSuggestionBuilder in) {
super(fieldname, in);
private TermSuggestionBuilder(String field, TermSuggestionBuilder in) {
super(field, in);
suggestMode = in.suggestMode;
accuracy = in.accuracy;
sort = in.sort;
@ -409,13 +411,14 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
// now we should have field name, check and copy fields over to the suggestion builder we return
if (fieldname == null) {
throw new ParsingException(parser.getTokenLocation(), "the required field option is missing");
throw new ElasticsearchParseException(
"the required field option [" + SuggestUtils.Fields.FIELD.getPreferredName() + "] is missing");
}
return new TermSuggestionBuilder(fieldname, tmpSuggestion);
}
@Override
protected SuggestionContext innerBuild(QueryShardContext context) throws IOException {
public SuggestionContext build(QueryShardContext context) throws IOException {
TermSuggestionContext suggestionContext = new TermSuggestionContext(context);
// copy over common settings to each suggestion builder
populateCommonFields(context.getMapperService(), suggestionContext);
@ -454,8 +457,8 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
}
@Override
public TermSuggestionBuilder doReadFrom(StreamInput in, String fieldname) throws IOException {
TermSuggestionBuilder builder = new TermSuggestionBuilder(fieldname);
public TermSuggestionBuilder doReadFrom(StreamInput in, String field) throws IOException {
TermSuggestionBuilder builder = new TermSuggestionBuilder(field);
builder.suggestMode = SuggestMode.PROTOTYPE.readFrom(in);
builder.accuracy = in.readFloat();
builder.sort = SortBy.PROTOTYPE.readFrom(in);

View File

@ -119,8 +119,8 @@ public class CustomSuggesterSearchIT extends ESIntegTestCase {
}
@Override
public CustomSuggestionBuilder doReadFrom(StreamInput in, String fieldname) throws IOException {
return new CustomSuggestionBuilder(fieldname, in.readString());
public CustomSuggestionBuilder doReadFrom(StreamInput in, String field) throws IOException {
return new CustomSuggestionBuilder(field, in.readString());
}
@Override
@ -183,7 +183,7 @@ public class CustomSuggesterSearchIT extends ESIntegTestCase {
}
@Override
protected SuggestionContext innerBuild(QueryShardContext context) throws IOException {
public SuggestionContext build(QueryShardContext context) throws IOException {
Map<String, Object> options = new HashMap<>();
options.put(FIELDNAME_FIELD.getPreferredName(), field());
options.put(RANDOM_SUFFIX_FIELD.getPreferredName(), randomSuffix);