/*
* 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 com.google.common.collect.Sets;
import org.apache.lucene.spatial.util.GeoHashUtils;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.suggest.SuggestRequest;
import org.elasticsearch.action.suggest.SuggestRequestBuilder;
import org.elasticsearch.action.suggest.SuggestResponse;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.search.suggest.Suggest.Suggestion;
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry;
import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionFuzzyBuilder;
import org.elasticsearch.search.suggest.context.ContextBuilder;
import org.elasticsearch.search.suggest.context.ContextMapping;
import org.elasticsearch.test.ESIntegTestCase;
import org.apache.lucene.util.LuceneTestCase.SuppressCodecs;
import org.hamcrest.Matchers;
import org.junit.Test;
import java.io.IOException;
import java.util.*;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertDistance;
import static org.hamcrest.Matchers.containsString;
@SuppressCodecs("*") // requires custom completion format
public class ContextSuggestSearchIT extends ESIntegTestCase {
private static final String INDEX = "test";
private static final String TYPE = "testType";
private static final String FIELD = "testField";
private static final String[][] HEROS = {
{ "Afari, Jamal", "Jamal Afari", "Jamal" },
{ "Allerdyce, St. John", "Allerdyce, John", "St. John", "St. John Allerdyce" },
{ "Beaubier, Jean-Paul", "Jean-Paul Beaubier", "Jean-Paul" },
{ "Beaubier, Jeanne-Marie", "Jeanne-Marie Beaubier", "Jeanne-Marie" },
{ "Braddock, Elizabeth \"Betsy\"", "Betsy", "Braddock, Elizabeth", "Elizabeth Braddock", "Elizabeth" },
{ "Cody Mushumanski gun Man", "the hunter", "gun man", "Cody Mushumanski" },
{ "Corbo, Adrian", "Adrian Corbo", "Adrian" },
{ "Corbo, Jared", "Jared Corbo", "Jared" },
{ "Creel, Carl \"Crusher\"", "Creel, Carl", "Crusher", "Carl Creel", "Carl" },
{ "Crichton, Lady Jacqueline Falsworth", "Lady Jacqueline Falsworth Crichton", "Lady Jacqueline Falsworth",
"Jacqueline Falsworth" }, { "Crichton, Kenneth", "Kenneth Crichton", "Kenneth" },
{ "MacKenzie, Al", "Al MacKenzie", "Al" },
{ "MacPherran, Mary \"Skeeter\"", "Mary MacPherran \"Skeeter\"", "MacPherran, Mary", "Skeeter", "Mary MacPherran" },
{ "MacTaggert, Moira", "Moira MacTaggert", "Moira" }, { "Rasputin, Illyana", "Illyana Rasputin", "Illyana" },
{ "Rasputin, Mikhail", "Mikhail Rasputin", "Mikhail" }, { "Rasputin, Piotr", "Piotr Rasputin", "Piotr" },
{ "Smythe, Alistair", "Alistair Smythe", "Alistair" }, { "Smythe, Spencer", "Spencer Smythe", "Spencer" },
{ "Whitemane, Aelfyre", "Aelfyre Whitemane", "Aelfyre" }, { "Whitemane, Kofi", "Kofi Whitemane", "Kofi" } };
@Test
public void testBasicGeo() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.location("st").precision("5km").neighbors(true))));
ensureYellow();
XContentBuilder source1 = jsonBuilder()
.startObject()
.startObject(FIELD)
.array("input", "Hotel Amsterdam", "Amsterdam")
.field("output", "Hotel Amsterdam in Berlin")
.startObject("context").latlon("st", 52.529172, 13.407333).endObject()
.endObject()
.endObject();
client().prepareIndex(INDEX, TYPE, "1").setSource(source1).execute().actionGet();
XContentBuilder source2 = jsonBuilder()
.startObject()
.startObject(FIELD)
.array("input", "Hotel Berlin", "Berlin")
.field("output", "Hotel Berlin in Amsterdam")
.startObject("context").latlon("st", 52.363389, 4.888695).endObject()
.endObject()
.endObject();
client().prepareIndex(INDEX, TYPE, "2").setSource(source2).execute().actionGet();
client().admin().indices().prepareRefresh(INDEX).get();
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text("h").size(10)
.addGeoLocation("st", 52.52, 13.4);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
assertEquals(suggestResponse.getSuggest().size(), 1);
assertEquals("Hotel Amsterdam in Berlin", suggestResponse.getSuggest().getSuggestion(suggestionName).iterator().next().getOptions().iterator().next().getText().string());
}
@Test
public void testMultiLevelGeo() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.location("st")
.precision(1)
.precision(2)
.precision(3)
.precision(4)
.precision(5)
.precision(6)
.precision(7)
.precision(8)
.precision(9)
.precision(10)
.precision(11)
.precision(12)
.neighbors(true))));
ensureYellow();
XContentBuilder source1 = jsonBuilder()
.startObject()
.startObject(FIELD)
.array("input", "Hotel Amsterdam", "Amsterdam")
.field("output", "Hotel Amsterdam in Berlin")
.startObject("context").latlon("st", 52.529172, 13.407333).endObject()
.endObject()
.endObject();
client().prepareIndex(INDEX, TYPE, "1").setSource(source1).execute().actionGet();
client().admin().indices().prepareRefresh(INDEX).get();
for (int precision = 1; precision <= 12; precision++) {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text("h").size(10)
.addGeoLocation("st", 52.529172, 13.407333, precision);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
assertEquals(suggestResponse.getSuggest().size(), 1);
assertEquals("Hotel Amsterdam in Berlin", suggestResponse.getSuggest().getSuggestion(suggestionName).iterator().next()
.getOptions().iterator().next().getText().string());
}
}
@Test
public void testMappingIdempotency() throws Exception {
List<Integer> precisions = new ArrayList<>();
for (int i = 0; i < randomIntBetween(4, 12); i++) {
precisions.add(i+1);
}
Collections.shuffle(precisions, getRandom());
XContentBuilder mapping = jsonBuilder().startObject().startObject(TYPE)
.startObject("properties").startObject("completion")
.field("type", "completion")
.startObject("context")
.startObject("location")
.field("type", "geo")
.array("precision", precisions.toArray(new Integer[precisions.size()]))
.endObject()
.endObject().endObject()
.endObject().endObject();
assertAcked(prepareCreate(INDEX).addMapping(TYPE, mapping.string()));
ensureYellow();
Collections.shuffle(precisions, getRandom());
mapping = jsonBuilder().startObject().startObject(TYPE)
.startObject("properties").startObject("completion")
.field("type", "completion")
.startObject("context")
.startObject("location")
.field("type", "geo")
.array("precision", precisions.toArray(new Integer[precisions.size()]))
.endObject()
.endObject().endObject()
.endObject().endObject();
assertAcked(client().admin().indices().preparePutMapping(INDEX).setType(TYPE).setSource(mapping.string()).get());
}
@Test
public void testGeoField() throws Exception {
XContentBuilder mapping = jsonBuilder();
mapping.startObject();
mapping.startObject(TYPE);
mapping.startObject("properties");
mapping.startObject("pin");
mapping.field("type", "geo_point");
mapping.endObject();
mapping.startObject(FIELD);
mapping.field("type", "completion");
mapping.field("analyzer", "simple");
mapping.startObject("context");
mapping.value(ContextBuilder.location("st", 5, true).field("pin").build());
mapping.endObject();
mapping.endObject();
mapping.endObject();
mapping.endObject();
mapping.endObject();
assertAcked(prepareCreate(INDEX).addMapping(TYPE, mapping));
ensureYellow();
XContentBuilder source1 = jsonBuilder()
.startObject()
.latlon("pin", 52.529172, 13.407333)
.startObject(FIELD)
.array("input", "Hotel Amsterdam", "Amsterdam")
.field("output", "Hotel Amsterdam in Berlin")
.startObject("context").endObject()
.endObject()
.endObject();
client().prepareIndex(INDEX, TYPE, "1").setSource(source1).execute().actionGet();
XContentBuilder source2 = jsonBuilder()
.startObject()
.latlon("pin", 52.363389, 4.888695)
.startObject(FIELD)
.array("input", "Hotel Berlin", "Berlin")
.field("output", "Hotel Berlin in Amsterdam")
.startObject("context").endObject()
.endObject()
.endObject();
client().prepareIndex(INDEX, TYPE, "2").setSource(source2).execute().actionGet();
refresh();
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text("h").size(10)
.addGeoLocation("st", 52.52, 13.4);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
assertEquals(suggestResponse.getSuggest().size(), 1);
assertEquals("Hotel Amsterdam in Berlin", suggestResponse.getSuggest().getSuggestion(suggestionName).iterator().next().getOptions().iterator().next().getText().string());
}
@Test
public void testSimpleGeo() throws Exception {
String reinickendorf = "u337p3mp11e2";
String pankow = "u33e0cyyjur4";
String koepenick = "u33dm4f7fn40";
String bernau = "u33etnjf1yjn";
String berlin = "u33dc1v0xupz";
String mitte = "u33dc0cpke4q";
String steglitz = "u336m36rjh2p";
String wilmersdorf = "u336wmw0q41s";
String spandau = "u336uqek7gh6";
String tempelhof = "u33d91jh3by0";
String schoeneberg = "u336xdrkzbq7";
String treptow = "u33d9unn7fp7";
double precision = 100.0; // meters
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.location("st").precision(precision).neighbors(true))));
ensureYellow();
String[] locations = { reinickendorf, pankow, koepenick, bernau, berlin, mitte, steglitz, wilmersdorf, spandau, tempelhof,
schoeneberg, treptow };
String[][] input = { { "pizza - reinickendorf", "pizza", "food" }, { "pizza - pankow", "pizza", "food" },
{ "pizza - koepenick", "pizza", "food" }, { "pizza - bernau", "pizza", "food" }, { "pizza - berlin", "pizza", "food" },
{ "pizza - mitte", "pizza - berlin mitte", "pizza", "food" },
{ "pizza - steglitz", "pizza - Berlin-Steglitz", "pizza", "food" }, { "pizza - wilmersdorf", "pizza", "food" },
{ "pizza - spandau", "spandau bei berlin", "pizza", "food" },
{ "pizza - tempelhof", "pizza - berlin-tempelhof", "pizza", "food" },
{ "pizza - schoeneberg", "pizza - schöneberg", "pizza - berlin schoeneberg", "pizza", "food" },
{ "pizza - treptow", "pizza", "food" } };
for (int i = 0; i < locations.length; i++) {
XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).field("input", input[i])
.startObject("context").field("st", locations[i]).endObject().field("payload", locations[i]).endObject().endObject();
client().prepareIndex(INDEX, TYPE, "" + i).setSource(source).execute().actionGet();
}
refresh();
assertGeoSuggestionsInRange(berlin, "pizza", precision);
assertGeoSuggestionsInRange(reinickendorf, "pizza", precision);
assertGeoSuggestionsInRange(spandau, "pizza", precision);
assertGeoSuggestionsInRange(koepenick, "pizza", precision);
assertGeoSuggestionsInRange(schoeneberg, "pizza", precision);
assertGeoSuggestionsInRange(tempelhof, "pizza", precision);
assertGeoSuggestionsInRange(bernau, "pizza", precision);
assertGeoSuggestionsInRange(pankow, "pizza", precision);
assertGeoSuggestionsInRange(mitte, "pizza", precision);
assertGeoSuggestionsInRange(steglitz, "pizza", precision);
assertGeoSuggestionsInRange(mitte, "pizza", precision);
assertGeoSuggestionsInRange(wilmersdorf, "pizza", precision);
assertGeoSuggestionsInRange(treptow, "pizza", precision);
}
@Test
public void testSimplePrefix() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.category("st"))));
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).field("input", HEROS[i])
.startObject("context").field("st", i%3).endObject()
.startObject("payload").field("group", i % 3).field("id", i).endObject()
.endObject().endObject();
client().prepareIndex(INDEX, TYPE, "" + i).setSource(source).execute().actionGet();
}
refresh();
assertPrefixSuggestions(0, "a", "Afari, Jamal", "Adrian Corbo", "Adrian");
assertPrefixSuggestions(0, "b", "Beaubier, Jeanne-Marie");
assertPrefixSuggestions(0, "c", "Corbo, Adrian", "Crichton, Lady Jacqueline Falsworth");
assertPrefixSuggestions(0, "mary", "Mary MacPherran \"Skeeter\"", "Mary MacPherran");
assertPrefixSuggestions(0, "s", "Skeeter", "Smythe, Spencer", "Spencer Smythe", "Spencer");
assertPrefixSuggestions(1, "s", "St. John", "St. John Allerdyce");
assertPrefixSuggestions(2, "s", "Smythe, Alistair");
assertPrefixSuggestions(1, "w", "Whitemane, Aelfyre");
assertPrefixSuggestions(2, "w", "Whitemane, Kofi");
}
@Test
public void testTypeCategoryIsActuallyCalledCategory() throws Exception {
XContentBuilder mapping = jsonBuilder();
mapping.startObject().startObject(TYPE).startObject("properties")
.startObject("suggest_field").field("type", "completion")
.startObject("context").startObject("color").field("type", "category").endObject().endObject()
.endObject()
.endObject().endObject().endObject();
assertAcked(prepareCreate(INDEX).addMapping(TYPE, mapping));
ensureYellow();
XContentBuilder doc1 = jsonBuilder();
doc1.startObject().startObject("suggest_field")
.field("input", "backpack_red")
.startObject("context").field("color", "red", "all_colors").endObject()
.endObject().endObject();
XContentBuilder doc2 = jsonBuilder();
doc2.startObject().startObject("suggest_field")
.field("input", "backpack_green")
.startObject("context").field("color", "green", "all_colors").endObject()
.endObject().endObject();
client().prepareIndex(INDEX, TYPE, "1")
.setSource(doc1).execute()
.actionGet();
client().prepareIndex(INDEX, TYPE, "2")
.setSource(doc2).execute()
.actionGet();
refresh();
getBackpackSuggestionAndCompare("all_colors", "backpack_red", "backpack_green");
getBackpackSuggestionAndCompare("red", "backpack_red");
getBackpackSuggestionAndCompare("green", "backpack_green");
getBackpackSuggestionAndCompare("not_existing_color");
}
private void getBackpackSuggestionAndCompare(String contextValue, String... expectedText) {
Set<String> expected = Sets.newHashSet(expectedText);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion("suggestion").field("suggest_field").text("back").size(10).addContextField("color", contextValue);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
Suggest suggest = suggestResponse.getSuggest();
assertEquals(suggest.size(), 1);
for (Suggestion<? extends Entry<? extends Option>> s : suggest) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
assertEquals(options.size(), expectedText.length);
for (CompletionSuggestion.Entry.Option option : options) {
assertTrue(expected.contains(option.getText().string()));
expected.remove(option.getText().string());
}
}
}
}
@Test
public void testBasic() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, false, ContextBuilder.reference("st", "_type"), ContextBuilder.reference("nd", "_type"))));
ensureYellow();
client().prepareIndex(INDEX, TYPE, "1")
.setSource(
jsonBuilder().startObject().startObject(FIELD).startArray("input").value("my hotel").value("this hotel").endArray()
.startObject("context").endObject()
.field("payload", TYPE + "|" + TYPE).endObject().endObject()).execute()
.actionGet();
refresh();
assertDoubleFieldSuggestions(TYPE, TYPE, "m", "my hotel");
}
@Test
public void testSimpleField() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.reference("st", "category"))));
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
client().prepareIndex(INDEX, TYPE, "" + i)
.setSource(
jsonBuilder().startObject().field("category", Integer.toString(i % 3)).startObject(FIELD).field("input", HEROS[i])
.startObject("context").endObject().field("payload", Integer.toString(i % 3))
.endObject().endObject()).execute().actionGet();
}
refresh();
assertFieldSuggestions("0", "a", "Afari, Jamal", "Adrian Corbo", "Adrian");
assertFieldSuggestions("0", "b", "Beaubier, Jeanne-Marie");
assertFieldSuggestions("0", "c", "Corbo, Adrian", "Crichton, Lady Jacqueline Falsworth");
assertFieldSuggestions("0", "mary", "Mary MacPherran \"Skeeter\"", "Mary MacPherran");
assertFieldSuggestions("0", "s", "Skeeter", "Smythe, Spencer", "Spencer Smythe", "Spencer");
assertFieldSuggestions("1", "s", "St. John", "St. John Allerdyce");
assertFieldSuggestions("2", "s", "Smythe, Alistair");
assertFieldSuggestions("1", "w", "Whitemane, Aelfyre");
assertFieldSuggestions("2", "w", "Whitemane, Kofi");
}
@Test // see issue #10987
public void testEmptySuggestion() throws Exception {
String mapping = jsonBuilder()
.startObject()
.startObject(TYPE)
.startObject("properties")
.startObject(FIELD)
.field("type", "completion")
.startObject("context")
.startObject("type_context")
.field("path", "_type")
.field("type", "category")
.endObject()
.endObject()
.endObject()
.endObject()
.endObject()
.endObject()
.string();
assertAcked(client().admin().indices().prepareCreate(INDEX).addMapping(TYPE, mapping).get());
ensureGreen();
client().prepareIndex(INDEX, TYPE, "1").setSource(FIELD, "")
.setRefresh(true).get();
}
@Test
public void testMultiValueField() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.reference("st", "category"))));
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
client().prepareIndex(INDEX, TYPE, "" + i)
.setSource(
jsonBuilder().startObject().startArray("category").value(Integer.toString(i % 3)).value("other").endArray()
.startObject(FIELD).array("input", HEROS[i]).startObject("context").endObject()
.field("payload", Integer.toString(i % 3)).endObject().endObject()).execute().actionGet();
}
refresh();
assertFieldSuggestions("0", "a", "Afari, Jamal", "Adrian Corbo", "Adrian");
assertFieldSuggestions("0", "b", "Beaubier, Jeanne-Marie");
assertFieldSuggestions("0", "c", "Corbo, Adrian", "Crichton, Lady Jacqueline Falsworth");
assertFieldSuggestions("0", "mary", "Mary MacPherran \"Skeeter\"", "Mary MacPherran");
assertFieldSuggestions("0", "s", "Skeeter", "Smythe, Spencer", "Spencer Smythe", "Spencer");
assertFieldSuggestions("1", "s", "St. John", "St. John Allerdyce");
assertFieldSuggestions("2", "s", "Smythe, Alistair");
assertFieldSuggestions("1", "w", "Whitemane, Aelfyre");
assertFieldSuggestions("2", "w", "Whitemane, Kofi");
}
@Test
public void testMultiContext() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.reference("st", "categoryA"), ContextBuilder.reference("nd", "categoryB"))));
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
client().prepareIndex(INDEX, TYPE, "" + i)
.setSource(
jsonBuilder().startObject().field("categoryA").value("" + (char) ('0' + (i % 3))).field("categoryB")
.value("" + (char) ('A' + (i % 3))).startObject(FIELD).array("input", HEROS[i])
.startObject("context").endObject().field("payload", ((char) ('0' + (i % 3))) + "" + (char) ('A' + (i % 3)))
.endObject().endObject()).execute().actionGet();
}
refresh();
assertMultiContextSuggestions("0", "A", "a", "Afari, Jamal", "Adrian Corbo", "Adrian");
assertMultiContextSuggestions("0", "A", "b", "Beaubier, Jeanne-Marie");
assertMultiContextSuggestions("0", "A", "c", "Corbo, Adrian", "Crichton, Lady Jacqueline Falsworth");
assertMultiContextSuggestions("0", "A", "mary", "Mary MacPherran \"Skeeter\"", "Mary MacPherran");
assertMultiContextSuggestions("0", "A", "s", "Skeeter", "Smythe, Spencer", "Spencer Smythe", "Spencer");
assertMultiContextSuggestions("1", "B", "s", "St. John", "St. John Allerdyce");
assertMultiContextSuggestions("2", "C", "s", "Smythe, Alistair");
assertMultiContextSuggestions("1", "B", "w", "Whitemane, Aelfyre");
assertMultiContextSuggestions("2", "C", "w", "Whitemane, Kofi");
}
@Test
public void testMultiContextWithFuzzyLogic() throws Exception {
assertAcked(prepareCreate(INDEX).addMapping(TYPE, createMapping(TYPE, ContextBuilder.reference("st", "categoryA"), ContextBuilder.reference("nd", "categoryB"))));
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
String source = jsonBuilder().startObject().field("categoryA", "" + (char) ('0' + (i % 3)))
.field("categoryB", "" + (char) ('a' + (i % 3))).startObject(FIELD).array("input", HEROS[i])
.startObject("context").endObject().startObject("payload").field("categoryA", "" + (char) ('0' + (i % 3)))
.field("categoryB", "" + (char) ('a' + (i % 3))).endObject().endObject().endObject().string();
client().prepareIndex(INDEX, TYPE, "" + i).setSource(source).execute().actionGet();
}
refresh();
String[] prefix1 = { "0", "1", "2" };
String[] prefix2 = { "a", "b", "c" };
String[] prefix3 = { "0", "1" };
String[] prefix4 = { "a", "b" };
assertContextWithFuzzySuggestions(prefix1, prefix2, "mary", "MacKenzie, Al", "MacPherran, Mary", "MacPherran, Mary \"Skeeter\"",
"MacTaggert, Moira", "Mary MacPherran", "Mary MacPherran \"Skeeter\"");
assertContextWithFuzzySuggestions(prefix1, prefix2, "mac", "Mikhail", "Mary MacPherran \"Skeeter\"", "MacTaggert, Moira",
"Moira MacTaggert", "Moira", "MacKenzie, Al", "Mary MacPherran", "Mikhail Rasputin", "MacPherran, Mary",
"MacPherran, Mary \"Skeeter\"");
assertContextWithFuzzySuggestions(prefix3, prefix4, "mary", "MacPherran, Mary", "MacPherran, Mary \"Skeeter\"",
"MacTaggert, Moira", "Mary MacPherran", "Mary MacPherran \"Skeeter\"");
assertContextWithFuzzySuggestions(prefix3, prefix4, "mac", "MacPherran, Mary", "MacPherran, Mary \"Skeeter\"", "MacTaggert, Moira",
"Mary MacPherran", "Mary MacPherran \"Skeeter\"", "Mikhail", "Mikhail Rasputin", "Moira", "Moira MacTaggert");
}
@Test
public void testSimpleType() throws Exception {
String[] types = { TYPE + "A", TYPE + "B", TYPE + "C" };
CreateIndexRequestBuilder createIndexRequestBuilder = prepareCreate(INDEX);
for (String type : types) {
createIndexRequestBuilder.addMapping(type, createMapping(type, ContextBuilder.reference("st", "_type")));
}
assertAcked(createIndexRequestBuilder);
ensureYellow();
for (int i = 0; i < HEROS.length; i++) {
String type = types[i % types.length];
client().prepareIndex(INDEX, type, "" + i)
.setSource(
jsonBuilder().startObject().startObject(FIELD).field("input", HEROS[i])
.startObject("context").endObject().field("payload", type).endObject().endObject()).execute().actionGet();
}
refresh();
assertFieldSuggestions(types[0], "a", "Afari, Jamal", "Adrian Corbo", "Adrian");
assertFieldSuggestions(types[0], "b", "Beaubier, Jeanne-Marie");
assertFieldSuggestions(types[0], "c", "Corbo, Adrian", "Crichton, Lady Jacqueline Falsworth");
assertFieldSuggestions(types[0], "mary", "Mary MacPherran \"Skeeter\"", "Mary MacPherran");
assertFieldSuggestions(types[0], "s", "Skeeter", "Smythe, Spencer", "Spencer Smythe", "Spencer");
assertFieldSuggestions(types[1], "s", "St. John", "St. John Allerdyce");
assertFieldSuggestions(types[2], "s", "Smythe, Alistair");
assertFieldSuggestions(types[1], "w", "Whitemane, Aelfyre");
assertFieldSuggestions(types[2], "w", "Whitemane, Kofi");
}
@Test // issue 5525, default location didnt work with lat/lon map, and did not set default location appropriately
public void testGeoContextDefaultMapping() throws Exception {
GeoPoint berlinAlexanderplatz = GeoPoint.fromGeohash("u33dc1");
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("poi").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", "500m")
.startObject("default").field("lat", berlinAlexanderplatz.lat()).field("lon", berlinAlexanderplatz.lon()).endObject()
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("poi", xContentBuilder));
ensureYellow();
index(INDEX, "poi", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Alexanderplatz").endObject().endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("b").size(10).addGeoLocation("location", berlinAlexanderplatz.lat(), berlinAlexanderplatz.lon());
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Berlin Alexanderplatz");
}
@Test // issue 5525, setting the path of a category context and then indexing a document without that field returned an error
public void testThatMissingPrefixesForContextReturnException() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("service").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("color")
.field("type", "category")
.field("path", "color")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("service", xContentBuilder));
ensureYellow();
// now index a document with color field
index(INDEX, "service", "1", jsonBuilder().startObject().field("color", "red").startObject("suggest").field("input", "backback").endObject().endObject());
// now index a document without a color field
try {
index(INDEX, "service", "2", jsonBuilder().startObject().startObject("suggest").field("input", "backback").endObject().endObject());
fail("index operation was not supposed to be successful");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("one or more prefixes needed"));
}
}
@Test // issue 5525, the geo point parser did not work when the lat/lon values were inside of a value object
public void testThatLocationVenueCanBeParsedAsDocumented() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("poi").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", "1m")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("poi", xContentBuilder));
ensureYellow();
SuggestRequest suggestRequest = new SuggestRequest(INDEX);
XContentBuilder builder = jsonBuilder().startObject()
.startObject("suggest")
.field("text", "m")
.startObject("completion")
.field("field", "suggest")
.startObject("context").startObject("location").startObject("value").field("lat", 0).field("lon", 0).endObject().field("precision", "1km").endObject().endObject()
.endObject()
.endObject()
.endObject();
suggestRequest.suggest(builder.bytes());
SuggestResponse suggestResponse = client().suggest(suggestRequest).get();
assertNoFailures(suggestResponse);
}
@Test
public void testThatCategoryDefaultWorks() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("color")
.field("type", "category").field("default", "red")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
index(INDEX, "item", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Hoodie red").endObject().endObject());
index(INDEX, "item", "2", jsonBuilder().startObject().startObject("suggest").field("input", "Hoodie blue").startObject("context").field("color", "blue").endObject().endObject().endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("h").size(10).addContextField("color", "red");
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Hoodie red");
}
@Test
public void testThatDefaultCategoryAndPathWorks() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("color")
.field("type", "category")
.field("default", "red")
.field("path", "color")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
index(INDEX, "item", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Hoodie red").endObject().endObject());
index(INDEX, "item", "2", jsonBuilder().startObject().startObject("suggest").field("input", "Hoodie blue").endObject().field("color", "blue").endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("h").size(10).addContextField("color", "red");
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Hoodie red");
}
@Test
public void testThatGeoPrecisionIsWorking() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", 4) // this means geo hashes with a length of four are used, like u345
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
// lets create some locations by geohashes in different cells with the precision 4
// this means, that poelchaustr is not a neighour to alexanderplatz, but they share the same prefix until the fourth char!
GeoPoint alexanderplatz = GeoPoint.fromGeohash("u33dc1");
GeoPoint poelchaustr = GeoPoint.fromGeohash("u33du5");
GeoPoint dahlem = GeoPoint.fromGeohash("u336q"); // berlin dahlem, should be included with that precision
GeoPoint middleOfNoWhere = GeoPoint.fromGeohash("u334"); // location for west from berlin, should not be included in any suggestions
index(INDEX, "item", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Alexanderplatz").field("weight", 3).startObject("context").startObject("location").field("lat", alexanderplatz.lat()).field("lon", alexanderplatz.lon()).endObject().endObject().endObject().endObject());
index(INDEX, "item", "2", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Poelchaustr.").field("weight", 2).startObject("context").startObject("location").field("lat", poelchaustr.lat()).field("lon", poelchaustr.lon()).endObject().endObject().endObject().endObject());
index(INDEX, "item", "3", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Far Away").field("weight", 1).startObject("context").startObject("location").field("lat", middleOfNoWhere.lat()).field("lon", middleOfNoWhere.lon()).endObject().endObject().endObject().endObject());
index(INDEX, "item", "4", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Dahlem").field("weight", 1).startObject("context").startObject("location").field("lat", dahlem.lat()).field("lon", dahlem.lon()).endObject().endObject().endObject().endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("b").size(10).addGeoLocation("location", alexanderplatz.lat(), alexanderplatz.lon());
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Berlin Alexanderplatz", "Berlin Poelchaustr.", "Berlin Dahlem");
}
@Test
public void testThatNeighborsCanBeExcluded() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", 6)
.field("neighbors", false)
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
GeoPoint alexanderplatz = GeoPoint.fromGeohash("u33dc1");
// does not look like it, but is a direct neighbor
// this test would fail, if the precision was set 4, as then both cells would be the same, u33d
GeoPoint cellNeighbourOfAlexanderplatz = GeoPoint.fromGeohash("u33dbc");
index(INDEX, "item", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Alexanderplatz").field("weight", 3).startObject("context").startObject("location").field("lat", alexanderplatz.lat()).field("lon", alexanderplatz.lon()).endObject().endObject().endObject().endObject());
index(INDEX, "item", "2", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Hackescher Markt").field("weight", 2).startObject("context").startObject("location").field("lat", cellNeighbourOfAlexanderplatz.lat()).field("lon", cellNeighbourOfAlexanderplatz.lon()).endObject().endObject().endObject().endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("b").size(10).addGeoLocation("location", alexanderplatz.lat(), alexanderplatz.lon());
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Berlin Alexanderplatz");
}
@Test
public void testThatGeoPathCanBeSelected() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", "5m")
.field("path", "loc")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
GeoPoint alexanderplatz = GeoPoint.fromGeohash("u33dc1");
index(INDEX, "item", "1", jsonBuilder().startObject().startObject("suggest").field("input", "Berlin Alexanderplatz").endObject().startObject("loc").field("lat", alexanderplatz.lat()).field("lon", alexanderplatz.lon()).endObject().endObject());
refresh();
CompletionSuggestionBuilder suggestionBuilder = SuggestBuilders.completionSuggestion("suggestion").field("suggest").text("b").size(10).addGeoLocation("location", alexanderplatz.lat(), alexanderplatz.lon());
SuggestResponse suggestResponse = client().prepareSuggest(INDEX).addSuggestion(suggestionBuilder).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Berlin Alexanderplatz");
}
@Test(expected = MapperParsingException.class)
public void testThatPrecisionIsRequired() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("item").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("path", "loc")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
}
@Test
public void testThatLatLonParsingFromSourceWorks() throws Exception {
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("mappings").startObject("test").startObject("properties").startObject("suggest_geo")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.field("precision", "1km")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject().endObject();
assertAcked(prepareCreate("test").setSource(xContentBuilder.bytes()));
double latitude = 52.22;
double longitude = 4.53;
String geohash = GeoHashUtils.stringEncode(longitude, latitude);
XContentBuilder doc1 = jsonBuilder().startObject().startObject("suggest_geo").field("input", "Hotel Marriot in Amsterdam").startObject("context").startObject("location").field("lat", latitude).field("lon", longitude).endObject().endObject().endObject().endObject();
index("test", "test", "1", doc1);
XContentBuilder doc2 = jsonBuilder().startObject().startObject("suggest_geo").field("input", "Hotel Marriot in Berlin").startObject("context").startObject("location").field("lat", 53.31).field("lon", 13.24).endObject().endObject().endObject().endObject();
index("test", "test", "2", doc2);
refresh();
XContentBuilder source = jsonBuilder().startObject().startObject("suggestion").field("text", "h").startObject("completion").field("field", "suggest_geo").startObject("context").field("location", geohash).endObject().endObject().endObject().endObject();
SuggestRequest suggestRequest = new SuggestRequest(INDEX).suggest(source.bytes());
SuggestResponse suggestResponse = client().suggest(suggestRequest).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Hotel Marriot in Amsterdam");
// this is exact the same request, but using lat/lon instead of geohash
source = jsonBuilder().startObject().startObject("suggestion").field("text", "h").startObject("completion").field("field", "suggest_geo").startObject("context").startObject("location").field("lat", latitude).field("lon", longitude).endObject().endObject().endObject().endObject().endObject();
suggestRequest = new SuggestRequest(INDEX).suggest(source.bytes());
suggestResponse = client().suggest(suggestRequest).get();
assertSuggestion(suggestResponse.getSuggest(), 0, "suggestion", "Hotel Marriot in Amsterdam");
}
public void assertGeoSuggestionsInRange(String location, String suggest, double precision) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text(suggest).size(10)
.addGeoLocation("st", location);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
Suggest suggest2 = suggestResponse.getSuggest();
assertTrue(suggest2.iterator().hasNext());
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
assertTrue(suggestion.iterator().hasNext());
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
assertTrue(options.iterator().hasNext());
for (CompletionSuggestion.Entry.Option option : options) {
String target = option.getPayloadAsString();
assertDistance(location, target, Matchers.lessThanOrEqualTo(precision));
}
}
}
}
public void assertPrefixSuggestions(long prefix, String suggest, String... hits) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text(suggest)
.size(hits.length + 1).addCategory("st", Long.toString(prefix));
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
ArrayList<String> suggestions = new ArrayList<>();
Suggest suggest2 = suggestResponse.getSuggest();
assertTrue(suggest2.iterator().hasNext());
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {
Map<String, Object> payload = option.getPayloadAsMap();
int group = (Integer) payload.get("group");
String text = option.getText().string();
assertEquals(prefix, group);
suggestions.add(text);
}
}
}
assertSuggestionsMatch(suggestions, hits);
}
public void assertContextWithFuzzySuggestions(String[] prefix1, String[] prefix2, String suggest, String... hits) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionFuzzyBuilder context = SuggestBuilders.fuzzyCompletionSuggestion(suggestionName).field(FIELD).text(suggest)
.size(hits.length + 10).addContextField("st", prefix1).addContextField("nd", prefix2).setFuzziness(Fuzziness.TWO);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
ArrayList<String> suggestions = new ArrayList<>();
Suggest suggest2 = suggestResponse.getSuggest();
assertTrue(suggest2.iterator().hasNext());
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {
Map<String, Object> payload = option.getPayloadAsMap();
String text = option.getText().string();
assertThat(prefix1, Matchers.hasItemInArray(payload.get("categoryA")));
assertThat(prefix2, Matchers.hasItemInArray(payload.get("categoryB")));
suggestions.add(text);
}
}
}
assertSuggestionsMatch(suggestions, hits);
}
public void assertFieldSuggestions(String value, String suggest, String... hits) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", value);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
ArrayList<String> suggestions = new ArrayList<>();
Suggest suggest2 = suggestResponse.getSuggest();
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {
String payload = option.getPayloadAsString();
String text = option.getText().string();
assertEquals(value, payload);
suggestions.add(text);
}
}
}
assertSuggestionsMatch(suggestions, hits);
}
public void assertDoubleFieldSuggestions(String field1, String field2, String suggest, String... hits) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", field1).addContextField("nd", field2);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
ArrayList<String> suggestions = new ArrayList<>();
Suggest suggest2 = suggestResponse.getSuggest();
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {
String payload = option.getPayloadAsString();
String text = option.getText().string();
assertEquals(field1 + "|" + field2, payload);
suggestions.add(text);
}
}
}
assertSuggestionsMatch(suggestions, hits);
}
public void assertMultiContextSuggestions(String value1, String value2, String suggest, String... hits) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = SuggestBuilders.completionSuggestion(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", value1).addContextField("nd", value2);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
SuggestResponse suggestResponse = suggestionRequest.execute().actionGet();
ArrayList<String> suggestions = new ArrayList<>();
Suggest suggest2 = suggestResponse.getSuggest();
for (Suggestion<? extends Entry<? extends Option>> s : suggest2) {
CompletionSuggestion suggestion = (CompletionSuggestion) s;
for (CompletionSuggestion.Entry entry : suggestion) {
List<CompletionSuggestion.Entry.Option> options = entry.getOptions();
for (CompletionSuggestion.Entry.Option option : options) {
String payload = option.getPayloadAsString();
String text = option.getText().string();
assertEquals(value1 + value2, payload);
suggestions.add(text);
}
}
}
assertSuggestionsMatch(suggestions, hits);
}
private void assertSuggestionsMatch(List<String> suggestions, String... hits) {
boolean[] suggested = new boolean[hits.length];
Arrays.sort(hits);
Arrays.fill(suggested, false);
int numSuggestions = 0;
for (String suggestion : suggestions) {
int hitpos = Arrays.binarySearch(hits, suggestion);
assertEquals(hits[hitpos], suggestion);
assertTrue(hitpos >= 0);
assertTrue(!suggested[hitpos]);
suggested[hitpos] = true;
numSuggestions++;
}
assertEquals(hits.length, numSuggestions);
}
private XContentBuilder createMapping(String type, ContextBuilder<?>... context) throws IOException {
return createMapping(type, false, context);
}
private XContentBuilder createMapping(String type, boolean preserveSeparators, ContextBuilder<?>... context) throws IOException {
return createMapping(type, "simple", "simple", true, preserveSeparators, true, context);
}
private XContentBuilder createMapping(String type, String indexAnalyzer, String searchAnalyzer, boolean payloads, boolean preserveSeparators,
boolean preservePositionIncrements, ContextBuilder<?>... contexts) throws IOException {
XContentBuilder mapping = jsonBuilder();
mapping.startObject();
mapping.startObject(type);
mapping.startObject("properties");
mapping.startObject(FIELD);
mapping.field("type", "completion");
mapping.field("analyzer", indexAnalyzer);
mapping.field("search_analyzer", searchAnalyzer);
mapping.field("payloads", payloads);
mapping.field("preserve_separators", preserveSeparators);
mapping.field("preserve_position_increments", preservePositionIncrements);
mapping.startObject("context");
for (ContextBuilder<? extends ContextMapping> context : contexts) {
mapping.value(context.build());
}
mapping.endObject();
mapping.endObject();
mapping.endObject();
mapping.endObject();
mapping.endObject();
return mapping;
}
}