improve geo internal cache of lat/lon, stored two double arrays instead of an array of GeoPoints.

This commit is contained in:
kimchy 2010-10-13 22:03:48 +02:00
parent 1f4aa5d9d2
commit 1578da404c
11 changed files with 272 additions and 87 deletions

View File

@ -75,8 +75,10 @@
<w>joda</w> <w>joda</w>
<w>jsonp</w> <w>jsonp</w>
<w>kimchy</w> <w>kimchy</w>
<w>latlon</w>
<w>lifecycle</w> <w>lifecycle</w>
<w>linefeeds</w> <w>linefeeds</w>
<w>lons</w>
<w>loopback</w> <w>loopback</w>
<w>lucene</w> <w>lucene</w>
<w>mcast</w> <w>mcast</w>

View File

@ -24,7 +24,6 @@ import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.elasticsearch.common.lucene.docset.GetDocSet; import org.elasticsearch.common.lucene.docset.GetDocSet;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
@ -82,30 +81,34 @@ public class GeoBoundingBoxFilter extends Filter {
} }
if (fieldData.multiValued()) { if (fieldData.multiValued()) {
GeoPoint[] points = fieldData.values(doc); double[] lats = fieldData.latValues(doc);
for (GeoPoint point : points) { double[] lons = fieldData.lonValues(doc);
if (point.lon() < 0) { for (int i = 0; i < lats.length; i++) {
if (-180.0 <= point.lon() && bottomRight.lon >= point.lon() double lat = lats[i];
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { double lon = lons[i];
if (lon < 0) {
if (-180.0 <= lon && bottomRight.lon >= lon
&& topLeft.lat >= lat && bottomRight.lat <= lat) {
return true; return true;
} }
} else { } else {
if (topLeft.lon <= point.lon() && 180 >= point.lon() if (topLeft.lon <= lon && 180 >= lon
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { && topLeft.lat >= lat && bottomRight.lat <= lat) {
return true; return true;
} }
} }
} }
} else { } else {
GeoPoint point = fieldData.value(doc); double lat = fieldData.latValue(doc);
if (point.lon() < 0) { double lon = fieldData.lonValue(doc);
if (-180.0 <= point.lon() && bottomRight.lon >= point.lon() if (lon < 0) {
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { if (-180.0 <= lon && bottomRight.lon >= lon
&& topLeft.lat >= lat && bottomRight.lat <= lat) {
return true; return true;
} }
} else { } else {
if (topLeft.lon <= point.lon() && 180 >= point.lon() if (topLeft.lon <= lon && 180 >= lon
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { && topLeft.lat >= lat && bottomRight.lat <= lat) {
return true; return true;
} }
} }
@ -129,18 +132,20 @@ public class GeoBoundingBoxFilter extends Filter {
} }
if (fieldData.multiValued()) { if (fieldData.multiValued()) {
GeoPoint[] points = fieldData.values(doc); double[] lats = fieldData.latValues(doc);
for (GeoPoint point : points) { double[] lons = fieldData.lonValues(doc);
if (topLeft.lon <= point.lon() && bottomRight.lon >= point.lon() for (int i = 0; i < lats.length; i++) {
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { if (topLeft.lon <= lons[i] && bottomRight.lon >= lons[i]
&& topLeft.lat >= lats[i] && bottomRight.lat <= lats[i]) {
return true; return true;
} }
} }
} else { } else {
GeoPoint point = fieldData.value(doc); double lat = fieldData.latValue(doc);
double lon = fieldData.lonValue(doc);
if (topLeft.lon <= point.lon() && bottomRight.lon >= point.lon() if (topLeft.lon <= lon && bottomRight.lon >= lon
&& topLeft.lat >= point.lat() && bottomRight.lat <= point.lat()) { && topLeft.lat >= lat && bottomRight.lat <= lat) {
return true; return true;
} }
} }

View File

@ -27,7 +27,6 @@ import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
@ -139,8 +138,7 @@ public class GeoDistanceDataComparator extends FieldComparator {
// is this true? push this to the "end" // is this true? push this to the "end"
distance = Double.MAX_VALUE; distance = Double.MAX_VALUE;
} else { } else {
GeoPoint point = fieldData.value(doc); distance = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), unit);
distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
} }
final double v2 = distance; final double v2 = distance;
if (bottom > v2) { if (bottom > v2) {
@ -158,8 +156,7 @@ public class GeoDistanceDataComparator extends FieldComparator {
// is this true? push this to the "end" // is this true? push this to the "end"
distance = Double.MAX_VALUE; distance = Double.MAX_VALUE;
} else { } else {
GeoPoint point = fieldData.value(doc); distance = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), unit);
distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
} }
values[slot] = distance; values[slot] = distance;
} }

View File

@ -25,7 +25,6 @@ import org.apache.lucene.search.Filter;
import org.elasticsearch.common.lucene.docset.GetDocSet; import org.elasticsearch.common.lucene.docset.GetDocSet;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
@ -94,17 +93,17 @@ public class GeoDistanceFilter extends Filter {
} }
if (fieldData.multiValued()) { if (fieldData.multiValued()) {
GeoPoint[] points = fieldData.values(doc); double[] lats = fieldData.latValues(doc);
for (GeoPoint point : points) { double[] lons = fieldData.lonValues(doc);
double d = geoDistance.calculate(lat, lon, point.lat(), point.lon(), DistanceUnit.MILES); for (int i = 0; i < lats.length; i++) {
double d = geoDistance.calculate(lat, lon, lats[i], lons[i], DistanceUnit.MILES);
if (d < distance) { if (d < distance) {
return true; return true;
} }
} }
return false; return false;
} else { } else {
GeoPoint point = fieldData.value(doc); double d = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), DistanceUnit.MILES);
double d = geoDistance.calculate(lat, lon, point.lat(), point.lon(), DistanceUnit.MILES);
return d < distance; return d < distance;
} }
} }

View File

@ -24,7 +24,6 @@ import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.elasticsearch.common.lucene.docset.GetDocSet; import org.elasticsearch.common.lucene.docset.GetDocSet;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
@ -73,15 +72,17 @@ public class GeoPolygonFilter extends Filter {
} }
if (fieldData.multiValued()) { if (fieldData.multiValued()) {
GeoPoint[] docPoints = fieldData.values(doc); double[] lats = fieldData.latValues(doc);
for (GeoPoint docPoint : docPoints) { double[] lons = fieldData.lonValues(doc);
if (pointInPolygon(points, docPoint.lat(), docPoint.lon())) { for (int i = 0; i < lats.length; i++) {
if (pointInPolygon(points, lats[i], lons[i])) {
return true; return true;
} }
} }
} else { } else {
GeoPoint point = fieldData.value(doc); double lat = fieldData.latValue(doc);
return pointInPolygon(points, point.lat(), point.lon()); double lon = fieldData.lonValue(doc);
return pointInPolygon(points, lat, lon);
} }
return false; return false;
} }

View File

@ -26,15 +26,23 @@ import org.elasticsearch.common.lucene.geo.GeoHashUtils;
*/ */
public class GeoPoint { public class GeoPoint {
private final double lat; private double lat;
private final double lon; private double lon;
GeoPoint() {
}
public GeoPoint(double lat, double lon) { public GeoPoint(double lat, double lon) {
this.lat = lat; this.lat = lat;
this.lon = lon; this.lon = lon;
} }
void latlon(double lat, double lon) {
this.lat = lat;
this.lon = lon;
}
public final double lat() { public final double lat() {
return this.lat; return this.lat;
} }

View File

@ -37,4 +37,20 @@ public class GeoPointDocFieldData extends DocFieldData<GeoPointFieldData> {
public GeoPoint[] getValues() { public GeoPoint[] getValues() {
return fieldData.values(docId); return fieldData.values(docId);
} }
public double getLat() {
return fieldData.latValue(docId);
}
public double getLon() {
return fieldData.lonValue(docId);
}
public double[] getLats() {
return fieldData.latValues(docId);
}
public double[] getLons() {
return fieldData.lonValues(docId);
}
} }

View File

@ -20,31 +20,49 @@
package org.elasticsearch.index.mapper.xcontent.geo; package org.elasticsearch.index.mapper.xcontent.geo;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
import org.elasticsearch.common.lucene.geo.GeoHashUtils;
import org.elasticsearch.common.thread.ThreadLocals;
import org.elasticsearch.common.trove.TDoubleArrayList;
import org.elasticsearch.index.field.data.FieldData; import org.elasticsearch.index.field.data.FieldData;
import org.elasticsearch.index.field.data.FieldDataType; import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.FieldDataLoader; import org.elasticsearch.index.field.data.support.FieldDataLoader;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData> { public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData> {
static ThreadLocal<ThreadLocals.CleanableValue<GeoPoint>> valuesCache = new ThreadLocal<ThreadLocals.CleanableValue<GeoPoint>>() {
@Override protected ThreadLocals.CleanableValue<GeoPoint> initialValue() {
return new ThreadLocals.CleanableValue<GeoPoint>(new GeoPoint());
}
};
public static final GeoPoint[] EMPTY_ARRAY = new GeoPoint[0]; public static final GeoPoint[] EMPTY_ARRAY = new GeoPoint[0];
protected final GeoPoint[] values; protected final double[] lat;
protected final double[] lon;
protected GeoPointFieldData(String fieldName, GeoPoint[] values) { protected GeoPointFieldData(String fieldName, double[] lat, double[] lon) {
super(fieldName); super(fieldName);
this.values = values; this.lat = lat;
this.lon = lon;
} }
abstract public GeoPoint value(int docId); abstract public GeoPoint value(int docId);
abstract public GeoPoint[] values(int docId); abstract public GeoPoint[] values(int docId);
abstract public double latValue(int docId);
abstract public double lonValue(int docId);
abstract public double[] latValues(int docId);
abstract public double[] lonValues(int docId);
@Override public GeoPointDocFieldData docFieldData(int docId) { @Override public GeoPointDocFieldData docFieldData(int docId) {
return super.docFieldData(docId); return super.docFieldData(docId);
} }
@ -62,19 +80,31 @@ public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData>
} }
@Override public void forEachValue(StringValueProc proc) { @Override public void forEachValue(StringValueProc proc) {
for (int i = 1; i < values.length; i++) { for (int i = 1; i < lat.length; i++) {
proc.onValue(values[i].geohash()); proc.onValue(GeoHashUtils.encode(lat[i], lon[i]));
} }
} }
public void forEachValue(PointValueProc proc) {
for (int i = 1; i < lat.length; i++) {
GeoPoint point = valuesCache.get().get();
point.latlon(lat[i], lon[i]);
proc.onValue(point);
}
}
public static interface PointValueProc {
void onValue(GeoPoint value);
}
public void forEachValue(ValueProc proc) { public void forEachValue(ValueProc proc) {
for (int i = 1; i < values.length; i++) { for (int i = 1; i < lat.length; i++) {
proc.onValue(values[i]); proc.onValue(lat[i], lon[i]);
} }
} }
public static interface ValueProc { public static interface ValueProc {
void onValue(GeoPoint value); void onValue(double lat, double lon);
} }
public static GeoPointFieldData load(IndexReader reader, String field) throws IOException { public static GeoPointFieldData load(IndexReader reader, String field) throws IOException {
@ -83,27 +113,29 @@ public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData>
static class StringTypeLoader extends FieldDataLoader.FreqsTypeLoader<GeoPointFieldData> { static class StringTypeLoader extends FieldDataLoader.FreqsTypeLoader<GeoPointFieldData> {
private final ArrayList<GeoPoint> terms = new ArrayList<GeoPoint>(); private final TDoubleArrayList lat = new TDoubleArrayList();
private final TDoubleArrayList lon = new TDoubleArrayList();
StringTypeLoader() { StringTypeLoader() {
super(); super();
// the first one indicates null value // the first one indicates null value
terms.add(null); lat.add(0);
lon.add(0);
} }
@Override public void collectTerm(String term) { @Override public void collectTerm(String term) {
int comma = term.indexOf(','); int comma = term.indexOf(',');
double lat = Double.parseDouble(term.substring(0, comma)); lat.add(Double.parseDouble(term.substring(0, comma)));
double lon = Double.parseDouble(term.substring(comma + 1)); lon.add(Double.parseDouble(term.substring(comma + 1)));
terms.add(new GeoPoint(lat, lon));
} }
@Override public GeoPointFieldData buildSingleValue(String field, int[] order) { @Override public GeoPointFieldData buildSingleValue(String field, int[] order) {
return new SingleValueGeoPointFieldData(field, order, terms.toArray(new GeoPoint[terms.size()])); return new SingleValueGeoPointFieldData(field, order, lat.toNativeArray(), lon.toNativeArray());
} }
@Override public GeoPointFieldData buildMultiValue(String field, int[][] order) { @Override public GeoPointFieldData buildMultiValue(String field, int[][] order) {
return new MultiValueGeoPointFieldData(field, order, terms.toArray(new GeoPoint[terms.size()])); return new MultiValueGeoPointFieldData(field, order, lat.toNativeArray(), lon.toNativeArray());
} }
} }
} }

View File

@ -19,7 +19,9 @@
package org.elasticsearch.index.mapper.xcontent.geo; package org.elasticsearch.index.mapper.xcontent.geo;
import org.elasticsearch.common.lucene.geo.GeoHashUtils;
import org.elasticsearch.common.thread.ThreadLocals; import org.elasticsearch.common.thread.ThreadLocals;
import org.elasticsearch.index.field.data.doubles.DoubleFieldData;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
@ -28,21 +30,44 @@ public class MultiValueGeoPointFieldData extends GeoPointFieldData {
private static final int VALUE_CACHE_SIZE = 100; private static final int VALUE_CACHE_SIZE = 100;
private static ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[][]>> valuesCache = new ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[][]>>() { private static ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[][]>> valuesArrayCache = new ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[][]>>() {
@Override protected ThreadLocals.CleanableValue<GeoPoint[][]> initialValue() { @Override protected ThreadLocals.CleanableValue<GeoPoint[][]> initialValue() {
GeoPoint[][] value = new GeoPoint[VALUE_CACHE_SIZE][]; GeoPoint[][] value = new GeoPoint[VALUE_CACHE_SIZE][];
for (int i = 0; i < value.length; i++) { for (int i = 0; i < value.length; i++) {
value[i] = new GeoPoint[i]; value[i] = new GeoPoint[i];
for (int j = 0; j < value.length; j++) {
value[i][j] = new GeoPoint();
}
} }
return new ThreadLocals.CleanableValue<GeoPoint[][]>(value); return new ThreadLocals.CleanableValue<GeoPoint[][]>(value);
} }
}; };
private ThreadLocal<ThreadLocals.CleanableValue<double[][]>> valuesLatCache = new ThreadLocal<ThreadLocals.CleanableValue<double[][]>>() {
@Override protected ThreadLocals.CleanableValue<double[][]> initialValue() {
double[][] value = new double[VALUE_CACHE_SIZE][];
for (int i = 0; i < value.length; i++) {
value[i] = new double[i];
}
return new ThreadLocals.CleanableValue<double[][]>(value);
}
};
private ThreadLocal<ThreadLocals.CleanableValue<double[][]>> valuesLonCache = new ThreadLocal<ThreadLocals.CleanableValue<double[][]>>() {
@Override protected ThreadLocals.CleanableValue<double[][]> initialValue() {
double[][] value = new double[VALUE_CACHE_SIZE][];
for (int i = 0; i < value.length; i++) {
value[i] = new double[i];
}
return new ThreadLocals.CleanableValue<double[][]>(value);
}
};
// order with value 0 indicates no value // order with value 0 indicates no value
private final int[][] order; private final int[][] order;
public MultiValueGeoPointFieldData(String fieldName, int[][] order, GeoPoint[] values) { public MultiValueGeoPointFieldData(String fieldName, int[][] order, double[] lat, double[] lon) {
super(fieldName, values); super(fieldName, lat, lon);
this.order = order; this.order = order;
} }
@ -60,7 +85,7 @@ public class MultiValueGeoPointFieldData extends GeoPointFieldData {
return; return;
} }
for (int docOrder : docOrders) { for (int docOrder : docOrders) {
proc.onValue(docId, values[docOrder].geohash()); proc.onValue(docId, GeoHashUtils.encode(lat[docOrder], lon[docOrder]));
} }
} }
@ -69,7 +94,10 @@ public class MultiValueGeoPointFieldData extends GeoPointFieldData {
if (docOrders == null) { if (docOrders == null) {
return null; return null;
} }
return values[docOrders[0]]; GeoPoint point = valuesCache.get().get();
int loc = docOrders[0];
point.latlon(lat[loc], lon[loc]);
return point;
} }
@Override public GeoPoint[] values(int docId) { @Override public GeoPoint[] values(int docId) {
@ -79,13 +107,68 @@ public class MultiValueGeoPointFieldData extends GeoPointFieldData {
} }
GeoPoint[] points; GeoPoint[] points;
if (docOrders.length < VALUE_CACHE_SIZE) { if (docOrders.length < VALUE_CACHE_SIZE) {
points = valuesCache.get().get()[docOrders.length]; points = valuesArrayCache.get().get()[docOrders.length];
for (int i = 0; i < docOrders.length; i++) {
int loc = docOrders[i];
points[i].latlon(lat[loc], lon[loc]);
}
} else { } else {
points = new GeoPoint[docOrders.length]; points = new GeoPoint[docOrders.length];
} for (int i = 0; i < docOrders.length; i++) {
for (int i = 0; i < docOrders.length; i++) { int loc = docOrders[i];
points[i] = values[docOrders[i]]; points[i] = new GeoPoint(lat[loc], lon[loc]);
}
} }
return points; return points;
} }
@Override public double latValue(int docId) {
int[] docOrders = order[docId];
if (docOrders == null) {
return 0;
}
return lat[docOrders[0]];
}
@Override public double lonValue(int docId) {
int[] docOrders = order[docId];
if (docOrders == null) {
return 0;
}
return lon[docOrders[0]];
}
@Override public double[] latValues(int docId) {
int[] docOrders = order[docId];
if (docOrders == null) {
return DoubleFieldData.EMPTY_DOUBLE_ARRAY;
}
double[] doubles;
if (docOrders.length < VALUE_CACHE_SIZE) {
doubles = valuesLatCache.get().get()[docOrders.length];
} else {
doubles = new double[docOrders.length];
}
for (int i = 0; i < docOrders.length; i++) {
doubles[i] = lat[docOrders[i]];
}
return doubles;
}
@Override public double[] lonValues(int docId) {
int[] docOrders = order[docId];
if (docOrders == null) {
return DoubleFieldData.EMPTY_DOUBLE_ARRAY;
}
double[] doubles;
if (docOrders.length < VALUE_CACHE_SIZE) {
doubles = valuesLonCache.get().get()[docOrders.length];
} else {
doubles = new double[docOrders.length];
}
for (int i = 0; i < docOrders.length; i++) {
doubles[i] = lon[docOrders[i]];
}
return doubles;
}
} }

View File

@ -19,35 +19,44 @@
package org.elasticsearch.index.mapper.xcontent.geo; package org.elasticsearch.index.mapper.xcontent.geo;
import org.elasticsearch.common.lucene.geo.GeoHashUtils;
import org.elasticsearch.common.thread.ThreadLocals; import org.elasticsearch.common.thread.ThreadLocals;
import org.elasticsearch.index.field.data.doubles.DoubleFieldData;
/** /**
* @author kimchy (shay.banon) * @author kimchy (shay.banon)
*/ */
public class SingleValueGeoPointFieldData extends GeoPointFieldData { public class SingleValueGeoPointFieldData extends GeoPointFieldData {
private static ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[]>> valuesCache = new ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[]>>() { private static ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[]>> valuesArrayCache = new ThreadLocal<ThreadLocals.CleanableValue<GeoPoint[]>>() {
@Override protected ThreadLocals.CleanableValue<GeoPoint[]> initialValue() { @Override protected ThreadLocals.CleanableValue<GeoPoint[]> initialValue() {
return new ThreadLocals.CleanableValue<GeoPoint[]>(new GeoPoint[1]); GeoPoint[] value = new GeoPoint[1];
value[0] = new GeoPoint();
return new ThreadLocals.CleanableValue<GeoPoint[]>(value);
} }
}; };
private ThreadLocal<ThreadLocals.CleanableValue<double[]>> valuesLatCache = new ThreadLocal<ThreadLocals.CleanableValue<double[]>>() {
@Override protected ThreadLocals.CleanableValue<double[]> initialValue() {
return new ThreadLocals.CleanableValue<double[]>(new double[1]);
}
};
private ThreadLocal<ThreadLocals.CleanableValue<double[]>> valuesLonCache = new ThreadLocal<ThreadLocals.CleanableValue<double[]>>() {
@Override protected ThreadLocals.CleanableValue<double[]> initialValue() {
return new ThreadLocals.CleanableValue<double[]>(new double[1]);
}
};
// order with value 0 indicates no value // order with value 0 indicates no value
private final int[] order; private final int[] order;
public SingleValueGeoPointFieldData(String fieldName, int[] order, GeoPoint[] values) { public SingleValueGeoPointFieldData(String fieldName, int[] order, double[] lat, double[] lon) {
super(fieldName, values); super(fieldName, lat, lon);
this.order = order; this.order = order;
} }
int[] order() {
return order;
}
GeoPoint[] values() {
return this.values;
}
@Override public boolean multiValued() { @Override public boolean multiValued() {
return false; return false;
} }
@ -61,11 +70,17 @@ public class SingleValueGeoPointFieldData extends GeoPointFieldData {
if (loc == 0) { if (loc == 0) {
return; return;
} }
proc.onValue(docId, values[loc].geohash()); proc.onValue(docId, GeoHashUtils.encode(lat[loc], lon[loc]));
} }
@Override public GeoPoint value(int docId) { @Override public GeoPoint value(int docId) {
return values[order[docId]]; int loc = order[docId];
if (loc == 0) {
return null;
}
GeoPoint point = valuesCache.get().get();
point.latlon(lat[loc], lon[loc]);
return point;
} }
@Override public GeoPoint[] values(int docId) { @Override public GeoPoint[] values(int docId) {
@ -73,8 +88,36 @@ public class SingleValueGeoPointFieldData extends GeoPointFieldData {
if (loc == 0) { if (loc == 0) {
return EMPTY_ARRAY; return EMPTY_ARRAY;
} }
GeoPoint[] ret = valuesCache.get().get(); GeoPoint[] ret = valuesArrayCache.get().get();
ret[0] = values[loc]; ret[0].latlon(lat[loc], lon[loc]);
return ret;
}
@Override public double latValue(int docId) {
return lat[order[docId]];
}
@Override public double lonValue(int docId) {
return lon[order[docId]];
}
@Override public double[] latValues(int docId) {
int loc = order[docId];
if (loc == 0) {
return DoubleFieldData.EMPTY_DOUBLE_ARRAY;
}
double[] ret = valuesLatCache.get().get();
ret[0] = lat[loc];
return ret;
}
@Override public double[] lonValues(int docId) {
int loc = order[docId];
if (loc == 0) {
return DoubleFieldData.EMPTY_DOUBLE_ARRAY;
}
double[] ret = valuesLonCache.get().get();
ret[0] = lon[loc];
return ret; return ret;
} }
} }

View File

@ -24,7 +24,6 @@ import org.elasticsearch.common.lucene.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType; import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldDataType;
import org.elasticsearch.search.facets.Facet; import org.elasticsearch.search.facets.Facet;
@ -94,9 +93,10 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
} }
if (fieldData.multiValued()) { if (fieldData.multiValued()) {
GeoPoint[] points = fieldData.values(doc); double[] lats = fieldData.latValues(doc);
for (GeoPoint point : points) { double[] lons = fieldData.lonValues(doc);
double distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit); for (int i = 0; i < lats.length; i++) {
double distance = geoDistance.calculate(lat, lon, lats[i], lons[i], unit);
for (GeoDistanceFacet.Entry entry : entries) { for (GeoDistanceFacet.Entry entry : entries) {
if (distance >= entry.getFrom() && distance < entry.getTo()) { if (distance >= entry.getFrom() && distance < entry.getTo()) {
entry.count++; entry.count++;
@ -105,8 +105,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
} }
} }
} else { } else {
GeoPoint point = fieldData.value(doc); double distance = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), unit);
double distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
for (GeoDistanceFacet.Entry entry : entries) { for (GeoDistanceFacet.Entry entry : entries) {
if (distance >= entry.getFrom() && distance < entry.getTo()) { if (distance >= entry.getFrom() && distance < entry.getTo()) {
entry.count++; entry.count++;