ContextSuggester: Adding couple of tests to catch more bugs

A bunch of minor fixes have been included here, especially due
to wrongly parsed mappings. Also using assertions resulted in an
NPE because they were disabled in the distribution.

Closes #5525
This commit is contained in:
Alexander Reelsen 2014-03-31 16:13:18 +02:00
parent f424319f9a
commit 0ff30ade69
6 changed files with 534 additions and 47 deletions

View File

@ -0,0 +1,219 @@
# This test creates one huge mapping in the setup
# Every test should use its own field to make sure it works
setup:
- do:
indices.create:
index: test
body:
mappings:
test:
"properties":
"suggest_context":
"type" : "completion"
"context":
"color":
"type" : "category"
"suggest_context_default_hardcoded":
"type" : "completion"
"context":
"color":
"type" : "category"
"default" : "red"
"suggest_context_default_path":
"type" : "completion"
"context":
"color":
"type" : "category"
"default" : "red"
"path" : "color"
"suggest_geo":
"type" : "completion"
"context":
"location":
"type" : "geo"
---
"Simple context suggestion should work":
- do:
index:
index: test
type: test
id: 1
body:
suggest_context:
input: "Hoodie red"
context:
color: "red"
- do:
index:
index: test
type: test
id: 2
body:
suggest_context:
input: "Hoodie blue"
context:
color: "blue"
- do:
indices.refresh: {}
- do:
suggest:
body:
result:
text: "hoo"
completion:
field: suggest_context
context:
color: "red"
- match: {result.0.options.0.text: "Hoodie red" }
---
"Hardcoded category value should work":
- do:
index:
index: test
type: test
id: 1
body:
suggest_context_default_hardcoded:
input: "Hoodie red"
- do:
index:
index: test
type: test
id: 2
body:
suggest_context_default_hardcoded:
input: "Hoodie blue"
context:
color: "blue"
- do:
indices.refresh: {}
- do:
suggest:
body:
result:
text: "hoo"
completion:
field: suggest_context_default_hardcoded
context:
color: "red"
- length: { result: 1 }
- length: { result.0.options: 1 }
- match: { result.0.options.0.text: "Hoodie red" }
---
"Category suggest context default path should work":
- do:
index:
index: test
type: test
id: 1
body:
suggest_context_default_path:
input: "Hoodie red"
- do:
index:
index: test
type: test
id: 2
body:
suggest_context_default_path:
input: "Hoodie blue"
color: "blue"
- do:
indices.refresh: {}
- do:
suggest:
body:
result:
text: "hoo"
completion:
field: suggest_context_default_path
context:
color: "red"
- length: { result: 1 }
- length: { result.0.options: 1 }
- match: { result.0.options.0.text: "Hoodie red" }
- do:
suggest:
body:
result:
text: "hoo"
completion:
field: suggest_context_default_path
context:
color: "blue"
- length: { result: 1 }
- length: { result.0.options: 1 }
- match: { result.0.options.0.text: "Hoodie blue" }
---
"Geo suggest should work":
- do:
index:
index: test
type: test
id: 1
body:
suggest_geo:
input: "Hotel Marriot in Amsterdam"
context:
location:
lat : 52.22
lon : 4.53
- do:
index:
index: test
type: test
id: 2
body:
suggest_geo:
input: "Hotel Marriot in Berlin"
context:
location:
lat : 53.31
lon : 13.24
- do:
indices.refresh: {}
- do:
suggest:
body:
result:
text: "hote"
completion:
field: suggest_geo
context:
location:
lat : 52.22
lon : 4.53
- length: { result: 1 }
- length: { result.0.options: 1 }
- match: { result.0.options.0.text: "Hotel Marriot in Amsterdam" }

View File

@ -21,6 +21,7 @@ package org.apache.lucene.analysis;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import java.io.IOException;
import java.io.Reader;
@ -96,7 +97,9 @@ public class PrefixAnalyzer extends Analyzer {
this.prefixes = prefixes;
this.currentPrefix = null;
this.separator = separator;
assert (prefixes != null && prefixes.iterator().hasNext()) : "one or more prefix needed";
if (prefixes == null || !prefixes.iterator().hasNext()) {
throw new ElasticsearchIllegalArgumentException("one or more prefixes needed");
}
}
@Override

View File

@ -33,7 +33,7 @@ import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
/**
* Defines how to perform suggesting. This builders allows a number of global options to be specified and
* an arbitrary number of {@link org.elasticsearch.search.suggest.SuggestBuilder.TermSuggestionBuilder} instances.
* an arbitrary number of {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder} instances.
* <p/>
* Suggesting works by suggesting terms that appear in the suggest text that are similar compared to the terms in
* provided text. These spelling suggestions are based on several options described in this class.
@ -66,7 +66,7 @@ public class SuggestBuilder implements ToXContent {
}
/**
* Adds an {@link org.elasticsearch.search.suggest.SuggestBuilder.TermSuggestionBuilder} instance under a user defined name.
* Adds an {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder} instance under a user defined name.
* The order in which the <code>Suggestions</code> are added, is the same as in the response.
*/
public SuggestBuilder addSuggestion(SuggestionBuilder<?> suggestion) {
@ -141,17 +141,28 @@ public class SuggestBuilder implements ToXContent {
}
/**
* Setup a Geolocation for suggestions. See {@link GeoContextMapping}.
* Setup a Geolocation for suggestions. See {@link GeolocationContextMapping}.
* @param lat Latitude of the location
* @param lon Longitude of the Location
* @return this
*/
public T addGeoLocation(String name, double lat, double lon) {
return addContextQuery(GeolocationContextMapping.query(name, lat, lon));
public T addGeoLocation(String name, double lat, double lon, int ... precisions) {
return addContextQuery(GeolocationContextMapping.query(name, lat, lon, precisions));
}
/**
* Setup a Geolocation for suggestions. See {@link GeoContextMapping}.
* Setup a Geolocation for suggestions. See {@link GeolocationContextMapping}.
* @param lat Latitude of the location
* @param lon Longitude of the Location
* @param precisions precisions as string var-args
* @return this
*/
public T addGeoLocationWithPrecision(String name, double lat, double lon, String ... precisions) {
return addContextQuery(GeolocationContextMapping.query(name, lat, lon, precisions));
}
/**
* Setup a Geolocation for suggestions. See {@link GeolocationContextMapping}.
* @param geohash Geohash of the location
* @return this
*/
@ -160,8 +171,8 @@ public class SuggestBuilder implements ToXContent {
}
/**
* Setup a Category for suggestions. See {@link CategoryMapping}.
* @param category name of the category
* Setup a Category for suggestions. See {@link CategoryContextMapping}.
* @param categories name of the category
* @return this
*/
public T addCategory(String name, CharSequence...categories) {
@ -169,8 +180,8 @@ public class SuggestBuilder implements ToXContent {
}
/**
* Setup a Category for suggestions. See {@link CategoryMapping}.
* @param category name of the category
* Setup a Category for suggestions. See {@link CategoryContextMapping}.
* @param categories name of the category
* @return this
*/
public T addCategory(String name, Iterable<? extends CharSequence> categories) {
@ -179,7 +190,7 @@ public class SuggestBuilder implements ToXContent {
/**
* Setup a Context Field for suggestions. See {@link CategoryContextMapping}.
* @param category name of the category
* @param fieldvalues name of the category
* @return this
*/
public T addContextField(String name, CharSequence...fieldvalues) {
@ -188,7 +199,7 @@ public class SuggestBuilder implements ToXContent {
/**
* Setup a Context Field for suggestions. See {@link CategoryContextMapping}.
* @param category name of the category
* @param fieldvalues name of the category
* @return this
*/
public T addContextField(String name, Iterable<? extends CharSequence> fieldvalues) {
@ -242,7 +253,7 @@ public class SuggestBuilder implements ToXContent {
/**
* 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.SuggestBuilder.TermSuggestionBuilder#setField(String)}
* {@link org.elasticsearch.search.suggest.term.TermSuggestionBuilder#field(String)}
* method
*/
@SuppressWarnings("unchecked")

View File

@ -19,6 +19,7 @@
package org.elasticsearch.search.suggest.context;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.apache.lucene.analysis.PrefixAnalyzer;
@ -235,16 +236,18 @@ public class CategoryContextMapping extends ContextMapping {
@Override
protected TokenStream wrapTokenStream(Document doc, TokenStream stream) {
if(values != null) {
if (values != null) {
return new PrefixAnalyzer.PrefixTokenFilter(stream, ContextMapping.SEPARATOR, values);
// if fieldname is default, BUT our default values are set, we take that one
} else if ((doc.getFields(fieldname).length == 0 || fieldname.equals(DEFAULT_FIELDNAME)) && defaultValues.iterator().hasNext()) {
return new PrefixAnalyzer.PrefixTokenFilter(stream, ContextMapping.SEPARATOR, defaultValues);
} else {
IndexableField[] fields = doc.getFields(fieldname);
ArrayList<CharSequence> values = new ArrayList<>(fields.length);
for (int i = 0; i < fields.length; i++) {
values.add(fields[i].stringValue());
}
return new PrefixAnalyzer.PrefixTokenFilter(stream, ContextMapping.SEPARATOR, values);
}
}
@ -252,12 +255,11 @@ public class CategoryContextMapping extends ContextMapping {
@Override
public String toString() {
StringBuilder sb = new StringBuilder("FieldConfig(" + fieldname + " = [");
Iterator<? extends CharSequence> value = this.defaultValues.iterator();
if (value.hasNext()) {
sb.append(value.next());
while (value.hasNext()) {
sb.append(", ").append(value.next());
}
if (this.values != null && this.values.iterator().hasNext()) {
sb.append("(").append(Joiner.on(", ").join(this.values.iterator())).append(")");
}
if (this.defaultValues != null && this.defaultValues.iterator().hasNext()) {
sb.append(" default(").append(Joiner.on(", ").join(this.defaultValues.iterator())).append(")");
}
return sb.append("])").toString();
}

View File

@ -83,7 +83,7 @@ public class GeolocationContextMapping extends ContextMapping {
* length of the geohashes
* @param neighbors
* should neighbors be indexed
* @param defaultLocation
* @param defaultLocations
* location to use, if it is not provided by the document
*/
protected GeolocationContextMapping(String name, int[] precision, boolean neighbors, Collection<String> defaultLocations, String fieldName) {
@ -158,7 +158,16 @@ public class GeolocationContextMapping extends ContextMapping {
builder.addDefaultLocation(location.toString());
}
} else if (def instanceof String) {
builder.addDefaultLocation(def.toString());
builder.addDefaultLocation(def.toString());
} else if (def instanceof Map) {
Map<String, Object> latlonMap = (Map<String, Object>) def;
if (!latlonMap.containsKey("lat") || !(latlonMap.get("lat") instanceof Double)) {
throw new ElasticsearchParseException("field [" + FIELD_MISSING + "] map must have field lat and a valid latitude");
}
if (!latlonMap.containsKey("lon") || !(latlonMap.get("lon") instanceof Double)) {
throw new ElasticsearchParseException("field [" + FIELD_MISSING + "] map must have field lon and a valid longitude");
}
builder.addDefaultLocation(Double.valueOf(latlonMap.get("lat").toString()), Double.valueOf(latlonMap.get("lon").toString()));
} else {
throw new ElasticsearchParseException("field [" + FIELD_MISSING + "] must be of type string or list");
}
@ -264,8 +273,16 @@ public class GeolocationContextMapping extends ContextMapping {
* longitude of the location
* @return new geolocation query
*/
public static GeoQuery query(String name, double lat, double lon) {
return query(name, GeoHashUtils.encode(lat, lon));
public static GeoQuery query(String name, double lat, double lon, int ... precisions) {
return query(name, GeoHashUtils.encode(lat, lon), precisions);
}
public static GeoQuery query(String name, double lat, double lon, String ... precisions) {
int precisionInts[] = new int[precisions.length];
for (int i = 0 ; i < precisions.length; i++) {
precisionInts[i] = GeoUtils.geoHashLevelsForPrecision(precisions[i]);
}
return query(name, GeoHashUtils.encode(lat, lon), precisionInts);
}
/**
@ -275,8 +292,8 @@ public class GeolocationContextMapping extends ContextMapping {
* geohash of the location
* @return new geolocation query
*/
public static GeoQuery query(String name, String geohash) {
return new GeoQuery(name, geohash);
public static GeoQuery query(String name, String geohash, int ... precisions) {
return new GeoQuery(name, geohash, precisions);
}
private static final int parsePrecision(XContentParser parser) throws IOException, ElasticsearchParseException {
@ -338,6 +355,7 @@ public class GeolocationContextMapping extends ContextMapping {
}
} else if (FIELD_VALUE.equals(fieldName)) {
if(Double.isNaN(lon) && Double.isNaN(lat)) {
parser.nextToken();
point = GeoUtils.parseGeoPoint(parser);
} else {
throw new ElasticsearchParseException("only lat/lon or [" + FIELD_VALUE + "] is allowed");
@ -450,7 +468,7 @@ public class GeolocationContextMapping extends ContextMapping {
/**
* Set the precision use o make suggestions
*
* @param precision
* @param meters
* precision as distance in meters
* @return this
*/
@ -466,7 +484,7 @@ public class GeolocationContextMapping extends ContextMapping {
/**
* Set the precision use o make suggestions
*
* @param precision
* @param level
* maximum length of geohashes
* @return this
*/
@ -504,7 +522,7 @@ public class GeolocationContextMapping extends ContextMapping {
* Set a default location that should be used, if no location is
* provided by the query
*
* @param geohash
* @param geohashes
* geohash of the default location
* @return this
*/
@ -578,15 +596,28 @@ public class GeolocationContextMapping extends ContextMapping {
if (locations == null || locations.size() == 0) {
if(mapping.fieldName != null) {
IndexableField[] fields = doc.getFields(mapping.fieldName);
if(fields.length > 0) {
if(fields.length == 0) {
IndexableField[] lonFields = doc.getFields(mapping.fieldName + ".lon");
IndexableField[] latFields = doc.getFields(mapping.fieldName + ".lat");
if (lonFields.length > 0 && latFields.length > 0) {
geohashes = new ArrayList<>(fields.length);
GeoPoint spare = new GeoPoint();
for (int i = 0 ; i < lonFields.length ; i++) {
IndexableField lonField = lonFields[i];
IndexableField latField = latFields[i];
spare.reset(latField.numericValue().doubleValue(), lonField.numericValue().doubleValue());
geohashes.add(spare.geohash());
}
} else {
geohashes = mapping.defaultLocations;
}
} else {
geohashes = new ArrayList<>(fields.length);
GeoPoint spare = new GeoPoint();
for (IndexableField field : fields) {
spare.resetFromString(field.stringValue());
geohashes.add(spare.geohash());
}
} else {
geohashes = mapping.defaultLocations;
}
} else {
geohashes = mapping.defaultLocations;

View File

@ -18,11 +18,14 @@
*/
package org.elasticsearch.search.suggest;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import com.google.common.collect.Sets;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
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.GeoHashUtils;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.search.suggest.Suggest.Suggestion;
@ -35,14 +38,16 @@ import org.elasticsearch.search.suggest.context.ContextBuilder;
import org.elasticsearch.search.suggest.context.ContextMapping;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.hamcrest.Matchers;
import org.junit.Ignore;
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.assertAcked;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertDistance;
import static org.hamcrest.Matchers.containsString;
public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
@ -96,7 +101,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
client().admin().indices().prepareRefresh(INDEX).get();
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text("h").size(10)
.addGeoLocation("st", 52.52, 13.4);
@ -106,7 +111,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
assertEquals(suggestResponse.getSuggest().size(), 1);
assertEquals("Hotel Amsterdam in Berlin", suggestResponse.getSuggest().getSuggestion(suggestionName).iterator().next().getOptions().iterator().next().getText().string());
}
@Test
public void testGeoField() throws Exception {
@ -158,7 +163,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
refresh();
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text("h").size(10)
.addGeoLocation("st", 52.52, 13.4);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
@ -466,9 +471,225 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
assertFieldSuggestions(types[2], "w", "Whitemane, Kofi");
}
public void assertGeoSuggestionsInRange(String location, String suggest, double precision) throws IOException {
@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 = GeoHashUtils.decode("u33dc1");
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
XContentBuilder xContentBuilder = jsonBuilder().startObject()
.startObject("poi").startObject("properties").startObject("suggest")
.field("type", "completion")
.startObject("context").startObject("location")
.field("type", "geo")
.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 = new CompletionSuggestionBuilder("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 succesful");
} catch (ElasticsearchIllegalArgumentException 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")
.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 = new CompletionSuggestionBuilder("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 = new CompletionSuggestionBuilder("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 = GeoHashUtils.decode("u33dc1");
GeoPoint poelchaustr = GeoHashUtils.decode("u33du5");
GeoPoint dahlem = GeoHashUtils.decode("u336q"); // berlin dahlem, should be included with that precision
GeoPoint middleOfNoWhere = GeoHashUtils.decode("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 = new CompletionSuggestionBuilder("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 = GeoHashUtils.decode("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 = GeoHashUtils.decode("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 = new CompletionSuggestionBuilder("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("path", "loc")
.endObject().endObject()
.endObject().endObject().endObject()
.endObject();
assertAcked(prepareCreate(INDEX).addMapping("item", xContentBuilder));
ensureYellow();
GeoPoint alexanderplatz = GeoHashUtils.decode("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 = new CompletionSuggestionBuilder("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");
}
public void assertGeoSuggestionsInRange(String location, String suggest, double precision) throws IOException {
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text(suggest).size(10)
.addGeoLocation("st", location);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
@ -491,7 +712,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
}
public void assertPrefixSuggestions(long prefix, String suggest, String... hits) throws IOException {
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text(suggest)
.size(hits.length + 1).addCategory("st", Long.toString(prefix));
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
@ -516,7 +737,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
}
public void assertContextWithFuzzySuggestions(String[] prefix1, String[] prefix2, String suggest, String... hits) throws IOException {
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionFuzzyBuilder context = new CompletionSuggestionFuzzyBuilder(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);
@ -544,7 +765,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
}
public void assertFieldSuggestions(String value, String suggest, String... hits) throws IOException {
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", value);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
@ -569,7 +790,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
}
public void assertDoubleFieldSuggestions(String field1, String field2, String suggest, String... hits) throws IOException {
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", field1).addContextField("nd", field2);
SuggestRequestBuilder suggestionRequest = client().prepareSuggest(INDEX).addSuggestion(context);
@ -593,7 +814,7 @@ public class ContextSuggestSearchTests extends ElasticsearchIntegrationTest {
}
public void assertMultiContextSuggestions(String value1, String value2, String suggest, String... hits) throws IOException {
String suggestionName = RandomStrings.randomAsciiOfLength(new Random(), 10);
String suggestionName = randomAsciiOfLength(10);
CompletionSuggestionBuilder context = new CompletionSuggestionBuilder(suggestionName).field(FIELD).text(suggest).size(10)
.addContextField("st", value1).addContextField("nd", value2);