Fail geohash_cell filter if geohash prefix is not enabled

closes #4335
This commit is contained in:
Shay Banon 2013-12-04 13:16:55 +01:00
parent eacbd8f88d
commit 74bfa27e7e
5 changed files with 91 additions and 93 deletions

View File

@ -213,8 +213,6 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser {
} else if (fieldName.equals("geohash_prefix")) {
builder.geohashPrefix(XContentMapValues.nodeBooleanValue(fieldNode));
if (XContentMapValues.nodeBooleanValue(fieldNode)) {
// automatically set geohash to true as well...
// TODO: Should we do this in the builder
builder.enableGeoHash(true);
}
} else if (fieldName.equals("precision_step")) {
@ -320,6 +318,10 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser {
return enableLatLon;
}
public boolean isEnableGeohashPrefix() {
return enableGeohashPrefix;
}
@Override
public void parse(ParseContext context) throws IOException {
ContentPath.Type origPathType = context.path().pathType();

View File

@ -358,10 +358,10 @@ public abstract class FilterBuilders {
* must have <code>{&quot;type&quot;:&quot;geo_point&quot;, &quot;geohash&quot;:true}</code>
* to work.
*
* @param fieldname The geopoint field name.
* @param name The geo point field name.
*/
public static GeohashFilter.Builder geoHashFilter(String fieldname) {
return new GeohashFilter.Builder(fieldname);
public static GeohashCellFilter.Builder geoHashCellFilter(String name) {
return new GeohashCellFilter.Builder(name);
}
/**
@ -369,11 +369,11 @@ public abstract class FilterBuilders {
* must have <code>{&quot;type&quot;:&quot;geo_point&quot;, &quot;geohash&quot;:true}</code>
* to work.
*
* @param fieldname The geopoint field name.
* @param name The geo point field name.
* @param geohash The Geohash to filter
*/
public static GeohashFilter.Builder geoHashFilter(String fieldname, String geohash) {
return new GeohashFilter.Builder(fieldname, geohash);
public static GeohashCellFilter.Builder geoHashCellFilter(String name, String geohash) {
return new GeohashCellFilter.Builder(name, geohash);
}
/**
@ -381,11 +381,11 @@ public abstract class FilterBuilders {
* must have <code>{&quot;type&quot;:&quot;geo_point&quot;, &quot;geohash&quot;:true}</code>
* to work.
*
* @param fieldname The geopoint field name.
* @param point a geopoint within the geohash bucket
* @param name The geo point field name.
* @param point a geo point within the geohash bucket
*/
public static GeohashFilter.Builder geoHashFilter(String fieldname, GeoPoint point) {
return new GeohashFilter.Builder(fieldname, point);
public static GeohashCellFilter.Builder geoHashCellFilter(String name, GeoPoint point) {
return new GeohashCellFilter.Builder(name, point);
}
/**
@ -393,12 +393,12 @@ public abstract class FilterBuilders {
* must have <code>{&quot;type&quot;:&quot;geo_point&quot;, &quot;geohash&quot;:true}</code>
* to work.
*
* @param fieldname The geopoint field name
* @param name The geo point field name
* @param geohash The Geohash to filter
* @param neighbors should the neighbor cell also be filtered
*/
public static GeohashFilter.Builder geoHashFilter(String fieldname, String geohash, boolean neighbors) {
return new GeohashFilter.Builder(fieldname, geohash, neighbors);
public static GeohashCellFilter.Builder geoHashCellFilter(String name, String geohash, boolean neighbors) {
return new GeohashCellFilter.Builder(name, geohash, neighbors);
}
/**

View File

@ -41,12 +41,12 @@ import java.io.IOException;
import java.util.List;
/**
* A gehash filter filters {@link GeoPoint}s by their geohashes. Basically the a
* A geohash cell filter that filters {@link GeoPoint}s by their geohashes. Basically the a
* Geohash prefix is defined by the filter and all geohashes that are matching this
* prefix will be returned. The <code>neighbors</code> flag allows to filter
* geohashes that surround the given geohash. In general the neighborhood of a
* geohash is defined by its eight adjacent cells.<br />
* The structure of the {@link GeohashFilter} is defined as:
* The structure of the {@link GeohashCellFilter} is defined as:
* <pre>
* &quot;geohash_bbox&quot; {
* &quot;field&quot;:&quot;location&quot;,
@ -55,7 +55,7 @@ import java.util.List;
* }
* </pre>
*/
public class GeohashFilter {
public class GeohashCellFilter {
public static final String NAME = "geohash_cell";
public static final String NEIGHBORS = "neighbors";
@ -95,62 +95,62 @@ public class GeohashFilter {
// because a transformation from a geohash to a point an back to the
// geohash will extend the accuracy of the hash to max precision
// i.e. by filing up with z's.
private String fieldname;
private String field;
private String geohash;
private int levels = -1;
private boolean neighbors;
public Builder(String fieldname) {
this(fieldname, null, false);
public Builder(String field) {
this(field, null, false);
}
public Builder(String fieldname, GeoPoint point) {
this(fieldname, point.geohash(), false);
public Builder(String field, GeoPoint point) {
this(field, point.geohash(), false);
}
public Builder(String fieldname, String geohash) {
this(fieldname, geohash, false);
public Builder(String field, String geohash) {
this(field, geohash, false);
}
public Builder(String fieldname, String geohash, boolean neighbors) {
public Builder(String field, String geohash, boolean neighbors) {
super();
this.fieldname = fieldname;
this.field = field;
this.geohash = geohash;
this.neighbors = neighbors;
}
public Builder setPoint(GeoPoint point) {
public Builder point(GeoPoint point) {
this.geohash = point.getGeohash();
return this;
}
public Builder setPoint(double lat, double lon) {
public Builder point(double lat, double lon) {
this.geohash = GeoHashUtils.encode(lat, lon);
return this;
}
public Builder setGeohash(String geohash) {
public Builder geohash(String geohash) {
this.geohash = geohash;
return this;
}
public Builder setPrecision(int levels) {
public Builder precision(int levels) {
this.levels = levels;
return this;
}
public Builder setPrecision(String precision) {
public Builder precision(String precision) {
double meters = DistanceUnit.parse(precision, DistanceUnit.METERS, DistanceUnit.METERS);
return setPrecision(GeoUtils.geoHashLevelsForPrecision(meters));
return precision(GeoUtils.geoHashLevelsForPrecision(meters));
}
public Builder setNeighbors(boolean neighbors) {
public Builder neighbors(boolean neighbors) {
this.neighbors = neighbors;
return this;
}
public Builder setField(String fieldname) {
this.fieldname = fieldname;
public Builder field(String field) {
this.field = field;
return this;
}
@ -163,7 +163,7 @@ public class GeohashFilter {
if(levels > 0) {
builder.field(PRECISION, levels);
}
builder.field(fieldname, geohash);
builder.field(field, geohash);
builder.endObject();
}
@ -229,6 +229,10 @@ public class GeohashFilter {
}
}
if (geohash == null) {
throw new QueryParsingException(parseContext.index(), "no geohash value provided to geohash_cell filter");
}
MapperService.SmartNameFieldMappers smartMappers = parseContext.smartFieldMappers(fieldName);
if (smartMappers == null || !smartMappers.hasMapper()) {
throw new QueryParsingException(parseContext.index(), "failed to find geo_point field [" + fieldName + "]");
@ -240,6 +244,9 @@ public class GeohashFilter {
}
GeoPointFieldMapper geoMapper = ((GeoPointFieldMapper.GeoStringFieldMapper) mapper).geoMapper();
if (!geoMapper.isEnableGeohashPrefix()) {
throw new QueryParsingException(parseContext.index(), "can't execute geohash_cell on field [" + fieldName + "], geohash_prefix is not enabled");
}
if(levels > 0) {
int len = Math.min(levels, geohash.length());

View File

@ -134,7 +134,7 @@ public class IndicesQueriesModule extends AbstractModule {
fpBinders.addBinding().to(GeoDistanceFilterParser.class).asEagerSingleton();
fpBinders.addBinding().to(GeoDistanceRangeFilterParser.class).asEagerSingleton();
fpBinders.addBinding().to(GeoBoundingBoxFilterParser.class).asEagerSingleton();
fpBinders.addBinding().to(GeohashFilter.Parser.class).asEagerSingleton();
fpBinders.addBinding().to(GeohashCellFilter.Parser.class).asEagerSingleton();
fpBinders.addBinding().to(GeoPolygonFilterParser.class).asEagerSingleton();
if (ShapesAvailability.JTS_AVAILABLE) {
fpBinders.addBinding().to(GeoShapeFilterParser.class).asEagerSingleton();

View File

@ -59,8 +59,7 @@ import java.util.Random;
import java.util.zip.GZIPInputStream;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.FilterBuilders.geoBoundingBoxFilter;
import static org.elasticsearch.index.query.FilterBuilders.geoDistanceFilter;
import static org.elasticsearch.index.query.FilterBuilders.*;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.hamcrest.Matchers.*;
@ -80,7 +79,7 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
disjointSupport = testRelationSupport(SpatialOperation.IsDisjointTo);
withinSupport = testRelationSupport(SpatialOperation.IsWithin);
}
private static byte[] unZipData(String path) throws IOException {
InputStream is = Streams.class.getResourceAsStream(path);
if (is == null) {
@ -103,27 +102,27 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
try {
// self intersection polygon
ShapeBuilder.newPolygon()
.point(-10, -10)
.point(10, 10)
.point(-10, 10)
.point(10, -10)
.close().build();
.point(-10, -10)
.point(10, 10)
.point(-10, 10)
.point(10, -10)
.close().build();
assert false : "Self intersection not detected";
} catch (InvalidShapeException e) {
}
// polygon with hole
ShapeBuilder.newPolygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
.close().close().build();
try {
// polygon with overlapping hole
ShapeBuilder.newPolygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-5, -5).point(-5, 11).point(5, 11).point(5, -5)
.close().close().build();
@ -134,8 +133,8 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
try {
// polygon with intersection holes
ShapeBuilder.newPolygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
.close()
.hole()
@ -149,14 +148,14 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
try {
// Common line in polygon
ShapeBuilder.newPolygon()
.point(-10, -10)
.point(-10, 10)
.point(-5, 10)
.point(-5, -5)
.point(-5, 20)
.point(10, 20)
.point(10, -10)
.close().build();
.point(-10, -10)
.point(-10, 10)
.point(-5, 10)
.point(-5, -5)
.point(-5, 20)
.point(10, 20)
.point(10, -10)
.close().build();
assert false : "Self intersection not detected";
} catch (InvalidShapeException e) {
}
@ -179,7 +178,7 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
// Multipolygon: polygon with hole and polygon within the whole
ShapeBuilder.newMultiPolygon()
.polygon()
.polygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
@ -238,9 +237,9 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
// with a hole of size 5x5 equidistant from all sides. This hole in turn contains
// the second polygon of size 4x4 equidistant from all sites
MultiPolygonBuilder polygon = ShapeBuilder.newMultiPolygon()
.polygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.polygon()
.point(-10, -10).point(-10, 10).point(10, 10).point(10, -10)
.hole()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
.close()
.close()
@ -249,7 +248,7 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
.close();
BytesReference data = jsonBuilder().startObject().field("area", polygon).endObject().bytes();
client().prepareIndex("shapes", "polygon", "1").setSource(data).execute().actionGet();
client().admin().indices().prepareRefresh().execute().actionGet();
@ -307,8 +306,8 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
// Create a polygon that fills the empty area of the polygon defined above
PolygonBuilder inverse = ShapeBuilder.newPolygon()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
.hole()
.point(-5, -5).point(-5, 5).point(5, 5).point(5, -5)
.hole()
.point(-4, -4).point(-4, 4).point(4, 4).point(4, -4)
.close()
.close();
@ -347,8 +346,8 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
// Create a polygon crossing longitude 180.
builder = ShapeBuilder.newPolygon()
.point(170, -10).point(190, -10).point(190, 10).point(170, 10)
.close();
.point(170, -10).point(190, -10).point(190, 10).point(170, 10)
.close();
data = jsonBuilder().startObject().field("area", builder).endObject().bytes();
client().prepareIndex("shapes", "polygon", "1").setSource(data).execute().actionGet();
@ -357,7 +356,7 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
// Create a polygon crossing longitude 180 with hole.
builder = ShapeBuilder.newPolygon()
.point(170, -10).point(190, -10).point(190, 10).point(170, 10)
.hole().point(175, -5).point(185,-5).point(185,5).point(175,5).close()
.hole().point(175, -5).point(185, -5).point(185, 5).point(175, 5).close()
.close();
data = jsonBuilder().startObject().field("area", builder).endObject().bytes();
@ -465,48 +464,34 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
}
@Test
public void testGeoHashFilter() throws IOException {
public void testGeohashCellFilter() throws IOException {
String geohash = randomhash(10);
logger.info("Testing geohash boundingbox filter for [{}]", geohash);
logger.info("Testing geohash_cell filter for [{}]", geohash);
List<String> neighbors = GeoHashUtils.neighbors(geohash);
List<String> parentNeighbors = GeoHashUtils.neighbors(geohash.substring(0, geohash.length() - 1));
logger.info("Neighbors {}", neighbors);
logger.info("Parent Neighbors {}", parentNeighbors);
String mapping = XContentFactory.jsonBuilder()
.startObject()
.startObject("location")
.startObject("properties")
.startObject("pin")
.field("type", "geo_point")
.field("geohash_prefix", true)
.field("latlon", false)
.endObject()
.endObject()
.endObject()
.endObject()
.string();
ensureYellow();
client().admin().indices().prepareCreate("locations").addMapping("location", mapping).execute().actionGet();
client().admin().indices().prepareCreate("locations").addMapping("location", "pin", "type=geo_point,geohash_prefix=true,latlon=false").execute().actionGet();
// Index a pin
client().prepareIndex("locations", "location", "1").setCreate(true).setSource("{\"pin\":\"" + geohash + "\"}").execute().actionGet();
client().prepareIndex("locations", "location", "1").setCreate(true).setSource("pin", geohash).execute().actionGet();
// index neighbors
for (int i = 0; i < neighbors.size(); i++) {
client().prepareIndex("locations", "location", "N" + i).setCreate(true).setSource("{\"pin\":\"" + neighbors.get(i) + "\"}").execute().actionGet();
client().prepareIndex("locations", "location", "N" + i).setCreate(true).setSource("pin", neighbors.get(i)).execute().actionGet();
}
// Index parent cell
client().prepareIndex("locations", "location", "p").setCreate(true).setSource("{\"pin\":\"" + geohash.substring(0, geohash.length() - 1) + "\"}").execute().actionGet();
client().prepareIndex("locations", "location", "p").setCreate(true).setSource("pin", geohash.substring(0, geohash.length() - 1)).execute().actionGet();
// index neighbors
for (int i = 0; i < parentNeighbors.size(); i++) {
client().prepareIndex("locations", "location", "p" + i).setCreate(true).setSource("{\"pin\":\"" + parentNeighbors.get(i) + "\"}").execute().actionGet();
client().prepareIndex("locations", "location", "p" + i).setCreate(true).setSource("pin", parentNeighbors.get(i)).execute().actionGet();
}
client().admin().indices().prepareRefresh("locations").execute().actionGet();
@ -515,6 +500,10 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
SearchResponse results1 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setFilter("{\"geohash_cell\": {\"pin\": \"" + geohash + "\", \"neighbors\": false}}").execute().actionGet();
assertHitCount(results1, 1);
// test the same, just with the builder
results1 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setFilter(geoHashCellFilter("pin", geohash, false)).execute().actionGet();
assertHitCount(results1, 1);
// Result of the parent query should contain the parent it self, its neighbors, the child and all its neighbors
SearchResponse results2 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setFilter("{\"geohash_cell\": {\"pin\": \"" + geohash.substring(0, geohash.length() - 1) + "\", \"neighbors\": true}}").execute().actionGet();
assertHitCount(results2, 2 + neighbors.size() + parentNeighbors.size());