Refactored the term suggestion builder for the query refactoring effort
Added the term suggestion builder's serialization/deserialization and equals/hashCode methods.
This commit is contained in:
parent
b7e3323ded
commit
882cd5c6ee
|
@ -70,17 +70,9 @@ import static org.elasticsearch.ElasticsearchException.readStackTrace;
|
|||
|
||||
public abstract class StreamInput extends InputStream {
|
||||
|
||||
private final NamedWriteableRegistry namedWriteableRegistry;
|
||||
|
||||
private Version version = Version.CURRENT;
|
||||
|
||||
protected StreamInput() {
|
||||
this.namedWriteableRegistry = new NamedWriteableRegistry();
|
||||
}
|
||||
|
||||
protected StreamInput(NamedWriteableRegistry namedWriteableRegistry) {
|
||||
this.namedWriteableRegistry = namedWriteableRegistry;
|
||||
}
|
||||
protected StreamInput() { }
|
||||
|
||||
public Version getVersion() {
|
||||
return this.version;
|
||||
|
|
|
@ -55,6 +55,7 @@ import static org.elasticsearch.common.unit.TimeValue.parseTimeValue;
|
|||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
import static org.elasticsearch.search.suggest.SuggestBuilders.termSuggestion;
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.SuggestMode;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -262,7 +263,9 @@ public class RestSearchAction extends BaseRestHandler {
|
|||
int suggestSize = request.paramAsInt("suggest_size", 5);
|
||||
String suggestMode = request.param("suggest_mode");
|
||||
searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion(
|
||||
termSuggestion(suggestField).field(suggestField).text(suggestText).size(suggestSize).suggestMode(suggestMode)));
|
||||
termSuggestion(suggestField).field(suggestField)
|
||||
.text(suggestText).size(suggestSize)
|
||||
.suggestMode(SuggestMode.fromString(suggestMode))));
|
||||
modified = true;
|
||||
}
|
||||
return modified;
|
||||
|
|
|
@ -223,6 +223,10 @@ import org.elasticsearch.search.rescore.QueryRescorerBuilder;
|
|||
import org.elasticsearch.search.rescore.RescoreBuilder;
|
||||
import org.elasticsearch.search.suggest.Suggester;
|
||||
import org.elasticsearch.search.suggest.Suggesters;
|
||||
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
||||
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
|
||||
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
|
||||
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -365,6 +369,9 @@ public class SearchModule extends AbstractModule {
|
|||
|
||||
protected void configureSuggesters() {
|
||||
suggesters.bind(binder());
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, TermSuggestionBuilder.PROTOTYPE);
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, PhraseSuggestionBuilder.PROTOTYPE);
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, CompletionSuggestionBuilder.PROTOTYPE);
|
||||
}
|
||||
|
||||
protected void configureHighlighters() {
|
||||
|
|
|
@ -25,16 +25,30 @@ import org.apache.lucene.util.automaton.LevenshteinAutomata;
|
|||
|
||||
public class DirectSpellcheckerSettings {
|
||||
|
||||
private SuggestMode suggestMode = SuggestMode.SUGGEST_WHEN_NOT_IN_INDEX;
|
||||
private float accuracy = 0.5f;
|
||||
private Suggest.Suggestion.Sort sort = Suggest.Suggestion.Sort.SCORE;
|
||||
private StringDistance stringDistance = DirectSpellChecker.INTERNAL_LEVENSHTEIN;
|
||||
private int maxEdits = LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE;
|
||||
private int maxInspections = 5;
|
||||
private float maxTermFreq = 0.01f;
|
||||
private int prefixLength = 1;
|
||||
private int minWordLength = 4;
|
||||
private float minDocFreq = 0f;
|
||||
// NB: If this changes, make sure to change the default in TermBuilderSuggester
|
||||
public static SuggestMode DEFAULT_SUGGEST_MODE = SuggestMode.SUGGEST_WHEN_NOT_IN_INDEX;
|
||||
public static float DEFAULT_ACCURACY = 0.5f;
|
||||
// NB: If this changes, make sure to change the default in TermBuilderSuggester
|
||||
public static Suggest.Suggestion.Sort DEFAULT_SORT = Suggest.Suggestion.Sort.SCORE;
|
||||
// NB: If this changes, make sure to change the default in TermBuilderSuggester
|
||||
public static StringDistance DEFAULT_STRING_DISTANCE = DirectSpellChecker.INTERNAL_LEVENSHTEIN;
|
||||
public static int DEFAULT_MAX_EDITS = LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE;
|
||||
public static int DEFAULT_MAX_INSPECTIONS = 5;
|
||||
public static float DEFAULT_MAX_TERM_FREQ = 0.01f;
|
||||
public static int DEFAULT_PREFIX_LENGTH = 1;
|
||||
public static int DEFAULT_MIN_WORD_LENGTH = 4;
|
||||
public static float DEFAULT_MIN_DOC_FREQ = 0f;
|
||||
|
||||
private SuggestMode suggestMode = DEFAULT_SUGGEST_MODE;
|
||||
private float accuracy = DEFAULT_ACCURACY;
|
||||
private Suggest.Suggestion.Sort sort = DEFAULT_SORT;
|
||||
private StringDistance stringDistance = DEFAULT_STRING_DISTANCE;
|
||||
private int maxEdits = DEFAULT_MAX_EDITS;
|
||||
private int maxInspections = DEFAULT_MAX_INSPECTIONS;
|
||||
private float maxTermFreq = DEFAULT_MAX_TERM_FREQ;
|
||||
private int prefixLength = DEFAULT_PREFIX_LENGTH;
|
||||
private int minWordLength = DEFAULT_MIN_WORD_LENGTH;
|
||||
private float minDocFreq = DEFAULT_MIN_DOC_FREQ;
|
||||
|
||||
public SuggestMode suggestMode() {
|
||||
return suggestMode;
|
||||
|
|
|
@ -19,32 +19,30 @@
|
|||
package org.elasticsearch.search.suggest;
|
||||
|
||||
import org.elasticsearch.action.support.ToXContentToBytes;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Defines how to perform suggesting. This builders allows a number of global options to be specified and
|
||||
* an arbitrary number of {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder} instances.
|
||||
* an arbitrary number of {@link SuggestionBuilder} instances.
|
||||
* <p>
|
||||
* Suggesting works by suggesting terms that appear in the suggest text that are similar compared to the terms in
|
||||
* provided text. These spelling suggestions are based on several options described in this class.
|
||||
* Suggesting works by suggesting terms/phrases that appear in the suggest text that are similar compared
|
||||
* to the terms in provided text. These suggestions are based on several options described in this class.
|
||||
*/
|
||||
public class SuggestBuilder extends ToXContentToBytes {
|
||||
public class SuggestBuilder extends ToXContentToBytes implements Writeable<SuggestBuilder> {
|
||||
|
||||
private final String name;
|
||||
private String globalText;
|
||||
|
||||
private final List<SuggestionBuilder<?>> suggestions = new ArrayList<>();
|
||||
|
||||
public SuggestBuilder() {
|
||||
this.name = null;
|
||||
}
|
||||
|
||||
public SuggestBuilder(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +52,7 @@ public class SuggestBuilder extends ToXContentToBytes {
|
|||
* The suggest text gets analyzed by the suggest analyzer or the suggest field search analyzer.
|
||||
* For each analyzed token, suggested terms are suggested if possible.
|
||||
*/
|
||||
public SuggestBuilder setText(String globalText) {
|
||||
public SuggestBuilder setText(@Nullable String globalText) {
|
||||
this.globalText = globalText;
|
||||
return this;
|
||||
}
|
||||
|
@ -77,12 +75,7 @@ public class SuggestBuilder extends ToXContentToBytes {
|
|||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if(name == null) {
|
||||
builder.startObject();
|
||||
} else {
|
||||
builder.startObject(name);
|
||||
}
|
||||
|
||||
if (globalText != null) {
|
||||
builder.field("text", globalText);
|
||||
}
|
||||
|
@ -92,4 +85,45 @@ public class SuggestBuilder extends ToXContentToBytes {
|
|||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestBuilder readFrom(StreamInput in) throws IOException {
|
||||
final SuggestBuilder builder = new SuggestBuilder();
|
||||
builder.globalText = in.readOptionalString();
|
||||
final int size = in.readVInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
builder.suggestions.add(in.readSuggestion());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeOptionalString(globalText);
|
||||
final int size = suggestions.size();
|
||||
out.writeVInt(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
out.writeSuggestion(suggestions.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (other == null || getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
SuggestBuilder o = (SuggestBuilder)other;
|
||||
return Objects.equals(globalText, o.globalText) &&
|
||||
Objects.equals(suggestions, o.suggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(globalText, suggestions);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
|
|||
protected static final ParseField SHARDSIZE_FIELD = new ParseField("shard_size");
|
||||
|
||||
public SuggestionBuilder(String name) {
|
||||
Objects.requireNonNull(name, "Suggester 'name' cannot be null");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
@ -296,4 +297,5 @@ public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends
|
|||
* HashCode for the subclass of {@link SuggestionBuilder} to implement.
|
||||
*/
|
||||
protected abstract int doHashCode();
|
||||
|
||||
}
|
|
@ -49,9 +49,11 @@ import java.util.Set;
|
|||
*/
|
||||
public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSuggestionBuilder> {
|
||||
|
||||
public static final CompletionSuggestionBuilder PROTOTYPE = new CompletionSuggestionBuilder("_na_"); // name doesn't matter
|
||||
final static String SUGGESTION_NAME = "completion";
|
||||
static final ParseField PAYLOAD_FIELD = new ParseField("payload");
|
||||
static final ParseField CONTEXTS_FIELD = new ParseField("contexts", "context");
|
||||
|
||||
private FuzzyOptionsBuilder fuzzyOptionsBuilder;
|
||||
private RegexOptionsBuilder regexOptionsBuilder;
|
||||
private final Map<String, List<ToXContent>> queryContexts = new HashMap<>();
|
||||
|
|
|
@ -16,13 +16,26 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.search.suggest.term;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_ACCURACY;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_MAX_EDITS;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_MAX_INSPECTIONS;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_MAX_TERM_FREQ;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_MIN_DOC_FREQ;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_MIN_WORD_LENGTH;
|
||||
import static org.elasticsearch.search.suggest.DirectSpellcheckerSettings.DEFAULT_PREFIX_LENGTH;
|
||||
|
||||
/**
|
||||
* Defines the actual suggest command. Each command uses the global options
|
||||
|
@ -31,18 +44,19 @@ import java.io.IOException;
|
|||
*/
|
||||
public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuilder> {
|
||||
|
||||
public static final TermSuggestionBuilder PROTOTYPE = new TermSuggestionBuilder("_na_"); // name doesn't matter
|
||||
static final String SUGGESTION_NAME = "term";
|
||||
|
||||
private String suggestMode;
|
||||
private Float accuracy;
|
||||
private String sort;
|
||||
private String stringDistance;
|
||||
private Integer maxEdits;
|
||||
private Integer maxInspections;
|
||||
private Float maxTermFreq;
|
||||
private Integer prefixLength;
|
||||
private Integer minWordLength;
|
||||
private Float minDocFreq;
|
||||
private SuggestMode suggestMode = SuggestMode.MISSING;
|
||||
private Float accuracy = DEFAULT_ACCURACY;
|
||||
private SortBy sort = SortBy.SCORE;
|
||||
private StringDistanceImpl stringDistance = StringDistanceImpl.INTERNAL;
|
||||
private Integer maxEdits = DEFAULT_MAX_EDITS;
|
||||
private Integer maxInspections = DEFAULT_MAX_INSPECTIONS;
|
||||
private Float maxTermFreq = DEFAULT_MAX_TERM_FREQ;
|
||||
private Integer prefixLength = DEFAULT_PREFIX_LENGTH;
|
||||
private Integer minWordLength = DEFAULT_MIN_WORD_LENGTH;
|
||||
private Float minDocFreq = DEFAULT_MIN_DOC_FREQ;
|
||||
|
||||
/**
|
||||
* @param name
|
||||
|
@ -65,11 +79,19 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* tokens in the suggest text.
|
||||
* </ol>
|
||||
*/
|
||||
public TermSuggestionBuilder suggestMode(String suggestMode) {
|
||||
public TermSuggestionBuilder suggestMode(SuggestMode suggestMode) {
|
||||
Objects.requireNonNull(suggestMode, "suggestMode must not be null");
|
||||
this.suggestMode = suggestMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the suggest mode setting.
|
||||
*/
|
||||
public SuggestMode suggestMode() {
|
||||
return suggestMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* s how similar the suggested terms at least need to be compared to the
|
||||
* original suggest text tokens. A value between 0 and 1 can be specified.
|
||||
|
@ -78,11 +100,21 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* <p>
|
||||
* Default is <tt>0.5</tt>
|
||||
*/
|
||||
public TermSuggestionBuilder setAccuracy(float accuracy) {
|
||||
public TermSuggestionBuilder accuracy(float accuracy) {
|
||||
if (accuracy < 0.0f || accuracy > 1.0f) {
|
||||
throw new IllegalArgumentException("accuracy must be between 0 and 1");
|
||||
}
|
||||
this.accuracy = accuracy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the accuracy setting.
|
||||
*/
|
||||
public Float accuracy() {
|
||||
return accuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how to sort the suggest terms per suggest text token. Two possible
|
||||
* values:
|
||||
|
@ -90,19 +122,27 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* <li><code>score</code> - Sort should first be based on score, then
|
||||
* document frequency and then the term itself.
|
||||
* <li><code>frequency</code> - Sort should first be based on document
|
||||
* frequency, then scotr and then the term itself.
|
||||
* frequency, then score and then the term itself.
|
||||
* </ol>
|
||||
* <p>
|
||||
* What the score is depends on the suggester being used.
|
||||
*/
|
||||
public TermSuggestionBuilder sort(String sort) {
|
||||
public TermSuggestionBuilder sort(SortBy sort) {
|
||||
Objects.requireNonNull(sort, "sort must not be null");
|
||||
this.sort = sort;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sort setting.
|
||||
*/
|
||||
public SortBy sort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets what string distance implementation to use for comparing how similar
|
||||
* suggested terms are. Four possible values can be specified:
|
||||
* suggested terms are. Five possible values can be specified:
|
||||
* <ol>
|
||||
* <li><code>internal</code> - This is the default and is based on
|
||||
* <code>damerau_levenshtein</code>, but highly optimized for comparing
|
||||
|
@ -117,32 +157,60 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* n-grams.
|
||||
* </ol>
|
||||
*/
|
||||
public TermSuggestionBuilder stringDistance(String stringDistance) {
|
||||
public TermSuggestionBuilder stringDistance(StringDistanceImpl stringDistance) {
|
||||
Objects.requireNonNull(stringDistance, "stringDistance must not be null");
|
||||
this.stringDistance = stringDistance;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string distance implementation setting.
|
||||
*/
|
||||
public StringDistanceImpl stringDistance() {
|
||||
return stringDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum edit distance candidate suggestions can have in order to
|
||||
* be considered as a suggestion. Can only be a value between 1 and 2. Any
|
||||
* other value result in an bad request error being thrown. Defaults to
|
||||
* <tt>2</tt>.
|
||||
*/
|
||||
public TermSuggestionBuilder maxEdits(Integer maxEdits) {
|
||||
public TermSuggestionBuilder maxEdits(int maxEdits) {
|
||||
if (maxEdits < 1 || maxEdits > 2) {
|
||||
throw new IllegalArgumentException("maxEdits must be between 1 and 2");
|
||||
}
|
||||
this.maxEdits = maxEdits;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum edit distance setting.
|
||||
*/
|
||||
public Integer maxEdits() {
|
||||
return maxEdits;
|
||||
}
|
||||
|
||||
/**
|
||||
* A factor that is used to multiply with the size in order to inspect more
|
||||
* candidate suggestions. Can improve accuracy at the cost of performance.
|
||||
* Defaults to <tt>5</tt>.
|
||||
*/
|
||||
public TermSuggestionBuilder maxInspections(Integer maxInspections) {
|
||||
public TermSuggestionBuilder maxInspections(int maxInspections) {
|
||||
if (maxInspections < 0) {
|
||||
throw new IllegalArgumentException("maxInspections must be positive");
|
||||
}
|
||||
this.maxInspections = maxInspections;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the factor for inspecting more candidate suggestions setting.
|
||||
*/
|
||||
public Integer maxInspections() {
|
||||
return maxInspections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a maximum threshold in number of documents a suggest text token can
|
||||
* exist in order to be corrected. Can be a relative percentage number (e.g
|
||||
|
@ -155,10 +223,23 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* also improves the suggest performance.
|
||||
*/
|
||||
public TermSuggestionBuilder maxTermFreq(float maxTermFreq) {
|
||||
if (maxTermFreq < 0.0f) {
|
||||
throw new IllegalArgumentException("maxTermFreq must be positive");
|
||||
}
|
||||
if (maxTermFreq > 1.0f && maxTermFreq != Math.floor(maxTermFreq)) {
|
||||
throw new IllegalArgumentException("if maxTermFreq is greater than 1, it must not be a fraction");
|
||||
}
|
||||
this.maxTermFreq = maxTermFreq;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum term frequency threshold setting.
|
||||
*/
|
||||
public Float maxTermFreq() {
|
||||
return maxTermFreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of minimal prefix characters that must match in order be
|
||||
* a candidate suggestion. Defaults to 1. Increasing this number improves
|
||||
|
@ -166,19 +247,39 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* terms.
|
||||
*/
|
||||
public TermSuggestionBuilder prefixLength(int prefixLength) {
|
||||
if (prefixLength < 0) {
|
||||
throw new IllegalArgumentException("prefixLength must be positive");
|
||||
}
|
||||
this.prefixLength = prefixLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum prefix length that must match setting.
|
||||
*/
|
||||
public Integer prefixLength() {
|
||||
return prefixLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum length a suggest text term must have in order to be
|
||||
* corrected. Defaults to <tt>4</tt>.
|
||||
*/
|
||||
public TermSuggestionBuilder minWordLength(int minWordLength) {
|
||||
if (minWordLength < 1) {
|
||||
throw new IllegalArgumentException("minWordLength must be greater or equal to 1");
|
||||
}
|
||||
this.minWordLength = minWordLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimum length of a text term to be corrected setting.
|
||||
*/
|
||||
public Integer minWordLength() {
|
||||
return minWordLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a minimal threshold in number of documents a suggested term should
|
||||
* appear in. This can be specified as an absolute number or as a relative
|
||||
|
@ -187,10 +288,24 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
* value higher than 1 is specified then the number cannot be fractional.
|
||||
*/
|
||||
public TermSuggestionBuilder minDocFreq(float minDocFreq) {
|
||||
if (minDocFreq < 0.0f) {
|
||||
throw new IllegalArgumentException("minDocFreq must be positive");
|
||||
}
|
||||
if (minDocFreq > 1.0f && minDocFreq != Math.floor(minDocFreq)) {
|
||||
throw new IllegalArgumentException("if minDocFreq is greater than 1, it must not be a fraction");
|
||||
}
|
||||
this.minDocFreq = minDocFreq;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the minimal threshold for the frequency of a term appearing in the
|
||||
* document set setting.
|
||||
*/
|
||||
public Float minDocFreq() {
|
||||
return minDocFreq;
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
if (suggestMode != null) {
|
||||
|
@ -233,25 +348,149 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
|
|||
|
||||
@Override
|
||||
public void doWriteTo(StreamOutput out) throws IOException {
|
||||
// NORELEASE
|
||||
throw new UnsupportedOperationException();
|
||||
suggestMode.writeTo(out);
|
||||
out.writeFloat(accuracy);
|
||||
sort.writeTo(out);
|
||||
stringDistance.writeTo(out);
|
||||
out.writeVInt(maxEdits);
|
||||
out.writeVInt(maxInspections);
|
||||
out.writeFloat(maxTermFreq);
|
||||
out.writeVInt(prefixLength);
|
||||
out.writeVInt(minWordLength);
|
||||
out.writeFloat(minDocFreq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TermSuggestionBuilder doReadFrom(StreamInput in, String name) throws IOException {
|
||||
// NORELEASE
|
||||
throw new UnsupportedOperationException();
|
||||
TermSuggestionBuilder builder = new TermSuggestionBuilder(name);
|
||||
builder.suggestMode = SuggestMode.PROTOTYPE.readFrom(in);
|
||||
builder.accuracy = in.readFloat();
|
||||
builder.sort = SortBy.PROTOTYPE.readFrom(in);
|
||||
builder.stringDistance = StringDistanceImpl.PROTOTYPE.readFrom(in);
|
||||
builder.maxEdits = in.readVInt();
|
||||
builder.maxInspections = in.readVInt();
|
||||
builder.maxTermFreq = in.readFloat();
|
||||
builder.prefixLength = in.readVInt();
|
||||
builder.minWordLength = in.readVInt();
|
||||
builder.minDocFreq = in.readFloat();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(TermSuggestionBuilder other) {
|
||||
// NORELEASE
|
||||
return false;
|
||||
return Objects.equals(suggestMode, other.suggestMode) &&
|
||||
Objects.equals(accuracy, other.accuracy) &&
|
||||
Objects.equals(sort, other.sort) &&
|
||||
Objects.equals(stringDistance, other.stringDistance) &&
|
||||
Objects.equals(maxEdits, other.maxEdits) &&
|
||||
Objects.equals(maxInspections, other.maxInspections) &&
|
||||
Objects.equals(maxTermFreq, other.maxTermFreq) &&
|
||||
Objects.equals(prefixLength, other.prefixLength) &&
|
||||
Objects.equals(minWordLength, other.minWordLength) &&
|
||||
Objects.equals(minDocFreq, other.minDocFreq);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
// NORELEASE
|
||||
return 0;
|
||||
return Objects.hash(suggestMode, accuracy, sort, stringDistance, maxEdits, maxInspections,
|
||||
maxTermFreq, prefixLength, minWordLength, minDocFreq);
|
||||
}
|
||||
|
||||
|
||||
/** An enum representing the valid suggest modes. */
|
||||
public enum SuggestMode implements Writeable<SuggestMode> {
|
||||
/** Only suggest terms in the suggest text that aren't in the index. This is the default. */
|
||||
MISSING,
|
||||
/** Only suggest terms that occur in more docs then the original suggest text term. */
|
||||
POPULAR,
|
||||
/** Suggest any matching suggest terms based on tokens in the suggest text. */
|
||||
ALWAYS;
|
||||
|
||||
protected static SuggestMode PROTOTYPE = MISSING;
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeVInt(ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuggestMode readFrom(final StreamInput in) throws IOException {
|
||||
int ordinal = in.readVInt();
|
||||
if (ordinal < 0 || ordinal >= values().length) {
|
||||
throw new IOException("Unknown SuggestMode ordinal [" + ordinal + "]");
|
||||
}
|
||||
return values()[ordinal];
|
||||
}
|
||||
|
||||
public static SuggestMode fromString(final String str) {
|
||||
Objects.requireNonNull(str, "Input string is null");
|
||||
return valueOf(str.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
/** An enum representing the valid sorting options */
|
||||
public enum SortBy implements Writeable<SortBy> {
|
||||
/** Sort should first be based on score, then document frequency and then the term itself. */
|
||||
SCORE,
|
||||
/** Sort should first be based on document frequency, then score and then the term itself. */
|
||||
FREQUENCY;
|
||||
|
||||
protected static SortBy PROTOTYPE = SCORE;
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeVInt(ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortBy readFrom(final StreamInput in) throws IOException {
|
||||
int ordinal = in.readVInt();
|
||||
if (ordinal < 0 || ordinal >= values().length) {
|
||||
throw new IOException("Unknown SortBy ordinal [" + ordinal + "]");
|
||||
}
|
||||
return values()[ordinal];
|
||||
}
|
||||
|
||||
public static SortBy fromString(final String str) {
|
||||
Objects.requireNonNull(str, "Input string is null");
|
||||
return valueOf(str.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
/** An enum representing the valid string edit distance algorithms for determining suggestions. */
|
||||
public enum StringDistanceImpl implements Writeable<StringDistanceImpl> {
|
||||
/** This is the default and is based on <code>damerau_levenshtein</code>, but highly optimized
|
||||
* for comparing string distance for terms inside the index. */
|
||||
INTERNAL,
|
||||
/** String distance algorithm based on Damerau-Levenshtein algorithm. */
|
||||
DAMERAU_LEVENSHTEIN,
|
||||
/** String distance algorithm based on Levenstein edit distance algorithm. */
|
||||
LEVENSTEIN,
|
||||
/** String distance algorithm based on Jaro-Winkler algorithm. */
|
||||
JAROWINKLER,
|
||||
/** String distance algorithm based on character n-grams. */
|
||||
NGRAM;
|
||||
|
||||
protected static StringDistanceImpl PROTOTYPE = INTERNAL;
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeVInt(ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringDistanceImpl readFrom(final StreamInput in) throws IOException {
|
||||
int ordinal = in.readVInt();
|
||||
if (ordinal < 0 || ordinal >= values().length) {
|
||||
throw new IOException("Unknown StringDistanceImpl ordinal [" + ordinal + "]");
|
||||
}
|
||||
return values()[ordinal];
|
||||
}
|
||||
|
||||
public static StringDistanceImpl fromString(final String str) {
|
||||
Objects.requireNonNull(str, "Input string is null");
|
||||
return valueOf(str.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.io.stream;
|
||||
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Abstract class offering base functionality for testing @{link Writeable} enums.
|
||||
*/
|
||||
public abstract class AbstractWriteableEnumTestCase extends ESTestCase {
|
||||
|
||||
/**
|
||||
* Test that the ordinals for the enum are consistent (i.e. the order hasn't changed)
|
||||
* because writing an enum to a stream often uses the ordinal value.
|
||||
*/
|
||||
public abstract void testValidOrdinals();
|
||||
|
||||
/**
|
||||
* Test that the conversion from a string to enum is correct.
|
||||
*/
|
||||
public abstract void testFromString();
|
||||
|
||||
/**
|
||||
* Test that the correct enum value is produced from the serialized value in the {@link StreamInput}.
|
||||
*/
|
||||
public abstract void testReadFrom() throws IOException;
|
||||
|
||||
/**
|
||||
* Test that the correct serialized value is produced from the {@link StreamOutput}.
|
||||
*/
|
||||
public abstract void testWriteTo() throws IOException;
|
||||
|
||||
// a convenience method for testing the write of a writeable enum
|
||||
protected static void assertWriteToStream(final Writeable writeableEnum, final int ordinal) throws IOException {
|
||||
try (BytesStreamOutput out = new BytesStreamOutput()) {
|
||||
writeableEnum.writeTo(out);
|
||||
try (StreamInput in = StreamInput.wrap(out.bytes())) {
|
||||
assertThat(in.readVInt(), equalTo(ordinal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// a convenience method for testing the read of a writeable enum
|
||||
protected static <T extends Writeable<T>> void assertReadFromStream(final int ordinal, final Writeable<T> expected) throws IOException {
|
||||
try (BytesStreamOutput out = new BytesStreamOutput()) {
|
||||
out.writeVInt(ordinal);
|
||||
try (StreamInput in = StreamInput.wrap(out.bytes())) {
|
||||
assertThat(expected.readFrom(in), equalTo(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,7 +23,9 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
|||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
|
||||
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
|
||||
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -46,7 +48,9 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
|
|||
@BeforeClass
|
||||
public static void init() {
|
||||
namedWriteableRegistry = new NamedWriteableRegistry();
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, TermSuggestionBuilder.PROTOTYPE);
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, PhraseSuggestionBuilder.PROTOTYPE);
|
||||
namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, CompletionSuggestionBuilder.PROTOTYPE);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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.search.suggest.term;
|
||||
|
||||
import org.elasticsearch.common.io.stream.AbstractWriteableEnumTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.SortBy;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Test the {@link SortBy} enum.
|
||||
*/
|
||||
public class SortByTests extends AbstractWriteableEnumTestCase {
|
||||
|
||||
@Override
|
||||
public void testValidOrdinals() {
|
||||
assertThat(SortBy.SCORE.ordinal(), equalTo(0));
|
||||
assertThat(SortBy.FREQUENCY.ordinal(), equalTo(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFromString() {
|
||||
assertThat(SortBy.fromString("score"), equalTo(SortBy.SCORE));
|
||||
assertThat(SortBy.fromString("frequency"), equalTo(SortBy.FREQUENCY));
|
||||
final String doesntExist = "doesnt_exist";
|
||||
try {
|
||||
SortBy.fromString(doesntExist);
|
||||
fail("SortBy should not have an element " + doesntExist);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
SortBy.fromString(null);
|
||||
fail("SortBy.fromString on a null value should throw an exception.");
|
||||
} catch (NullPointerException e) {
|
||||
assertThat(e.getMessage(), equalTo("Input string is null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testWriteTo() throws IOException {
|
||||
assertWriteToStream(SortBy.SCORE, 0);
|
||||
assertWriteToStream(SortBy.FREQUENCY, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testReadFrom() throws IOException {
|
||||
assertReadFromStream(0, SortBy.SCORE);
|
||||
assertReadFromStream(1, SortBy.FREQUENCY);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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.search.suggest.term;
|
||||
|
||||
import org.elasticsearch.common.io.stream.AbstractWriteableEnumTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.StringDistanceImpl;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Test for the {@link StringDistanceImpl} enum.
|
||||
*/
|
||||
public class StringDistanceImplTests extends AbstractWriteableEnumTestCase {
|
||||
|
||||
@Override
|
||||
public void testValidOrdinals() {
|
||||
assertThat(StringDistanceImpl.INTERNAL.ordinal(), equalTo(0));
|
||||
assertThat(StringDistanceImpl.DAMERAU_LEVENSHTEIN.ordinal(), equalTo(1));
|
||||
assertThat(StringDistanceImpl.LEVENSTEIN.ordinal(), equalTo(2));
|
||||
assertThat(StringDistanceImpl.JAROWINKLER.ordinal(), equalTo(3));
|
||||
assertThat(StringDistanceImpl.NGRAM.ordinal(), equalTo(4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFromString() {
|
||||
assertThat(StringDistanceImpl.fromString("internal"), equalTo(StringDistanceImpl.INTERNAL));
|
||||
assertThat(StringDistanceImpl.fromString("damerau_levenshtein"), equalTo(StringDistanceImpl.DAMERAU_LEVENSHTEIN));
|
||||
assertThat(StringDistanceImpl.fromString("levenstein"), equalTo(StringDistanceImpl.LEVENSTEIN));
|
||||
assertThat(StringDistanceImpl.fromString("jarowinkler"), equalTo(StringDistanceImpl.JAROWINKLER));
|
||||
assertThat(StringDistanceImpl.fromString("ngram"), equalTo(StringDistanceImpl.NGRAM));
|
||||
final String doesntExist = "doesnt_exist";
|
||||
try {
|
||||
StringDistanceImpl.fromString(doesntExist);
|
||||
fail("StringDistanceImpl should not have an element " + doesntExist);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
StringDistanceImpl.fromString(null);
|
||||
fail("StringDistanceImpl.fromString on a null value should throw an exception.");
|
||||
} catch (NullPointerException e) {
|
||||
assertThat(e.getMessage(), equalTo("Input string is null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testWriteTo() throws IOException {
|
||||
assertWriteToStream(StringDistanceImpl.INTERNAL, 0);
|
||||
assertWriteToStream(StringDistanceImpl.DAMERAU_LEVENSHTEIN, 1);
|
||||
assertWriteToStream(StringDistanceImpl.LEVENSTEIN, 2);
|
||||
assertWriteToStream(StringDistanceImpl.JAROWINKLER, 3);
|
||||
assertWriteToStream(StringDistanceImpl.NGRAM, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testReadFrom() throws IOException {
|
||||
assertReadFromStream(0, StringDistanceImpl.INTERNAL);
|
||||
assertReadFromStream(1, StringDistanceImpl.DAMERAU_LEVENSHTEIN);
|
||||
assertReadFromStream(2, StringDistanceImpl.LEVENSTEIN);
|
||||
assertReadFromStream(3, StringDistanceImpl.JAROWINKLER);
|
||||
assertReadFromStream(4, StringDistanceImpl.NGRAM);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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.search.suggest.term;
|
||||
|
||||
import org.elasticsearch.common.io.stream.AbstractWriteableEnumTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.SuggestMode;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
/**
|
||||
* Test the {@link SuggestMode} enum.
|
||||
*/
|
||||
public class SuggestModeTests extends AbstractWriteableEnumTestCase {
|
||||
|
||||
@Override
|
||||
public void testValidOrdinals() {
|
||||
assertThat(SuggestMode.MISSING.ordinal(), equalTo(0));
|
||||
assertThat(SuggestMode.POPULAR.ordinal(), equalTo(1));
|
||||
assertThat(SuggestMode.ALWAYS.ordinal(), equalTo(2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFromString() {
|
||||
assertThat(SuggestMode.fromString("missing"), equalTo(SuggestMode.MISSING));
|
||||
assertThat(SuggestMode.fromString("popular"), equalTo(SuggestMode.POPULAR));
|
||||
assertThat(SuggestMode.fromString("always"), equalTo(SuggestMode.ALWAYS));
|
||||
final String doesntExist = "doesnt_exist";
|
||||
try {
|
||||
SuggestMode.fromString(doesntExist);
|
||||
fail("SuggestMode should not have an element " + doesntExist);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
SuggestMode.fromString(null);
|
||||
fail("SuggestMode.fromString on a null value should throw an exception.");
|
||||
} catch (NullPointerException e) {
|
||||
assertThat(e.getMessage(), equalTo("Input string is null"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testWriteTo() throws IOException {
|
||||
assertWriteToStream(SuggestMode.MISSING, 0);
|
||||
assertWriteToStream(SuggestMode.POPULAR, 1);
|
||||
assertWriteToStream(SuggestMode.ALWAYS, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testReadFrom() throws IOException {
|
||||
assertReadFromStream(0, SuggestMode.MISSING);
|
||||
assertReadFromStream(1, SuggestMode.POPULAR);
|
||||
assertReadFromStream(2, SuggestMode.ALWAYS);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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.search.suggest.term;
|
||||
|
||||
import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.SortBy;
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.StringDistanceImpl;
|
||||
import static org.elasticsearch.search.suggest.term.TermSuggestionBuilder.SuggestMode;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
* Test the {@link TermSuggestionBuilder} class.
|
||||
*/
|
||||
public class TermSuggestionBuilderTests extends AbstractSuggestionBuilderTestCase<TermSuggestionBuilder> {
|
||||
|
||||
@Override
|
||||
protected TermSuggestionBuilder randomSuggestionBuilder() {
|
||||
TermSuggestionBuilder testBuilder = new TermSuggestionBuilder(randomAsciiOfLength(10));
|
||||
maybeSet(testBuilder::suggestMode, randomSuggestMode());
|
||||
maybeSet(testBuilder::accuracy, randomFloat());
|
||||
maybeSet(testBuilder::sort, randomSort());
|
||||
maybeSet(testBuilder::stringDistance, randomStringDistance());
|
||||
maybeSet(testBuilder::maxEdits, randomIntBetween(1, 2));
|
||||
maybeSet(testBuilder::maxInspections, randomInt(Integer.MAX_VALUE));
|
||||
maybeSet(testBuilder::maxTermFreq, randomFloat());
|
||||
maybeSet(testBuilder::prefixLength, randomInt(Integer.MAX_VALUE));
|
||||
maybeSet(testBuilder::minWordLength, randomInt(Integer.MAX_VALUE));
|
||||
maybeSet(testBuilder::minDocFreq, randomFloat());
|
||||
return testBuilder;
|
||||
}
|
||||
|
||||
private SuggestMode randomSuggestMode() {
|
||||
final int randomVal = randomIntBetween(0, 2);
|
||||
switch (randomVal) {
|
||||
case 0: return SuggestMode.MISSING;
|
||||
case 1: return SuggestMode.POPULAR;
|
||||
case 2: return SuggestMode.ALWAYS;
|
||||
default: throw new IllegalArgumentException("No suggest mode with an ordinal of " + randomVal);
|
||||
}
|
||||
}
|
||||
|
||||
private SortBy randomSort() {
|
||||
int randomVal = randomIntBetween(0, 1);
|
||||
switch (randomVal) {
|
||||
case 0: return SortBy.SCORE;
|
||||
case 1: return SortBy.FREQUENCY;
|
||||
default: throw new IllegalArgumentException("No sort mode with an ordinal of " + randomVal);
|
||||
}
|
||||
}
|
||||
|
||||
private StringDistanceImpl randomStringDistance() {
|
||||
int randomVal = randomIntBetween(0, 4);
|
||||
switch (randomVal) {
|
||||
case 0: return StringDistanceImpl.INTERNAL;
|
||||
case 1: return StringDistanceImpl.DAMERAU_LEVENSHTEIN;
|
||||
case 2: return StringDistanceImpl.LEVENSTEIN;
|
||||
case 3: return StringDistanceImpl.JAROWINKLER;
|
||||
case 4: return StringDistanceImpl.NGRAM;
|
||||
default: throw new IllegalArgumentException("No string distance algorithm with an ordinal of " + randomVal);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mutateSpecificParameters(TermSuggestionBuilder builder) throws IOException {
|
||||
switch (randomIntBetween(0, 9)) {
|
||||
case 0:
|
||||
builder.suggestMode(randomValueOtherThan(builder.suggestMode(), () -> randomSuggestMode()));
|
||||
break;
|
||||
case 1:
|
||||
builder.accuracy(randomValueOtherThan(builder.accuracy(), () -> randomFloat()));
|
||||
break;
|
||||
case 2:
|
||||
builder.sort(randomValueOtherThan(builder.sort(), () -> randomSort()));
|
||||
break;
|
||||
case 3:
|
||||
builder.stringDistance(randomValueOtherThan(builder.stringDistance(), () -> randomStringDistance()));
|
||||
break;
|
||||
case 4:
|
||||
builder.maxEdits(randomValueOtherThan(builder.maxEdits(), () -> randomIntBetween(1, 2)));
|
||||
break;
|
||||
case 5:
|
||||
builder.maxInspections(randomValueOtherThan(builder.maxInspections(), () -> randomInt(Integer.MAX_VALUE)));
|
||||
break;
|
||||
case 6:
|
||||
builder.maxTermFreq(randomValueOtherThan(builder.maxTermFreq(), () -> randomFloat()));
|
||||
break;
|
||||
case 7:
|
||||
builder.prefixLength(randomValueOtherThan(builder.prefixLength(), () -> randomInt(Integer.MAX_VALUE)));
|
||||
break;
|
||||
case 8:
|
||||
builder.minWordLength(randomValueOtherThan(builder.minWordLength(), () -> randomInt(Integer.MAX_VALUE)));
|
||||
break;
|
||||
case 9:
|
||||
builder.minDocFreq(randomValueOtherThan(builder.minDocFreq(), () -> randomFloat()));
|
||||
break;
|
||||
default:
|
||||
break; // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public void testInvalidParameters() throws IOException {
|
||||
TermSuggestionBuilder builder = new TermSuggestionBuilder(randomAsciiOfLength(10));
|
||||
// test invalid accuracy values
|
||||
try {
|
||||
builder.accuracy(-0.5f);
|
||||
fail("Should not allow accuracy to be set to a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.accuracy(1.1f);
|
||||
fail("Should not allow accuracy to be greater than 1.0.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// test invalid max edit distance values
|
||||
try {
|
||||
builder.maxEdits(0);
|
||||
fail("Should not allow maxEdits to be less than 1.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.maxEdits(-1);
|
||||
fail("Should not allow maxEdits to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.maxEdits(3);
|
||||
fail("Should not allow maxEdits to be greater than 2.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// test invalid max inspections values
|
||||
try {
|
||||
builder.maxInspections(-1);
|
||||
fail("Should not allow maxInspections to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// test invalid max term freq values
|
||||
try {
|
||||
builder.maxTermFreq(-0.5f);
|
||||
fail("Should not allow max term freq to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.maxTermFreq(1.5f);
|
||||
fail("If max term freq is greater than 1, it must be a whole number.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.maxTermFreq(2.0f); // this should be allowed
|
||||
} catch (IllegalArgumentException e) {
|
||||
fail("A max term freq greater than 1 that is a whole number should be allowed.");
|
||||
}
|
||||
// test invalid min doc freq values
|
||||
try {
|
||||
builder.minDocFreq(-0.5f);
|
||||
fail("Should not allow min doc freq to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.minDocFreq(1.5f);
|
||||
fail("If min doc freq is greater than 1, it must be a whole number.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.minDocFreq(2.0f); // this should be allowed
|
||||
} catch (IllegalArgumentException e) {
|
||||
fail("A min doc freq greater than 1 that is a whole number should be allowed.");
|
||||
}
|
||||
// test invalid min word length values
|
||||
try {
|
||||
builder.minWordLength(0);
|
||||
fail("A min word length < 1 should not be allowed.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.minWordLength(-1);
|
||||
fail("Should not allow min word length to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// test invalid prefix length values
|
||||
try {
|
||||
builder.prefixLength(-1);
|
||||
fail("Should not allow prefix length to be a negative value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// test invalid size values
|
||||
try {
|
||||
builder.size(0);
|
||||
fail("Size must be a positive value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
builder.size(-1);
|
||||
fail("Size must be a positive value.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
// null values not allowed for enums
|
||||
try {
|
||||
builder.sort(null);
|
||||
fail("Should not allow setting a null sort value.");
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
try {
|
||||
builder.stringDistance(null);
|
||||
fail("Should not allow setting a null string distance value.");
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
try {
|
||||
builder.suggestMode(null);
|
||||
fail("Should not allow setting a null suggest mode value.");
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testDefaultValuesSet() {
|
||||
TermSuggestionBuilder builder = new TermSuggestionBuilder(randomAsciiOfLength(10));
|
||||
assertThat(builder.accuracy(), notNullValue());
|
||||
assertThat(builder.maxEdits(), notNullValue());
|
||||
assertThat(builder.maxInspections(), notNullValue());
|
||||
assertThat(builder.maxTermFreq(), notNullValue());
|
||||
assertThat(builder.minDocFreq(), notNullValue());
|
||||
assertThat(builder.minWordLength(), notNullValue());
|
||||
assertThat(builder.prefixLength(), notNullValue());
|
||||
assertThat(builder.sort(), notNullValue());
|
||||
assertThat(builder.stringDistance(), notNullValue());
|
||||
assertThat(builder.suggestMode(), notNullValue());
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,8 @@ import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
|
|||
import static org.elasticsearch.search.suggest.SuggestBuilders.phraseSuggestion;
|
||||
import static org.elasticsearch.search.suggest.SuggestBuilders.termSuggestion;
|
||||
import static org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder.candidateGenerator;
|
||||
import static org.elasticsearch.search.suggest.SuggestionBuilder.SortBy;
|
||||
import static org.elasticsearch.search.suggest.SuggestionBuilder.SuggestMode;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSuggestion;
|
||||
|
@ -100,7 +102,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
refresh();
|
||||
|
||||
TermSuggestionBuilder termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("abcd")
|
||||
.field("text");
|
||||
logger.info("--> run suggestions with one index");
|
||||
|
@ -114,7 +116,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
index("test_1", "type1", "4", "text", "ab cc");
|
||||
refresh();
|
||||
termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("ab cd")
|
||||
.minWordLength(1)
|
||||
.field("text");
|
||||
|
@ -141,7 +143,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
refresh();
|
||||
|
||||
termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("ab cd")
|
||||
.minWordLength(1)
|
||||
.field("text");
|
||||
|
@ -161,7 +163,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
|
||||
|
||||
termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("ABCD")
|
||||
.minWordLength(1)
|
||||
.field("text");
|
||||
|
@ -241,7 +243,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
assertThat("didn't ask for suggestions but got some", search.getSuggest(), nullValue());
|
||||
|
||||
TermSuggestionBuilder termSuggestion = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("abcd")
|
||||
.field("text")
|
||||
.size(10);
|
||||
|
@ -316,7 +318,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
assertThat("didn't ask for suggestions but got some", search.getSuggest(), nullValue());
|
||||
|
||||
TermSuggestionBuilder termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("abcd")
|
||||
.field("text");
|
||||
Suggest suggest = searchSuggest( termSuggest);
|
||||
|
@ -336,7 +338,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
refresh();
|
||||
|
||||
TermSuggestionBuilder termSuggest = termSuggestion("test")
|
||||
.suggestMode("always") // Always, otherwise the results can vary between requests.
|
||||
.suggestMode(SuggestMode.ALWAYS) // Always, otherwise the results can vary between requests.
|
||||
.text("abcd")
|
||||
.field("text");
|
||||
Suggest suggest = searchSuggest( termSuggest);
|
||||
|
@ -361,13 +363,13 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
Suggest suggest = searchSuggest(
|
||||
termSuggestion("size1")
|
||||
.size(1).text("prefix_abcd").maxTermFreq(10).prefixLength(1).minDocFreq(0)
|
||||
.field("field1").suggestMode("always"),
|
||||
.field("field1").suggestMode(SuggestMode.ALWAYS),
|
||||
termSuggestion("field2")
|
||||
.field("field2").text("prefix_eeeh prefix_efgh")
|
||||
.maxTermFreq(10).minDocFreq(0).suggestMode("always"),
|
||||
.maxTermFreq(10).minDocFreq(0).suggestMode(SuggestMode.ALWAYS),
|
||||
termSuggestion("accuracy")
|
||||
.field("field2").text("prefix_efgh").setAccuracy(1f)
|
||||
.maxTermFreq(10).minDocFreq(0).suggestMode("always"));
|
||||
.field("field2").text("prefix_efgh").accuracy(1f)
|
||||
.maxTermFreq(10).minDocFreq(0).suggestMode(SuggestMode.ALWAYS));
|
||||
assertSuggestion(suggest, 0, "size1", "prefix_aacd");
|
||||
assertThat(suggest.getSuggestion("field2").getEntries().get(0).getText().string(), equalTo("prefix_eeeh"));
|
||||
assertSuggestion(suggest, 0, "field2", "prefix_efgh");
|
||||
|
@ -403,15 +405,15 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
|
||||
Suggest suggest = searchSuggest( "prefix_abcd",
|
||||
termSuggestion("size3SortScoreFirst")
|
||||
.size(3).minDocFreq(0).field("field1").suggestMode("always"),
|
||||
.size(3).minDocFreq(0).field("field1").suggestMode(SuggestMode.ALWAYS),
|
||||
termSuggestion("size10SortScoreFirst")
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode("always").shardSize(50),
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode(SuggestMode.ALWAYS).shardSize(50),
|
||||
termSuggestion("size3SortScoreFirstMaxEdits1")
|
||||
.maxEdits(1)
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode("always"),
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode(SuggestMode.ALWAYS),
|
||||
termSuggestion("size10SortFrequencyFirst")
|
||||
.size(10).sort("frequency").shardSize(1000)
|
||||
.minDocFreq(0).field("field1").suggestMode("always"));
|
||||
.size(10).sort(SortBy.FREQUENCY).shardSize(1000)
|
||||
.minDocFreq(0).field("field1").suggestMode(SuggestMode.ALWAYS));
|
||||
|
||||
// The commented out assertions fail sometimes because suggestions are based off of shard frequencies instead of index frequencies.
|
||||
assertSuggestion(suggest, 0, "size3SortScoreFirst", "prefix_aacd", "prefix_abcc", "prefix_accd");
|
||||
|
@ -784,7 +786,7 @@ public class SuggestSearchTests extends ESIntegTestCase {
|
|||
|
||||
Suggest suggest = searchSuggest( "foobar",
|
||||
termSuggestion("simple")
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode("always"));
|
||||
.size(10).minDocFreq(0).field("field1").suggestMode(SuggestMode.ALWAYS));
|
||||
ElasticsearchAssertions.assertSuggestionSize(suggest, 0, 3, "simple");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue