summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorChristoph Büscher <christoph@elastic.co>2016-01-26 09:59:11 +0100
committerChristoph Büscher <christoph@elastic.co>2016-01-27 17:16:37 +0100
commitc00c0fa020a2ab3391a38312aba898db517f9f47 (patch)
treea48988c77087da01fe78cd1d055bbe284a5f0322 /core
parent60180fecf8edfcb162099148aec57c7ff27f934f (diff)
Initial refactoring for phrase suggester
Adding initial serialization methods (readFrom, writeTo) to the PhraseSuggestionBuilder, also adding the base test framework for serialiazation testing, equals and hashCode. Moving SuggestionBuilder out of the global SuggestBuilder for better readability.
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java5
-rw-r--r--core/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java4
-rw-r--r--core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java16
-rw-r--r--core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java17
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/SuggestBuilder.java131
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/SuggestionBuilder.java299
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionBuilder.java47
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java176
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java39
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/AbstractSuggestionBuilderTestCase.java186
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java3
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java2
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/CustomSuggesterSearchIT.java61
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilderTests.java114
14 files changed, 945 insertions, 155 deletions
diff --git a/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java b/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java
index 0d1c4932d4..5dcb39fa14 100644
--- a/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java
+++ b/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequest.java
@@ -30,6 +30,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.search.suggest.SuggestBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import java.io.IOException;
import java.util.Arrays;
@@ -99,10 +100,10 @@ public final class SuggestRequest extends BroadcastRequest<SuggestRequest> {
}
/**
- * set a new source using a {@link org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder}
+ * set a new source using a {@link org.elasticsearch.search.suggest.SuggestionBuilder}
* for completion suggestion lookup
*/
- public SuggestRequest suggest(SuggestBuilder.SuggestionBuilder suggestionBuilder) {
+ public SuggestRequest suggest(SuggestionBuilder suggestionBuilder) {
return suggest(suggestionBuilder.buildAsBytes(Requests.CONTENT_TYPE));
}
diff --git a/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java b/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java
index 06a2b00c64..d9f957aa2b 100644
--- a/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java
+++ b/core/src/main/java/org/elasticsearch/action/suggest/SuggestRequestBuilder.java
@@ -27,7 +27,7 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.search.suggest.SuggestBuilder;
-import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import java.io.IOException;
@@ -45,7 +45,7 @@ public class SuggestRequestBuilder extends BroadcastOperationRequestBuilder<Sugg
/**
* Add a definition for suggestions to the request
*/
- public <T> SuggestRequestBuilder addSuggestion(SuggestionBuilder<T> suggestion) {
+ public SuggestRequestBuilder addSuggestion(SuggestionBuilder<?> suggestion) {
suggest.addSuggestion(suggestion);
return this;
}
diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java
index 02e937dbd8..3f44b61f21 100644
--- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java
+++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamInput.java
@@ -38,6 +38,7 @@ import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.search.rescore.RescoreBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -282,6 +283,14 @@ public abstract class StreamInput extends InputStream {
}
@Nullable
+ public Float readOptionalFloat() throws IOException {
+ if (readBoolean()) {
+ return readFloat();
+ }
+ return null;
+ }
+
+ @Nullable
public Integer readOptionalVInt() throws IOException {
if (readBoolean()) {
return readVInt();
@@ -684,6 +693,13 @@ public abstract class StreamInput extends InputStream {
}
/**
+ * Reads a {@link SuggestionBuilder} from the current stream
+ */
+ public SuggestionBuilder<?> readSuggestion() throws IOException {
+ return readNamedWriteable(SuggestionBuilder.class);
+ }
+
+ /**
* Reads a {@link org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder} from the current stream
*/
public ScoreFunctionBuilder<?> readScoreFunction() throws IOException {
diff --git a/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java b/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java
index 0863717a5a..5e0af597b0 100644
--- a/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java
+++ b/core/src/main/java/org/elasticsearch/common/io/stream/StreamOutput.java
@@ -37,6 +37,7 @@ import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
import org.elasticsearch.search.rescore.RescoreBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.joda.time.ReadableInstant;
import java.io.EOFException;
@@ -230,6 +231,15 @@ public abstract class StreamOutput extends OutputStream {
}
}
+ public void writeOptionalFloat(@Nullable Float floatValue) throws IOException {
+ if (floatValue == null) {
+ writeBoolean(false);
+ } else {
+ writeBoolean(true);
+ writeFloat(floatValue);
+ }
+ }
+
public void writeOptionalText(@Nullable Text text) throws IOException {
if (text == null) {
writeInt(-1);
@@ -684,4 +694,11 @@ public abstract class StreamOutput extends OutputStream {
public void writeRescorer(RescoreBuilder<?> rescorer) throws IOException {
writeNamedWriteable(rescorer);
}
+
+ /**
+ * Writes a {@link SuggestionBuilder} to the current stream
+ */
+ public void writeSuggestion(SuggestionBuilder suggestion) throws IOException {
+ writeNamedWriteable(suggestion);
+ }
}
diff --git a/core/src/main/java/org/elasticsearch/search/suggest/SuggestBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/SuggestBuilder.java
index 5621e03e7d..92661b21f1 100644
--- a/core/src/main/java/org/elasticsearch/search/suggest/SuggestBuilder.java
+++ b/core/src/main/java/org/elasticsearch/search/suggest/SuggestBuilder.java
@@ -42,14 +42,14 @@ public class SuggestBuilder extends ToXContentToBytes {
public SuggestBuilder() {
this.name = null;
}
-
+
public SuggestBuilder(String name) {
this.name = name;
}
-
+
/**
* Sets the text to provide suggestions for. The suggest text is a required option that needs
- * to be set either via this setter or via the {@link org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder#setText(String)} method.
+ * to be set either via this setter or via the {@link org.elasticsearch.search.suggest.SuggestionBuilder#text(String)} method.
* <p>
* 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.
@@ -67,7 +67,7 @@ public class SuggestBuilder extends ToXContentToBytes {
suggestions.add(suggestion);
return this;
}
-
+
/**
* Returns all suggestions with the defined names.
*/
@@ -82,7 +82,7 @@ public class SuggestBuilder extends ToXContentToBytes {
} else {
builder.startObject(name);
}
-
+
if (globalText != null) {
builder.field("text", globalText);
}
@@ -92,125 +92,4 @@ public class SuggestBuilder extends ToXContentToBytes {
builder.endObject();
return builder;
}
-
- public static abstract class SuggestionBuilder<T> extends ToXContentToBytes {
-
- private String name;
- private String suggester;
- private String text;
- private String prefix;
- private String regex;
- private String field;
- private String analyzer;
- private Integer size;
- private Integer shardSize;
-
- public SuggestionBuilder(String name, String suggester) {
- this.name = name;
- this.suggester = suggester;
- }
-
- /**
- * Same as in {@link SuggestBuilder#setText(String)}, but in the suggestion scope.
- */
- @SuppressWarnings("unchecked")
- public T text(String text) {
- this.text = text;
- return (T) this;
- }
-
- protected void setPrefix(String prefix) {
- this.prefix = prefix;
- }
-
- protected void setRegex(String regex) {
- this.regex = regex;
- }
-
- @Override
- public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
- builder.startObject(name);
- if (text != null) {
- builder.field("text", text);
- }
- if (prefix != null) {
- builder.field("prefix", prefix);
- }
- if (regex != null) {
- builder.field("regex", regex);
- }
- builder.startObject(suggester);
- if (analyzer != null) {
- builder.field("analyzer", analyzer);
- }
- if (field != null) {
- builder.field("field", field);
- }
- if (size != null) {
- builder.field("size", size);
- }
- if (shardSize != null) {
- builder.field("shard_size", shardSize);
- }
-
- builder = innerToXContent(builder, params);
- builder.endObject();
- builder.endObject();
- return builder;
- }
-
- protected abstract XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException;
-
- /**
- * Sets from what field to fetch the candidate suggestions from. This is an
- * required option and needs to be set via this setter or
- * {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder#field(String)}
- * method
- */
- @SuppressWarnings("unchecked")
- public T field(String field) {
- this.field = field;
- return (T)this;
- }
-
- /**
- * Sets the analyzer to analyse to suggest text with. Defaults to the search
- * analyzer of the suggest field.
- */
- @SuppressWarnings("unchecked")
- public T analyzer(String analyzer) {
- this.analyzer = analyzer;
- return (T)this;
- }
-
- /**
- * Sets the maximum suggestions to be returned per suggest text term.
- */
- @SuppressWarnings("unchecked")
- public T size(int size) {
- if (size <= 0) {
- throw new IllegalArgumentException("Size must be positive");
- }
- this.size = size;
- return (T)this;
- }
-
- /**
- * Sets the maximum number of suggested term to be retrieved from each
- * individual shard. During the reduce phase the only the top N suggestions
- * are returned based on the <code>size</code> option. Defaults to the
- * <code>size</code> option.
- * <p>
- * Setting this to a value higher than the `size` can be useful in order to
- * get a more accurate document frequency for suggested terms. Due to the
- * fact that terms are partitioned amongst shards, the shard level document
- * frequencies of suggestions may not be precise. Increasing this will make
- * these document frequencies more precise.
- */
- @SuppressWarnings("unchecked")
- public T shardSize(Integer shardSize) {
- this.shardSize = shardSize;
- return (T)this;
- }
- }
}
diff --git a/core/src/main/java/org/elasticsearch/search/suggest/SuggestionBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/SuggestionBuilder.java
new file mode 100644
index 0000000000..7705f2201d
--- /dev/null
+++ b/core/src/main/java/org/elasticsearch/search/suggest/SuggestionBuilder.java
@@ -0,0 +1,299 @@
+/*
+ * 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;
+
+import org.elasticsearch.action.support.ToXContentToBytes;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.io.stream.NamedWriteable;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Base class for the different suggestion implementations.
+ */
+public abstract class SuggestionBuilder<T extends SuggestionBuilder<T>> extends ToXContentToBytes implements NamedWriteable<T> {
+
+ protected final String name;
+ // TODO this seems mandatory and should be constructor arg
+ protected String fieldname;
+ protected String text;
+ protected String prefix;
+ protected String regex;
+ protected String analyzer;
+ protected Integer size;
+ protected Integer shardSize;
+
+ protected static final ParseField TEXT_FIELD = new ParseField("text");
+ protected static final ParseField PREFIX_FIELD = new ParseField("prefix");
+ protected static final ParseField REGEX_FIELD = new ParseField("regex");
+ protected static final ParseField FIELDNAME_FIELD = new ParseField("field");
+ protected static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
+ protected static final ParseField SIZE_FIELD = new ParseField("size");
+ protected static final ParseField SHARDSIZE_FIELD = new ParseField("shard_size");
+
+ public SuggestionBuilder(String name) {
+ this.name = name;
+ }
+
+ /**
+ * get the name for this suggestion
+ */
+ public String name() {
+ return this.name;
+ }
+
+ /**
+ * Same as in {@link SuggestBuilder#setText(String)}, but in the suggestion scope.
+ */
+ @SuppressWarnings("unchecked")
+ public T text(String text) {
+ this.text = text;
+ return (T) this;
+ }
+
+ /**
+ * get the text for this suggestion
+ */
+ public String text() {
+ return this.text;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T prefix(String prefix) {
+ this.prefix = prefix;
+ return (T) this;
+ }
+
+ /**
+ * get the prefix for this suggestion
+ */
+ public String prefix() {
+ return this.prefix;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected T regex(String regex) {
+ this.regex = regex;
+ return (T) this;
+ }
+
+ /**
+ * get the regex for this suggestion
+ */
+ public String regex() {
+ return this.regex;
+ }
+
+ @Override
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ builder.startObject(name);
+ 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);
+ }
+ if (fieldname != null) {
+ 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();
+ builder.endObject();
+ return builder;
+ }
+
+ 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();
+ }
+
+ protected abstract XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException;
+
+ /**
+ * Sets from what field to fetch the candidate suggestions from. This is an
+ * required option and needs to be set via this setter or
+ * {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder#field(String)}
+ * method
+ */
+ @SuppressWarnings("unchecked")
+ public T field(String field) {
+ this.fieldname = field;
+ return (T)this;
+ }
+
+ /**
+ * get the {@link #field()} parameter
+ */
+ public String field() {
+ return this.fieldname;
+ }
+
+ /**
+ * Sets the analyzer to analyse to suggest text with. Defaults to the search
+ * analyzer of the suggest field.
+ */
+ @SuppressWarnings("unchecked")
+ public T analyzer(String analyzer) {
+ this.analyzer = analyzer;
+ return (T)this;
+ }
+
+ /**
+ * get the {@link #analyzer()} parameter
+ */
+ public String analyzer() {
+ return this.analyzer;
+ }
+
+ /**
+ * Sets the maximum suggestions to be returned per suggest text term.
+ */
+ @SuppressWarnings("unchecked")
+ public T size(int size) {
+ if (size <= 0) {
+ throw new IllegalArgumentException("Size must be positive");
+ }
+ this.size = size;
+ return (T)this;
+ }
+
+ /**
+ * get the {@link #size()} parameter
+ */
+ public Integer size() {
+ return this.size;
+ }
+
+ /**
+ * Sets the maximum number of suggested term to be retrieved from each
+ * individual shard. During the reduce phase the only the top N suggestions
+ * are returned based on the <code>size</code> option. Defaults to the
+ * <code>size</code> option.
+ * <p>
+ * Setting this to a value higher than the `size` can be useful in order to
+ * get a more accurate document frequency for suggested terms. Due to the
+ * fact that terms are partitioned amongst shards, the shard level document
+ * frequencies of suggestions may not be precise. Increasing this will make
+ * these document frequencies more precise.
+ */
+ @SuppressWarnings("unchecked")
+ public T shardSize(Integer shardSize) {
+ this.shardSize = shardSize;
+ return (T)this;
+ }
+
+ /**
+ * get the {@link #shardSize()} parameter
+ */
+ public Integer shardSize() {
+ return this.shardSize;
+ }
+
+
+ @Override
+ public final T readFrom(StreamInput in) throws IOException {
+ String name = in.readString();
+ T suggestionBuilder = doReadFrom(in, name);
+ suggestionBuilder.fieldname = in.readOptionalString();
+ suggestionBuilder.text = in.readOptionalString();
+ suggestionBuilder.prefix = in.readOptionalString();
+ suggestionBuilder.regex = in.readOptionalString();
+ suggestionBuilder.analyzer = in.readOptionalString();
+ suggestionBuilder.size = in.readOptionalVInt();
+ suggestionBuilder.shardSize = in.readOptionalVInt();
+ return suggestionBuilder;
+ }
+
+ /**
+ * Subclass should return a new instance, reading itself from the input string
+ * @param in the input string to read from
+ * @param name the name of the suggestion (read from stream by {@link SuggestionBuilder}
+ */
+ protected abstract T doReadFrom(StreamInput in, String name) throws IOException;
+
+ @Override
+ public final void writeTo(StreamOutput out) throws IOException {
+ out.writeString(name);
+ doWriteTo(out);
+ out.writeOptionalString(fieldname);
+ out.writeOptionalString(text);
+ out.writeOptionalString(prefix);
+ out.writeOptionalString(regex);
+ out.writeOptionalString(analyzer);
+ out.writeOptionalVInt(size);
+ out.writeOptionalVInt(shardSize);
+ }
+
+ protected abstract void doWriteTo(StreamOutput out) throws IOException;
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ T other = (T) obj;
+ return Objects.equals(name, other.name()) &&
+ Objects.equals(text, other.text()) &&
+ Objects.equals(prefix, other.prefix()) &&
+ Objects.equals(regex, other.regex()) &&
+ Objects.equals(fieldname, other.field()) &&
+ Objects.equals(analyzer, other.analyzer()) &&
+ Objects.equals(size, other.size()) &&
+ Objects.equals(shardSize, other.shardSize()) &&
+ doEquals(other);
+ }
+
+ /**
+ * Indicates whether some other {@link SuggestionBuilder} of the same type is "equal to" this one.
+ */
+ protected abstract boolean doEquals(T other);
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(name, text, prefix, regex, fieldname, analyzer, size, shardSize, doHashCode());
+ }
+
+ /**
+ * HashCode for the subclass of {@link SuggestionBuilder} to implement.
+ */
+ protected abstract int doHashCode();
+} \ No newline at end of file
diff --git a/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionBuilder.java
index 9cf78ea667..1b515e7540 100644
--- a/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionBuilder.java
+++ b/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestionBuilder.java
@@ -22,11 +22,13 @@ import org.apache.lucene.search.suggest.document.FuzzyCompletionQuery;
import org.apache.lucene.util.automaton.Operations;
import org.apache.lucene.util.automaton.RegExp;
import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.RegexpFlag;
-import org.elasticsearch.search.suggest.SuggestBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext;
import org.elasticsearch.search.suggest.completion.context.GeoQueryContext;
@@ -45,7 +47,7 @@ import java.util.Set;
* are created at index-time and so must be defined in the mapping with the type "completion" before
* indexing.
*/
-public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilder<CompletionSuggestionBuilder> {
+public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSuggestionBuilder> {
final static String SUGGESTION_NAME = "completion";
static final ParseField PAYLOAD_FIELD = new ParseField("payload");
@@ -56,7 +58,7 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
private final Set<String> payloadFields = new HashSet<>();
public CompletionSuggestionBuilder(String name) {
- super(name, SUGGESTION_NAME);
+ super(name);
}
/**
@@ -255,8 +257,9 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
* Sets the prefix to provide completions for.
* The prefix gets analyzed by the suggest analyzer.
*/
+ @Override
public CompletionSuggestionBuilder prefix(String prefix) {
- super.setPrefix(prefix);
+ super.prefix(prefix);
return this;
}
@@ -264,7 +267,7 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
* Same as {@link #prefix(String)} with fuzziness of <code>fuzziness</code>
*/
public CompletionSuggestionBuilder prefix(String prefix, Fuzziness fuzziness) {
- super.setPrefix(prefix);
+ super.prefix(prefix);
this.fuzzyOptionsBuilder = new FuzzyOptionsBuilder().setFuzziness(fuzziness);
return this;
}
@@ -274,7 +277,7 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
* see {@link FuzzyOptionsBuilder}
*/
public CompletionSuggestionBuilder prefix(String prefix, FuzzyOptionsBuilder fuzzyOptionsBuilder) {
- super.setPrefix(prefix);
+ super.prefix(prefix);
this.fuzzyOptionsBuilder = fuzzyOptionsBuilder;
return this;
}
@@ -282,8 +285,9 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
/**
* Sets a regular expression pattern for prefixes to provide completions for.
*/
+ @Override
public CompletionSuggestionBuilder regex(String regex) {
- super.setRegex(regex);
+ super.regex(regex);
return this;
}
@@ -362,4 +366,33 @@ public class CompletionSuggestionBuilder extends SuggestBuilder.SuggestionBuilde
}
return builder;
}
+
+ @Override
+ public String getWriteableName() {
+ return SUGGESTION_NAME;
+ }
+
+ @Override
+ public void doWriteTo(StreamOutput out) throws IOException {
+ // NORELEASE
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CompletionSuggestionBuilder doReadFrom(StreamInput in, String name) throws IOException {
+ // NORELEASE
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean doEquals(CompletionSuggestionBuilder other) {
+ // NORELEASE
+ return false;
+ }
+
+ @Override
+ protected int doHashCode() {
+ // NORELEASE
+ return 0;
+ }
}
diff --git a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java
index 1055fbe83f..46c9b0f99f 100644
--- a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java
+++ b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilder.java
@@ -18,10 +18,12 @@
*/
package org.elasticsearch.search.suggest.phrase;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.script.Template;
-import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import java.io.IOException;
import java.util.ArrayList;
@@ -29,12 +31,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.Set;
/**
* Defines the actual suggest command for phrase suggestions ( <tt>phrase</tt>).
*/
public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSuggestionBuilder> {
+
+ static final String SUGGESTION_NAME = "phrase";
+
+ public static final PhraseSuggestionBuilder PROTOTYPE = new PhraseSuggestionBuilder("_na_");
+
private Float maxErrors;
private String separator;
private Float realWordErrorLikelihood;
@@ -51,7 +59,7 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
private Boolean collatePrune;
public PhraseSuggestionBuilder(String name) {
- super(name, "phrase");
+ super(name);
}
/**
@@ -68,6 +76,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the {@link #gramSize(int)} parameter
+ */
+ public Integer gramSize() {
+ return this.gramSize;
+ }
+
+ /**
* Sets the maximum percentage of the terms that at most considered to be
* misspellings in order to form a correction. This method accepts a float
* value in the range [0..1) as a fraction of the actual query terms a
@@ -82,6 +97,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the maxErrors setting
+ */
+ public Float maxErrors() {
+ return this.maxErrors;
+ }
+
+ /**
* Sets the separator that is used to separate terms in the bigram field. If
* not set the whitespace character is used as a separator.
*/
@@ -91,6 +113,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the separator that is used to separate terms in the bigram field.
+ */
+ public String separator() {
+ return this.separator;
+ }
+
+ /**
* Sets the likelihood of a term being a misspelled even if the term exists
* in the dictionary. The default it <tt>0.95</tt> corresponding to 5% or
* the real words are misspelled.
@@ -101,6 +130,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the {@link #realWordErrorLikelihood(Float)} parameter
+ */
+ public Float realWordErrorLikelihood() {
+ return this.realWordErrorLikelihood;
+ }
+
+ /**
* Sets the confidence level for this suggester. The confidence level
* defines a factor applied to the input phrases score which is used as a
* threshold for other suggest candidates. Only candidates that score higher
@@ -115,6 +151,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the {@link #confidence()} parameter
+ */
+ public Float confidence() {
+ return this.confidence;
+ }
+
+ /**
* Adds a {@link CandidateGenerator} to this suggester. The
* {@link CandidateGenerator} is used to draw candidates for each individual
* phrase term before the candidates are scored.
@@ -147,6 +190,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the setting for {@link #forceUnigrams()}
+ */
+ public Boolean forceUnigrams() {
+ return this.forceUnigrams;
+ }
+
+ /**
* Sets an explicit smoothing model used for this suggester. The default is
* {@link PhraseSuggestionBuilder.StupidBackoff}.
*/
@@ -161,6 +211,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the {@link #tokenLimit(int)} parameter
+ */
+ public Integer tokenLimit() {
+ return this.tokenLimit;
+ }
+
+ /**
* Setup highlighting for suggestions. If this is called a highlight field
* is returned with suggestions wrapping changed tokens with preTag and postTag.
*/
@@ -174,6 +231,20 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * get the pre-tag for the highlighter set with {@link #highlight(String, String)}
+ */
+ public String preTag() {
+ return this.preTag;
+ }
+
+ /**
+ * get the post-tag for the highlighter set with {@link #highlight(String, String)}
+ */
+ public String postTag() {
+ return this.postTag;
+ }
+
+ /**
* Sets a query used for filtering out suggested phrases (collation).
*/
public PhraseSuggestionBuilder collateQuery(String collateQuery) {
@@ -190,6 +261,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * gets the query used for filtering out suggested phrases (collation).
+ */
+ public Template collateQuery() {
+ return this.collateQuery;
+ }
+
+ /**
* Sets additional params for collate script
*/
public PhraseSuggestionBuilder collateParams(Map<String, Object> collateParams) {
@@ -198,6 +276,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
/**
+ * gets additional params for collate script
+ */
+ public Map<String, Object> collateParams() {
+ return this.collateParams;
+ }
+
+ /**
* Sets whether to prune suggestions after collation
*/
public PhraseSuggestionBuilder collatePrune(boolean collatePrune) {
@@ -205,6 +290,13 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
return this;
}
+ /**
+ * Gets whether to prune suggestions after collation
+ */
+ public Boolean collatePrune() {
+ return this.collatePrune;
+ }
+
@Override
public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
if (realWordErrorLikelihood != null) {
@@ -428,7 +520,7 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
private Float minDocFreq;
/**
- * @param field Sets from what field to fetch the candidate suggestions from.
+ * @param field Sets from what field to fetch the candidate suggestions from.
*/
public DirectCandidateGenerator(String field) {
super("direct_generator");
@@ -655,4 +747,82 @@ public final class PhraseSuggestionBuilder extends SuggestionBuilder<PhraseSugge
}
+ @Override
+ public String getWriteableName() {
+ return SUGGESTION_NAME;
+ }
+
+ @Override
+ public void doWriteTo(StreamOutput out) throws IOException {
+ out.writeOptionalFloat(maxErrors);
+ out.writeOptionalFloat(realWordErrorLikelihood);
+ out.writeOptionalFloat(confidence);
+ out.writeOptionalVInt(gramSize);
+ // NORELEASE model.writeTo();
+ out.writeOptionalBoolean(forceUnigrams);
+ out.writeOptionalVInt(tokenLimit);
+ out.writeOptionalString(preTag);
+ out.writeOptionalString(postTag);
+ out.writeOptionalString(separator);
+ if (collateQuery != null) {
+ out.writeBoolean(true);
+ collateQuery.writeTo(out);
+ } else {
+ out.writeBoolean(false);
+ }
+ out.writeMap(collateParams);
+ out.writeOptionalBoolean(collatePrune);
+ // NORELEASE write Map<String, List<CandidateGenerator>> generators = new HashMap<>();
+ }
+
+ @Override
+ public PhraseSuggestionBuilder doReadFrom(StreamInput in, String name) throws IOException {
+ PhraseSuggestionBuilder builder = new PhraseSuggestionBuilder(name);
+ builder.maxErrors = in.readOptionalFloat();
+ builder.realWordErrorLikelihood = in.readOptionalFloat();
+ builder.confidence = in.readOptionalFloat();
+ builder.gramSize = in.readOptionalVInt();
+ // NORELEASE read model
+ builder.forceUnigrams = in.readOptionalBoolean();
+ builder.tokenLimit = in.readOptionalVInt();
+ builder.preTag = in.readOptionalString();
+ builder.postTag = in.readOptionalString();
+ builder.separator = in.readOptionalString();
+ if (in.readBoolean()) {
+ builder.collateQuery = Template.readTemplate(in);
+ }
+ builder.collateParams = in.readMap();
+ builder.collatePrune = in.readOptionalBoolean();
+ // NORELEASE read Map<String, List<CandidateGenerator>> generators;
+ return builder;
+ }
+
+ @Override
+ protected boolean doEquals(PhraseSuggestionBuilder other) {
+ return Objects.equals(maxErrors, other.maxErrors) &&
+ Objects.equals(separator, other.separator) &&
+ Objects.equals(realWordErrorLikelihood, other.realWordErrorLikelihood) &&
+ Objects.equals(confidence, other.confidence) &&
+ // NORELEASE Objects.equals(generator, other.generator) &&
+ Objects.equals(gramSize, other.gramSize) &&
+ // NORELEASE Objects.equals(model, other.model) &&
+ Objects.equals(forceUnigrams, other.forceUnigrams) &&
+ Objects.equals(tokenLimit, other.tokenLimit) &&
+ Objects.equals(preTag, other.preTag) &&
+ Objects.equals(postTag, other.postTag) &&
+ Objects.equals(collateQuery, other.collateQuery) &&
+ Objects.equals(collateParams, other.collateParams) &&
+ Objects.equals(collatePrune, other.collatePrune);
+ }
+
+ @Override
+ protected int doHashCode() {
+ return Objects.hash(maxErrors, separator, realWordErrorLikelihood, confidence,
+ /** NORELEASE generators, */
+ gramSize,
+ /** NORELEASE model, */
+ forceUnigrams, tokenLimit, preTag, postTag,
+ collateQuery, collateParams, collatePrune);
+ }
+
}
diff --git a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java
index 03eb388f00..e2a14c1a2b 100644
--- a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java
+++ b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestionBuilder.java
@@ -17,8 +17,10 @@
* 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.xcontent.XContentBuilder;
-import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder;
+import org.elasticsearch.search.suggest.SuggestionBuilder;
import java.io.IOException;
@@ -29,6 +31,8 @@ import java.io.IOException;
*/
public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuilder> {
+ static final String SUGGESTION_NAME = "term";
+
private String suggestMode;
private Float accuracy;
private String sort;
@@ -39,13 +43,13 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
private Integer prefixLength;
private Integer minWordLength;
private Float minDocFreq;
-
+
/**
* @param name
* The name of this suggestion. This is a required parameter.
*/
public TermSuggestionBuilder(String name) {
- super(name, "term");
+ super(name);
}
/**
@@ -221,4 +225,33 @@ public class TermSuggestionBuilder extends SuggestionBuilder<TermSuggestionBuild
}
return builder;
}
+
+ @Override
+ public String getWriteableName() {
+ return SUGGESTION_NAME;
+ }
+
+ @Override
+ public void doWriteTo(StreamOutput out) throws IOException {
+ // NORELEASE
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public TermSuggestionBuilder doReadFrom(StreamInput in, String name) throws IOException {
+ // NORELEASE
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected boolean doEquals(TermSuggestionBuilder other) {
+ // NORELEASE
+ return false;
+ }
+
+ @Override
+ protected int doHashCode() {
+ // NORELEASE
+ return 0;
+ }
}
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/AbstractSuggestionBuilderTestCase.java b/core/src/test/java/org/elasticsearch/search/suggest/AbstractSuggestionBuilderTestCase.java
new file mode 100644
index 0000000000..71d37a6911
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/search/suggest/AbstractSuggestionBuilderTestCase.java
@@ -0,0 +1,186 @@
+/*
+ * 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;
+
+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.phrase.PhraseSuggestionBuilder;
+import org.elasticsearch.test.ESTestCase;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+
+public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBuilder<SB>> extends ESTestCase {
+
+ private static final int NUMBER_OF_TESTBUILDERS = 20;
+ private static NamedWriteableRegistry namedWriteableRegistry;
+
+ /**
+ * setup for the whole base test class
+ */
+ @BeforeClass
+ public static void init() {
+ namedWriteableRegistry = new NamedWriteableRegistry();
+ namedWriteableRegistry.registerPrototype(SuggestionBuilder.class, PhraseSuggestionBuilder.PROTOTYPE);
+ }
+
+ @AfterClass
+ public static void afterClass() throws Exception {
+ namedWriteableRegistry = null;
+ }
+
+ /**
+ * Test serialization and deserialization of the suggestion builder
+ */
+ public void testSerialization() throws IOException {
+ for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
+ SB original = randomTestBuilder();
+ SB deserialized = serializedCopy(original);
+ assertEquals(deserialized, original);
+ assertEquals(deserialized.hashCode(), original.hashCode());
+ assertNotSame(deserialized, original);
+ }
+ }
+
+ /**
+ * returns a random suggestion builder, setting the common options randomly
+ */
+ protected SB randomTestBuilder() {
+ SB randomSuggestion = randomSuggestionBuilder();
+ maybeSet(randomSuggestion::text, randomAsciiOfLengthBetween(2, 20));
+ maybeSet(randomSuggestion::prefix, randomAsciiOfLengthBetween(2, 20));
+ maybeSet(randomSuggestion::regex, randomAsciiOfLengthBetween(2, 20));
+ maybeSet(randomSuggestion::field, randomAsciiOfLengthBetween(2, 20));
+ maybeSet(randomSuggestion::analyzer, randomAsciiOfLengthBetween(2, 20));
+ maybeSet(randomSuggestion::size, randomIntBetween(1, 20));
+ maybeSet(randomSuggestion::shardSize, randomInt(20));
+ return randomSuggestion;
+ }
+
+ /**
+ * create a randomized {@link SuggestBuilder} that is used in further tests
+ */
+ protected abstract SB randomSuggestionBuilder();
+
+ /**
+ * Test equality and hashCode properties
+ */
+ public void testEqualsAndHashcode() throws IOException {
+ for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
+ SB firstBuilder = randomTestBuilder();
+ assertFalse("suggestion builder is equal to null", firstBuilder.equals(null));
+ assertFalse("suggestion builder is equal to incompatible type", firstBuilder.equals(""));
+ assertTrue("suggestion builder is not equal to self", firstBuilder.equals(firstBuilder));
+ assertThat("same suggestion builder's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
+ equalTo(firstBuilder.hashCode()));
+ assertThat("different suggestion builders should not be equal", mutate(firstBuilder), not(equalTo(firstBuilder)));
+
+ SB secondBuilder = serializedCopy(firstBuilder);
+ assertTrue("suggestion builder is not equal to self", secondBuilder.equals(secondBuilder));
+ assertTrue("suggestion builder is not equal to its copy", firstBuilder.equals(secondBuilder));
+ assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
+ assertThat("suggestion builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(firstBuilder.hashCode()));
+
+ SB thirdBuilder = serializedCopy(secondBuilder);
+ assertTrue("suggestion builder is not equal to self", thirdBuilder.equals(thirdBuilder));
+ assertTrue("suggestion builder is not equal to its copy", secondBuilder.equals(thirdBuilder));
+ assertThat("suggestion builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
+ assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
+ assertThat("suggestion builder copy's hashcode is different from original hashcode", firstBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
+ assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
+ assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
+ }
+ }
+
+ private SB mutate(SB firstBuilder) throws IOException {
+ SB mutation = serializedCopy(firstBuilder);
+ assertNotSame(mutation, firstBuilder);
+ if (randomBoolean()) {
+ // change one of the common SuggestionBuilder parameters
+ switch (randomIntBetween(0, 6)) {
+ case 0:
+ mutation.text(randomValueOtherThan(mutation.text(), () -> randomAsciiOfLengthBetween(2, 20)));
+ break;
+ case 1:
+ mutation.prefix(randomValueOtherThan(mutation.prefix(), () -> randomAsciiOfLengthBetween(2, 20)));
+ break;
+ case 2:
+ mutation.regex(randomValueOtherThan(mutation.regex(), () -> randomAsciiOfLengthBetween(2, 20)));
+ break;
+ case 3:
+ mutation.field(randomValueOtherThan(mutation.field(), () -> randomAsciiOfLengthBetween(2, 20)));
+ break;
+ case 4:
+ mutation.analyzer(randomValueOtherThan(mutation.analyzer(), () -> randomAsciiOfLengthBetween(2, 20)));
+ break;
+ case 5:
+ mutation.size(randomValueOtherThan(mutation.size(), () -> randomIntBetween(1, 20)));
+ break;
+ case 6:
+ mutation.shardSize(randomValueOtherThan(mutation.shardSize(), () -> randomIntBetween(1, 20)));
+ break;
+ }
+ } else {
+ mutateSpecificParameters(firstBuilder);
+ }
+ return mutation;
+ }
+
+ /**
+ * take and input {@link SuggestBuilder} and return another one that is different in one aspect (to test non-equality)
+ */
+ protected abstract void mutateSpecificParameters(SB firstBuilder) throws IOException;
+
+ @SuppressWarnings("unchecked")
+ protected SB serializedCopy(SB original) throws IOException {
+ try (BytesStreamOutput output = new BytesStreamOutput()) {
+ output.writeSuggestion(original);;
+ try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
+ return (SB) in.readSuggestion();
+ }
+ }
+ }
+
+ protected static <T> void maybeSet(Consumer<T> consumer, T value) {
+ if (randomBoolean()) {
+ consumer.accept(value);
+ }
+ }
+
+ /**
+ * helper to get a random value in a certain range that's different from the input
+ */
+ protected static <T> T randomValueOtherThan(T input, Supplier<T> randomSupplier) {
+ T randomValue = null;
+ do {
+ randomValue = randomSupplier.get();
+ } while (randomValue.equals(input));
+ return randomValue;
+ }
+
+}
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
index 1543433be3..271fa08487 100644
--- a/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
+++ b/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearchIT.java
@@ -20,6 +20,7 @@ package org.elasticsearch.search.suggest;
import com.carrotsearch.hppc.ObjectLongHashMap;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
+
import org.apache.lucene.analysis.TokenStreamToAutomaton;
import org.apache.lucene.search.suggest.document.ContextSuggestField;
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
@@ -907,7 +908,7 @@ public class CompletionSuggestSearchIT extends ESIntegTestCase {
}
- public void assertSuggestions(String suggestionName, SuggestBuilder.SuggestionBuilder suggestBuilder, String... suggestions) {
+ public void assertSuggestions(String suggestionName, SuggestionBuilder suggestBuilder, String... suggestions) {
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestBuilder
).execute().actionGet();
assertSuggestions(suggestResponse, suggestionName, suggestions);
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
index 18d6d9b99f..d92bd865f5 100644
--- a/core/src/test/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
+++ b/core/src/test/java/org/elasticsearch/search/suggest/ContextCompletionSuggestSearchIT.java
@@ -632,7 +632,7 @@ public class ContextCompletionSuggestSearchIT extends ESIntegTestCase {
assertEquals("Hotel Amsterdam in Berlin", suggestResponse.getSuggest().getSuggestion(suggestionName).iterator().next().getOptions().iterator().next().getText().string());
}
- public void assertSuggestions(String suggestionName, SuggestBuilder.SuggestionBuilder suggestBuilder, String... suggestions) {
+ public void assertSuggestions(String suggestionName, SuggestionBuilder suggestBuilder, String... suggestions) {
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestBuilder
).execute().actionGet();
CompletionSuggestSearchIT.assertSuggestions(suggestResponse, suggestionName, suggestions);
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/CustomSuggesterSearchIT.java b/core/src/test/java/org/elasticsearch/search/suggest/CustomSuggesterSearchIT.java
index 18b4fa50e7..80eb4d7b7d 100644
--- a/core/src/test/java/org/elasticsearch/search/suggest/CustomSuggesterSearchIT.java
+++ b/core/src/test/java/org/elasticsearch/search/suggest/CustomSuggesterSearchIT.java
@@ -20,6 +20,8 @@ package org.elasticsearch.search.suggest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.plugins.Plugin;
@@ -31,6 +33,7 @@ import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.hasSize;
@@ -59,16 +62,7 @@ public class CustomSuggesterSearchIT extends ESIntegTestCase {
String randomField = randomAsciiOfLength(10);
String randomSuffix = randomAsciiOfLength(10);
SuggestBuilder suggestBuilder = new SuggestBuilder();
- suggestBuilder.addSuggestion(
- new SuggestBuilder.SuggestionBuilder<SuggestBuilder.SuggestionBuilder>("someName", "custom") {
- @Override
- protected XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
- builder.field("field", randomField);
- builder.field("suffix", randomSuffix);
- return builder;
- }
- }.text(randomText)
- );
+ suggestBuilder.addSuggestion(new CustomSuggestionBuilder("someName", randomField, randomSuffix).text(randomText));
SearchRequestBuilder searchRequestBuilder = client().prepareSearch("test").setTypes("test").setFrom(0).setSize(1)
.suggest(suggestBuilder);
@@ -83,4 +77,51 @@ public class CustomSuggesterSearchIT extends ESIntegTestCase {
assertThat(suggestions.get(1).getText().string(), is(String.format(Locale.ROOT, "%s-%s-%s-123", randomText, randomField, randomSuffix)));
}
+ class CustomSuggestionBuilder extends SuggestionBuilder<CustomSuggestionBuilder> {
+
+ private String randomField;
+ private String randomSuffix;
+
+ public CustomSuggestionBuilder(String name, String randomField, String randomSuffix) {
+ super(name);
+ this.randomField = randomField;
+ this.randomSuffix = randomSuffix;
+ }
+
+ @Override
+ protected XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
+ builder.field("field", randomField);
+ builder.field("suffix", randomSuffix);
+ return builder;
+ }
+
+ @Override
+ public String getWriteableName() {
+ return "custom";
+ }
+
+ @Override
+ public void doWriteTo(StreamOutput out) throws IOException {
+ out.writeString(randomField);
+ out.writeString(randomSuffix);
+ }
+
+ @Override
+ public CustomSuggestionBuilder doReadFrom(StreamInput in, String name) throws IOException {
+ return new CustomSuggestionBuilder(in.readString(), in.readString(), in.readString());
+ }
+
+ @Override
+ protected boolean doEquals(CustomSuggestionBuilder other) {
+ return Objects.equals(randomField, other.randomField) &&
+ Objects.equals(randomSuffix, other.randomSuffix);
+ }
+
+ @Override
+ protected int doHashCode() {
+ return Objects.hash(randomField, randomSuffix);
+ }
+
+ }
+
}
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilderTests.java b/core/src/test/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilderTests.java
new file mode 100644
index 0000000000..74f655b6aa
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestionBuilderTests.java
@@ -0,0 +1,114 @@
+/*
+ * 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.phrase;
+
+import org.elasticsearch.script.Template;
+import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class PhraseSuggestionBuilderTests extends AbstractSuggestionBuilderTestCase<PhraseSuggestionBuilder> {
+
+ @Override
+ protected PhraseSuggestionBuilder randomSuggestionBuilder() {
+ PhraseSuggestionBuilder testBuilder = new PhraseSuggestionBuilder(randomAsciiOfLength(10));
+ maybeSet(testBuilder::maxErrors, randomFloat());
+ maybeSet(testBuilder::separator, randomAsciiOfLengthBetween(1, 10));
+ maybeSet(testBuilder::realWordErrorLikelihood, randomFloat());
+ maybeSet(testBuilder::confidence, randomFloat());
+ maybeSet(testBuilder::collatePrune, randomBoolean());
+ maybeSet(testBuilder::collateQuery, randomAsciiOfLengthBetween(3, 20));
+ if (randomBoolean()) {
+ // preTag, postTag
+ testBuilder.highlight(randomAsciiOfLengthBetween(3, 20), randomAsciiOfLengthBetween(3, 20));
+ }
+ maybeSet(testBuilder::gramSize, randomIntBetween(1, 5));
+ maybeSet(testBuilder::forceUnigrams, randomBoolean());
+ maybeSet(testBuilder::tokenLimit, randomInt(20));
+ if (randomBoolean()) {
+ Map<String, Object> collateParams = new HashMap<>();
+ collateParams.put(randomAsciiOfLength(5), randomAsciiOfLength(5));
+ testBuilder.collateParams(collateParams );
+ }
+ if (randomBoolean()) {
+ // NORELEASE add random model
+ }
+
+ if (randomBoolean()) {
+ // NORELEASE add random generator
+ }
+ return testBuilder;
+ }
+
+ @Override
+ protected void mutateSpecificParameters(PhraseSuggestionBuilder builder) throws IOException {
+ switch (randomIntBetween(0, 7)) {
+ case 0:
+ builder.maxErrors(randomValueOtherThan(builder.maxErrors(), () -> randomFloat()));
+ break;
+ case 1:
+ builder.realWordErrorLikelihood(randomValueOtherThan(builder.realWordErrorLikelihood(), () -> randomFloat()));
+ break;
+ case 2:
+ builder.confidence(randomValueOtherThan(builder.confidence(), () -> randomFloat()));
+ break;
+ case 3:
+ builder.gramSize(randomValueOtherThan(builder.gramSize(), () -> randomIntBetween(1, 5)));
+ break;
+ case 4:
+ builder.tokenLimit(randomValueOtherThan(builder.tokenLimit(), () -> randomInt(20)));
+ break;
+ case 5:
+ builder.separator(randomValueOtherThan(builder.separator(), () -> randomAsciiOfLengthBetween(1, 10)));
+ break;
+ case 6:
+ Template collateQuery = builder.collateQuery();
+ if (collateQuery != null) {
+ builder.collateQuery(randomValueOtherThan(collateQuery.getScript(), () -> randomAsciiOfLengthBetween(3, 20)));
+ } else {
+ builder.collateQuery(randomAsciiOfLengthBetween(3, 20));
+ }
+ break;
+ case 7:
+ builder.collatePrune(builder.collatePrune() == null ? randomBoolean() : !builder.collatePrune() );
+ break;
+ case 8:
+ // preTag, postTag
+ String currentPre = builder.preTag();
+ if (currentPre != null) {
+ // simply double both values
+ builder.highlight(builder.preTag() + builder.preTag(), builder.postTag() + builder.postTag());
+ } else {
+ builder.highlight(randomAsciiOfLengthBetween(3, 20), randomAsciiOfLengthBetween(3, 20));
+ }
+ break;
+ case 9:
+ builder.forceUnigrams(builder.forceUnigrams() == null ? randomBoolean() : ! builder.forceUnigrams());
+ break;
+ case 10:
+ builder.collateParams().put(randomAsciiOfLength(5), randomAsciiOfLength(5));
+ break;
+ // TODO mutate random Model && generator
+ }
+ }
+
+}