mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-17 02:14:54 +00:00
parent
eacbd8f88d
commit
74bfa27e7e
@ -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();
|
||||
|
@ -358,10 +358,10 @@ public abstract class FilterBuilders {
|
||||
* must have <code>{"type":"geo_point", "geohash":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>{"type":"geo_point", "geohash":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>{"type":"geo_point", "geohash":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>{"type":"geo_point", "geohash":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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
* "geohash_bbox" {
|
||||
* "field":"location",
|
||||
@ -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());
|
@ -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();
|
||||
|
@ -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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user