Added caching support to geohash_filter

Caching is turned off by default.

Closes #6478
This commit is contained in:
Boaz Leskes 2014-06-12 12:09:52 +02:00
parent eabd4abf57
commit 7fb16c783d
3 changed files with 106 additions and 15 deletions

View File

@ -60,3 +60,13 @@ next to the given cell.
}
}
--------------------------------------------------
[float]
==== Caching
coming[1.3.0]
The result of the filter is not cached by default. The
`_cache` parameter can be set to `true` to turn caching on.
By default the filter uses the resulting geohash cells as a cache key.
This can be changed by using the `_cache_key` option.

View File

@ -32,6 +32,7 @@ import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.core.StringFieldMapper;
@ -61,6 +62,8 @@ public class GeohashCellFilter {
public static final String NAME = "geohash_cell";
public static final String NEIGHBORS = "neighbors";
public static final String PRECISION = "precision";
public static final String CACHE = "_cache";
public static final String CACHE_KEY = "_cache_key";
/**
* Create a new geohash filter for a given set of geohashes. In general this method
@ -100,6 +103,9 @@ public class GeohashCellFilter {
private String geohash;
private int levels = -1;
private boolean neighbors;
private Boolean cache;
private String cacheKey;
public Builder(String field) {
this(field, null, false);
@ -155,6 +161,19 @@ public class GeohashCellFilter {
return this;
}
/**
* Should the filter be cached or not. Defaults to <tt>false</tt>.
*/
public Builder cache(boolean cache) {
this.cache = cache;
return this;
}
public Builder cacheKey(String cacheKey) {
this.cacheKey = cacheKey;
return this;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
@ -164,6 +183,12 @@ public class GeohashCellFilter {
if(levels > 0) {
builder.field(PRECISION, levels);
}
if (cache != null) {
builder.field(CACHE, cache);
}
if (cacheKey != null) {
builder.field(CACHE_KEY, cacheKey);
}
builder.field(field, geohash);
builder.endObject();
@ -189,6 +214,9 @@ public class GeohashCellFilter {
String geohash = null;
int levels = -1;
boolean neighbors = false;
boolean cache = false;
CacheKeyFilter.Key cacheKey = null;
XContentParser.Token token;
if ((token = parser.currentToken()) != Token.START_OBJECT) {
@ -210,6 +238,12 @@ public class GeohashCellFilter {
} else if (NEIGHBORS.equals(field)) {
parser.nextToken();
neighbors = parser.booleanValue();
} else if (CACHE.equals(field)) {
parser.nextToken();
cache = parser.booleanValue();
} else if (CACHE_KEY.equals(field)) {
parser.nextToken();
cacheKey = new CacheKeyFilter.Key(parser.text());
} else {
fieldName = field;
token = parser.nextToken();
@ -254,11 +288,18 @@ public class GeohashCellFilter {
geohash = geohash.substring(0, len);
}
Filter filter;
if (neighbors) {
return create(parseContext, geoMapper, geohash, GeoHashUtils.addNeighbors(geohash, new ArrayList<CharSequence>(8)));
filter = create(parseContext, geoMapper, geohash, GeoHashUtils.addNeighbors(geohash, new ArrayList<CharSequence>(8)));
} else {
return create(parseContext, geoMapper, geohash, null);
filter = create(parseContext, geoMapper, geohash, null);
}
if (cache) {
filter = parseContext.cacheFilter(filter, cacheKey);
}
return filter;
}
}
}

View File

@ -34,6 +34,7 @@ import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.geo.GeoHashUtils;
import org.elasticsearch.common.geo.GeoPoint;
@ -44,6 +45,7 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.FilterBuilders;
import org.elasticsearch.index.query.GeohashCellFilter;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
@ -54,10 +56,7 @@ import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import java.util.*;
import java.util.zip.GZIPInputStream;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
@ -500,27 +499,68 @@ public class GeoFilterTests extends ElasticsearchIntegrationTest {
client().admin().indices().prepareRefresh("locations").execute().actionGet();
// Result of this geohash search should contain the geohash only
SearchResponse results1 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setPostFilter("{\"geohash_cell\": {\"pin\": \"" + geohash + "\", \"neighbors\": false}}").execute().actionGet();
assertHitCount(results1, 1);
Map<GeohashCellFilter.Builder, Long> expectedCounts = new HashMap<>();
Map<GeohashCellFilter.Builder, String[]> expectedResults = new HashMap<>();
Map<GeohashCellFilter.Builder, String> cacheKeys = new HashMap<>();
// test the same, just with the builder
results1 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(geoHashCellFilter("pin", geohash, false)).execute().actionGet();
assertHitCount(results1, 1);
expectedCounts.put(geoHashCellFilter("pin", geohash, false), 1L);
// 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()).setPostFilter("{\"geohash_cell\": {\"pin\": \"" + geohash.substring(0, geohash.length() - 1) + "\", \"neighbors\": true}}").execute().actionGet();
assertHitCount(results2, 2 + neighbors.size() + parentNeighbors.size());
expectedCounts.put(geoHashCellFilter("pin", geohash.substring(0, geohash.length() - 1), true), 2L + neighbors.size() + parentNeighbors.size());
// Testing point formats and precision
GeoPoint point = GeoHashUtils.decode(geohash);
int precision = geohash.length();
expectedCounts.put(geoHashCellFilter("pin", point).neighbors(true).precision(precision), 1L + neighbors.size());
logger.info("random testing of setting");
List<GeohashCellFilter.Builder> filterBuilders = new ArrayList<>(expectedCounts.keySet());
for (int j = filterBuilders.size() * 2 * randomIntBetween(1, 5); j > 0; j--) {
Collections.shuffle(filterBuilders, getRandom());
for (GeohashCellFilter.Builder builder : filterBuilders) {
if (randomBoolean()) {
builder.cache(randomBoolean());
}
if (randomBoolean()) {
String cacheKey = cacheKeys.get(builder);
if (cacheKey == null) {
cacheKey = randomUnicodeOfLength(6);
cacheKeys.put(builder, cacheKey);
}
builder.cacheKey(cacheKey);
} else {
builder.cacheKey(null);
}
try {
SearchResponse response = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(builder).get();
assertHitCount(response, expectedCounts.get(builder));
String[] expectedIds = expectedResults.get(builder);
if (expectedIds == null) {
ArrayList<String> ids = new ArrayList<>();
for (SearchHit hit : response.getHits()) {
ids.add(hit.id());
}
expectedResults.put(builder, ids.toArray(Strings.EMPTY_ARRAY));
continue;
}
assertSearchHits(response, expectedIds);
} catch (AssertionError error) {
throw new AssertionError(error.getMessage() + "\n geohash_cell filter:" + builder, error);
}
}
}
logger.info("Testing lat/lon format");
String pointTest1 = "{\"geohash_cell\": {\"pin\": {\"lat\": " + point.lat() + ",\"lon\": " + point.lon() + "},\"precision\": " + precision + ",\"neighbors\": true}}";
SearchResponse results3 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(pointTest1).execute().actionGet();
assertHitCount(results3, neighbors.size() + 1);
logger.info("Testing String format");
String pointTest2 = "{\"geohash_cell\": {\"pin\": \"" + point.lat() + "," + point.lon() + "\",\"precision\": " + precision + ",\"neighbors\": true}}";
SearchResponse results4 = client().prepareSearch("locations").setQuery(QueryBuilders.matchAllQuery()).setPostFilter(pointTest2).execute().actionGet();