Geo Distance / Range Facets might count documents several times for a range entry if the field is multi valued, closes #824.
This commit is contained in:
parent
5d6e84f206
commit
105d60ac9c
|
@ -142,6 +142,12 @@ public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData>
|
||||||
void onValue(double lat, double lon);
|
void onValue(double lat, double lon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void forEachValueInDoc(int docId, ValueInDocProc proc);
|
||||||
|
|
||||||
|
public static interface ValueInDocProc {
|
||||||
|
void onValue(int docId, double lat, double lon);
|
||||||
|
}
|
||||||
|
|
||||||
public static GeoPointFieldData load(IndexReader reader, String field) throws IOException {
|
public static GeoPointFieldData load(IndexReader reader, String field) throws IOException {
|
||||||
return FieldDataLoader.load(reader, field, new StringTypeLoader());
|
return FieldDataLoader.load(reader, field, new StringTypeLoader());
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,15 @@ public class MultiValueGeoPointFieldData extends GeoPointFieldData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
for (int[] ordinal : ordinals) {
|
||||||
|
int loc = ordinal[docId];
|
||||||
|
if (loc != 0) {
|
||||||
|
proc.onValue(docId, lat[loc], lon[loc]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void forEachOrdinalInDoc(int docId, OrdinalInDocProc proc) {
|
@Override public void forEachOrdinalInDoc(int docId, OrdinalInDocProc proc) {
|
||||||
for (int[] ordinal : ordinals) {
|
for (int[] ordinal : ordinals) {
|
||||||
proc.onOrdinal(docId, ordinal[docId]);
|
proc.onOrdinal(docId, ordinal[docId]);
|
||||||
|
|
|
@ -84,6 +84,14 @@ public class SingleValueGeoPointFieldData extends GeoPointFieldData {
|
||||||
proc.onOrdinal(docId, ordinals[docId]);
|
proc.onOrdinal(docId, ordinals[docId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
int loc = ordinals[docId];
|
||||||
|
if (loc == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proc.onValue(docId, lat[loc], lon[loc]);
|
||||||
|
}
|
||||||
|
|
||||||
@Override public GeoPoint value(int docId) {
|
@Override public GeoPoint value(int docId) {
|
||||||
int loc = ordinals[docId];
|
int loc = ordinals[docId];
|
||||||
if (loc == 0) {
|
if (loc == 0) {
|
||||||
|
|
|
@ -53,6 +53,11 @@ public interface GeoDistanceFacet extends Facet, Iterable<GeoDistanceFacet.Entry
|
||||||
|
|
||||||
double total;
|
double total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* internal field used to see if this entry was already found for a doc
|
||||||
|
*/
|
||||||
|
boolean foundInDoc = false;
|
||||||
|
|
||||||
Entry() {
|
Entry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,8 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
||||||
|
|
||||||
protected final GeoDistanceFacet.Entry[] entries;
|
protected final GeoDistanceFacet.Entry[] entries;
|
||||||
|
|
||||||
|
protected GeoPointFieldData.ValueInDocProc aggregator;
|
||||||
|
|
||||||
public GeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
public GeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||||
GeoDistanceFacet.Entry[] entries, SearchContext context) {
|
GeoDistanceFacet.Entry[] entries, SearchContext context) {
|
||||||
super(facetName);
|
super(facetName);
|
||||||
|
@ -78,6 +80,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.indexFieldName = smartMappers.mapper().names().indexName();
|
this.indexFieldName = smartMappers.mapper().names().indexName();
|
||||||
|
this.aggregator = new Aggregator(lat, lon, geoDistance, unit, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
||||||
|
@ -85,34 +88,48 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doCollect(int doc) throws IOException {
|
@Override protected void doCollect(int doc) throws IOException {
|
||||||
if (!fieldData.hasValue(doc)) {
|
for (GeoDistanceFacet.Entry entry : entries) {
|
||||||
return;
|
entry.foundInDoc = false;
|
||||||
|
}
|
||||||
|
fieldData.forEachValueInDoc(doc, aggregator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public Facet facet() {
|
||||||
|
return new InternalGeoDistanceFacet(facetName, entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Aggregator implements GeoPointFieldData.ValueInDocProc {
|
||||||
|
|
||||||
|
protected final double lat;
|
||||||
|
|
||||||
|
protected final double lon;
|
||||||
|
|
||||||
|
private final GeoDistance geoDistance;
|
||||||
|
|
||||||
|
private final DistanceUnit unit;
|
||||||
|
|
||||||
|
private final GeoDistanceFacet.Entry[] entries;
|
||||||
|
|
||||||
|
public Aggregator(double lat, double lon, GeoDistance geoDistance, DistanceUnit unit, GeoDistanceFacet.Entry[] entries) {
|
||||||
|
this.lat = lat;
|
||||||
|
this.lon = lon;
|
||||||
|
this.geoDistance = geoDistance;
|
||||||
|
this.unit = unit;
|
||||||
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldData.multiValued()) {
|
@Override public void onValue(int docId, double lat, double lon) {
|
||||||
double[] lats = fieldData.latValues(doc);
|
double distance = geoDistance.calculate(this.lat, this.lon, lat, lon, unit);
|
||||||
double[] lons = fieldData.lonValues(doc);
|
|
||||||
for (int i = 0; i < lats.length; i++) {
|
|
||||||
double distance = geoDistance.calculate(lat, lon, lats[i], lons[i], unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
entry.total += distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
double distance = geoDistance.calculate(lat, lon, fieldData.latValue(doc), fieldData.lonValue(doc), unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
for (GeoDistanceFacet.Entry entry : entries) {
|
||||||
|
if (entry.foundInDoc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
||||||
|
entry.foundInDoc = true;
|
||||||
entry.count++;
|
entry.count++;
|
||||||
entry.total += distance;
|
entry.total += distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Facet facet() {
|
|
||||||
return new InternalGeoDistanceFacet(facetName, entries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.search.facet.geodistance;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.search.Scorer;
|
import org.apache.lucene.search.Scorer;
|
||||||
import org.elasticsearch.common.unit.DistanceUnit;
|
import org.elasticsearch.common.unit.DistanceUnit;
|
||||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
|
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
|
||||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||||
import org.elasticsearch.script.SearchScript;
|
import org.elasticsearch.script.SearchScript;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
|
@ -37,12 +37,16 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
||||||
|
|
||||||
private final SearchScript script;
|
private final SearchScript script;
|
||||||
|
|
||||||
|
private Aggregator scriptAggregator;
|
||||||
|
|
||||||
public ScriptGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
public ScriptGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||||
GeoDistanceFacet.Entry[] entries, SearchContext context,
|
GeoDistanceFacet.Entry[] entries, SearchContext context,
|
||||||
String scriptLang, String script, Map<String, Object> params) {
|
String scriptLang, String script, Map<String, Object> params) {
|
||||||
super(facetName, fieldName, lat, lon, unit, geoDistance, entries, context);
|
super(facetName, fieldName, lat, lon, unit, geoDistance, entries, context);
|
||||||
|
|
||||||
this.script = context.scriptService().search(context.lookup(), scriptLang, script, params);
|
this.script = context.scriptService().search(context.lookup(), scriptLang, script, params);
|
||||||
|
this.aggregator = new Aggregator(lat, lon, geoDistance, unit, entries);
|
||||||
|
this.scriptAggregator = (Aggregator) this.aggregator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void setScorer(Scorer scorer) throws IOException {
|
@Override public void setScorer(Scorer scorer) throws IOException {
|
||||||
|
@ -55,32 +59,43 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doCollect(int doc) throws IOException {
|
@Override protected void doCollect(int doc) throws IOException {
|
||||||
if (!fieldData.hasValue(doc)) {
|
script.setNextDocId(doc);
|
||||||
return;
|
this.scriptAggregator.scriptValue = script.runAsDouble();
|
||||||
|
super.doCollect(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Aggregator implements GeoPointFieldData.ValueInDocProc {
|
||||||
|
|
||||||
|
protected final double lat;
|
||||||
|
|
||||||
|
protected final double lon;
|
||||||
|
|
||||||
|
private final GeoDistance geoDistance;
|
||||||
|
|
||||||
|
private final DistanceUnit unit;
|
||||||
|
|
||||||
|
private final GeoDistanceFacet.Entry[] entries;
|
||||||
|
|
||||||
|
double scriptValue;
|
||||||
|
|
||||||
|
public Aggregator(double lat, double lon, GeoDistance geoDistance, DistanceUnit unit, GeoDistanceFacet.Entry[] entries) {
|
||||||
|
this.lat = lat;
|
||||||
|
this.lon = lon;
|
||||||
|
this.geoDistance = geoDistance;
|
||||||
|
this.unit = unit;
|
||||||
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
script.setNextDocId(doc);
|
@Override public void onValue(int docId, double lat, double lon) {
|
||||||
|
double distance = geoDistance.calculate(this.lat, this.lon, lat, lon, unit);
|
||||||
double value = script.runAsDouble();
|
|
||||||
|
|
||||||
if (fieldData.multiValued()) {
|
|
||||||
GeoPoint[] points = fieldData.values(doc);
|
|
||||||
for (GeoPoint point : points) {
|
|
||||||
double distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
entry.total += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GeoPoint point = fieldData.value(doc);
|
|
||||||
double distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
for (GeoDistanceFacet.Entry entry : entries) {
|
||||||
|
if (entry.foundInDoc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
||||||
|
entry.foundInDoc = true;
|
||||||
entry.count++;
|
entry.count++;
|
||||||
entry.total += value;
|
entry.total += scriptValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,8 @@ import org.elasticsearch.common.unit.DistanceUnit;
|
||||||
import org.elasticsearch.index.field.data.FieldDataType;
|
import org.elasticsearch.index.field.data.FieldDataType;
|
||||||
import org.elasticsearch.index.field.data.NumericFieldData;
|
import org.elasticsearch.index.field.data.NumericFieldData;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
import org.elasticsearch.index.mapper.xcontent.geo.GeoPoint;
|
import org.elasticsearch.index.mapper.xcontent.geo.GeoPointFieldData;
|
||||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||||
import org.elasticsearch.search.facet.Facet;
|
|
||||||
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
|
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
|
|
||||||
|
@ -41,8 +40,6 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
||||||
|
|
||||||
private final FieldDataType valueFieldDataType;
|
private final FieldDataType valueFieldDataType;
|
||||||
|
|
||||||
private NumericFieldData valueFieldData;
|
|
||||||
|
|
||||||
public ValueGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
public ValueGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||||
GeoDistanceFacet.Entry[] entries, SearchContext context, String valueFieldName) {
|
GeoDistanceFacet.Entry[] entries, SearchContext context, String valueFieldName) {
|
||||||
super(facetName, fieldName, lat, lon, unit, geoDistance, entries, context);
|
super(facetName, fieldName, lat, lon, unit, geoDistance, entries, context);
|
||||||
|
@ -53,56 +50,55 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
||||||
}
|
}
|
||||||
this.indexValueFieldName = valueFieldName;
|
this.indexValueFieldName = valueFieldName;
|
||||||
this.valueFieldDataType = mapper.fieldDataType();
|
this.valueFieldDataType = mapper.fieldDataType();
|
||||||
|
this.aggregator = new Aggregator(lat, lon, geoDistance, unit, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
||||||
super.doSetNextReader(reader, docBase);
|
super.doSetNextReader(reader, docBase);
|
||||||
valueFieldData = (NumericFieldData) fieldDataCache.cache(valueFieldDataType, reader, indexValueFieldName);
|
((Aggregator) this.aggregator).valueFieldData = (NumericFieldData) fieldDataCache.cache(valueFieldDataType, reader, indexValueFieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doCollect(int doc) throws IOException {
|
public static class Aggregator implements GeoPointFieldData.ValueInDocProc {
|
||||||
if (!fieldData.hasValue(doc)) {
|
|
||||||
return;
|
protected final double lat;
|
||||||
|
|
||||||
|
protected final double lon;
|
||||||
|
|
||||||
|
private final GeoDistance geoDistance;
|
||||||
|
|
||||||
|
private final DistanceUnit unit;
|
||||||
|
|
||||||
|
private final GeoDistanceFacet.Entry[] entries;
|
||||||
|
|
||||||
|
NumericFieldData valueFieldData;
|
||||||
|
|
||||||
|
public Aggregator(double lat, double lon, GeoDistance geoDistance, DistanceUnit unit, GeoDistanceFacet.Entry[] entries) {
|
||||||
|
this.lat = lat;
|
||||||
|
this.lon = lon;
|
||||||
|
this.geoDistance = geoDistance;
|
||||||
|
this.unit = unit;
|
||||||
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fieldData.multiValued()) {
|
@Override public void onValue(int docId, double lat, double lon) {
|
||||||
GeoPoint[] points = fieldData.values(doc);
|
double distance = geoDistance.calculate(this.lat, this.lon, lat, lon, unit);
|
||||||
double[] values = valueFieldData.multiValued() ? valueFieldData.doubleValues(doc) : null;
|
|
||||||
for (int i = 0; i < points.length; i++) {
|
|
||||||
double distance = geoDistance.calculate(lat, lon, points[i].lat(), points[i].lon(), unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
if (values != null) {
|
|
||||||
if (i < values.length) {
|
|
||||||
entry.total += values[i];
|
|
||||||
}
|
|
||||||
} else if (valueFieldData.hasValue(doc)) {
|
|
||||||
entry.total += valueFieldData.doubleValue(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
GeoPoint point = fieldData.value(doc);
|
|
||||||
double distance = geoDistance.calculate(lat, lon, point.lat(), point.lon(), unit);
|
|
||||||
for (GeoDistanceFacet.Entry entry : entries) {
|
for (GeoDistanceFacet.Entry entry : entries) {
|
||||||
|
if (entry.foundInDoc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
if (distance >= entry.getFrom() && distance < entry.getTo()) {
|
||||||
|
entry.foundInDoc = true;
|
||||||
entry.count++;
|
entry.count++;
|
||||||
if (valueFieldData.multiValued()) {
|
if (valueFieldData.multiValued()) {
|
||||||
double[] values = valueFieldData.doubleValues(doc);
|
double[] values = valueFieldData.doubleValues(docId);
|
||||||
for (double value : values) {
|
for (double value : values) {
|
||||||
entry.total += value;
|
entry.total += value;
|
||||||
}
|
}
|
||||||
} else if (valueFieldData.hasValue(doc)) {
|
} else if (valueFieldData.hasValue(docId)) {
|
||||||
entry.total += valueFieldData.doubleValue(doc);
|
entry.total += valueFieldData.doubleValue(docId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Facet facet() {
|
|
||||||
return new InternalGeoDistanceFacet(facetName, entries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ public class KeyValueRangeFacetCollector extends AbstractFacetCollector {
|
||||||
|
|
||||||
private final RangeFacet.Entry[] entries;
|
private final RangeFacet.Entry[] entries;
|
||||||
|
|
||||||
|
private final RangeProc rangeProc;
|
||||||
|
|
||||||
public KeyValueRangeFacetCollector(String facetName, String keyFieldName, String valueFieldName, RangeFacet.Entry[] entries, SearchContext context) {
|
public KeyValueRangeFacetCollector(String facetName, String keyFieldName, String valueFieldName, RangeFacet.Entry[] entries, SearchContext context) {
|
||||||
super(facetName);
|
super(facetName);
|
||||||
this.entries = entries;
|
this.entries = entries;
|
||||||
|
@ -75,66 +77,61 @@ public class KeyValueRangeFacetCollector extends AbstractFacetCollector {
|
||||||
}
|
}
|
||||||
valueIndexFieldName = mapper.names().indexName();
|
valueIndexFieldName = mapper.names().indexName();
|
||||||
valueFieldDataType = mapper.fieldDataType();
|
valueFieldDataType = mapper.fieldDataType();
|
||||||
|
|
||||||
|
this.rangeProc = new RangeProc(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
@Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException {
|
||||||
keyFieldData = (NumericFieldData) fieldDataCache.cache(keyFieldDataType, reader, keyIndexFieldName);
|
keyFieldData = (NumericFieldData) fieldDataCache.cache(keyFieldDataType, reader, keyIndexFieldName);
|
||||||
valueFieldData = (NumericFieldData) fieldDataCache.cache(valueFieldDataType, reader, valueIndexFieldName);
|
rangeProc.valueFieldData = (NumericFieldData) fieldDataCache.cache(valueFieldDataType, reader, valueIndexFieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doCollect(int doc) throws IOException {
|
@Override protected void doCollect(int doc) throws IOException {
|
||||||
if (keyFieldData.multiValued()) {
|
for (RangeFacet.Entry entry : entries) {
|
||||||
if (valueFieldData.multiValued()) {
|
entry.foundInDoc = false;
|
||||||
// both multi valued, intersect based on the minimum size
|
|
||||||
double[] keys = keyFieldData.doubleValues(doc);
|
|
||||||
double[] values = valueFieldData.doubleValues(doc);
|
|
||||||
int size = Math.min(keys.length, values.length);
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
double key = keys[i];
|
|
||||||
for (RangeFacet.Entry entry : entries) {
|
|
||||||
if (key >= entry.getFrom() && key < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
entry.total += values[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// key multi valued, value is a single value
|
|
||||||
double value = valueFieldData.doubleValue(doc);
|
|
||||||
for (double key : keyFieldData.doubleValues(doc)) {
|
|
||||||
for (RangeFacet.Entry entry : entries) {
|
|
||||||
if (key >= entry.getFrom() && key < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
entry.total += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
double key = keyFieldData.doubleValue(doc);
|
|
||||||
if (valueFieldData.multiValued()) {
|
|
||||||
for (RangeFacet.Entry entry : entries) {
|
|
||||||
if (key >= entry.getFrom() && key < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
for (double value : valueFieldData.doubleValues(doc)) {
|
|
||||||
entry.total += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// both key and value are not multi valued
|
|
||||||
double value = valueFieldData.doubleValue(doc);
|
|
||||||
for (RangeFacet.Entry entry : entries) {
|
|
||||||
if (key >= entry.getFrom() && key < entry.getTo()) {
|
|
||||||
entry.count++;
|
|
||||||
entry.total += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
keyFieldData.forEachValueInDoc(doc, rangeProc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Facet facet() {
|
@Override public Facet facet() {
|
||||||
return new InternalRangeFacet(facetName, entries);
|
return new InternalRangeFacet(facetName, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RangeProc implements NumericFieldData.DoubleValueInDocProc {
|
||||||
|
|
||||||
|
private final RangeFacet.Entry[] entries;
|
||||||
|
|
||||||
|
private int missing;
|
||||||
|
|
||||||
|
NumericFieldData valueFieldData;
|
||||||
|
|
||||||
|
public RangeProc(RangeFacet.Entry[] entries) {
|
||||||
|
this.entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onValue(int docId, double value) {
|
||||||
|
for (RangeFacet.Entry entry : entries) {
|
||||||
|
if (entry.foundInDoc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value >= entry.getFrom() && value < entry.getTo()) {
|
||||||
|
entry.foundInDoc = true;
|
||||||
|
entry.count++;
|
||||||
|
if (valueFieldData.multiValued()) {
|
||||||
|
double[] valuesValues = valueFieldData.doubleValues(docId);
|
||||||
|
for (double valueValue : valuesValues) {
|
||||||
|
entry.total += valueValue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
double valueValue = valueFieldData.doubleValue(docId);
|
||||||
|
entry.total += valueValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onMissing(int docId) {
|
||||||
|
missing++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,11 @@ public interface RangeFacet extends Facet, Iterable<RangeFacet.Entry> {
|
||||||
|
|
||||||
double total;
|
double total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal field used in facet collection
|
||||||
|
*/
|
||||||
|
boolean foundInDoc;
|
||||||
|
|
||||||
Entry() {
|
Entry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,9 @@ public class RangeFacetCollector extends AbstractFacetCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void doCollect(int doc) throws IOException {
|
@Override protected void doCollect(int doc) throws IOException {
|
||||||
|
for (RangeFacet.Entry entry : entries) {
|
||||||
|
entry.foundInDoc = false;
|
||||||
|
}
|
||||||
fieldData.forEachValueInDoc(doc, rangeProc);
|
fieldData.forEachValueInDoc(doc, rangeProc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +96,11 @@ public class RangeFacetCollector extends AbstractFacetCollector {
|
||||||
|
|
||||||
@Override public void onValue(int docId, double value) {
|
@Override public void onValue(int docId, double value) {
|
||||||
for (RangeFacet.Entry entry : entries) {
|
for (RangeFacet.Entry entry : entries) {
|
||||||
|
if (entry.foundInDoc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (value >= entry.getFrom() && value < entry.getTo()) {
|
if (value >= entry.getFrom() && value < entry.getTo()) {
|
||||||
|
entry.foundInDoc = true;
|
||||||
entry.count++;
|
entry.count++;
|
||||||
entry.total += value;
|
entry.total += value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1147,8 +1147,8 @@ public class SimpleFacetsTests extends AbstractNodesTests {
|
||||||
assertThat(facet.entries().get(0).total(), closeTo(3, 0.000001));
|
assertThat(facet.entries().get(0).total(), closeTo(3, 0.000001));
|
||||||
assertThat(facet.entries().get(1).from(), closeTo(10, 0.000001));
|
assertThat(facet.entries().get(1).from(), closeTo(10, 0.000001));
|
||||||
assertThat(facet.entries().get(1).to(), closeTo(26, 0.000001));
|
assertThat(facet.entries().get(1).to(), closeTo(26, 0.000001));
|
||||||
assertThat(facet.entries().get(1).count(), equalTo(5l));
|
assertThat(facet.entries().get(1).count(), equalTo(3l));
|
||||||
assertThat(facet.entries().get(1).total(), closeTo(1 * 2 + 2 + 3 * 2, 0.000001));
|
assertThat(facet.entries().get(1).total(), closeTo(1 + 2 + 3, 0.000001));
|
||||||
assertThat(facet.entries().get(2).from(), closeTo(20, 0.000001));
|
assertThat(facet.entries().get(2).from(), closeTo(20, 0.000001));
|
||||||
assertThat(facet.entries().get(2).count(), equalTo(3l));
|
assertThat(facet.entries().get(2).count(), equalTo(3l));
|
||||||
assertThat(facet.entries().get(2).total(), closeTo(1 + 2 + 3, 0.000001));
|
assertThat(facet.entries().get(2).total(), closeTo(1 + 2 + 3, 0.000001));
|
||||||
|
|
|
@ -29,6 +29,8 @@ import org.testng.annotations.AfterClass;
|
||||||
import org.testng.annotations.BeforeClass;
|
import org.testng.annotations.BeforeClass;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.*;
|
import static org.elasticsearch.common.xcontent.XContentFactory.*;
|
||||||
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
|
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
|
||||||
import static org.elasticsearch.search.facet.FacetBuilders.*;
|
import static org.elasticsearch.search.facet.FacetBuilders.*;
|
||||||
|
@ -217,4 +219,62 @@ public class GeoDistanceFacetTests extends AbstractNodesTests {
|
||||||
assertThat(facet.entries().get(3).count(), equalTo(5l));
|
assertThat(facet.entries().get(3).count(), equalTo(5l));
|
||||||
assertThat(facet.entries().get(3).total(), closeTo(24, 0.00001));
|
assertThat(facet.entries().get(3).total(), closeTo(24, 0.00001));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test public void multiLocationGeoDistanceTest() throws Exception {
|
||||||
|
try {
|
||||||
|
client.admin().indices().prepareDelete("test").execute().actionGet();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type1")
|
||||||
|
.startObject("properties").startObject("location").field("type", "geo_point").field("lat_lon", true).endObject().endObject()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
client.admin().indices().prepareCreate("test").addMapping("type1", mapping).execute().actionGet();
|
||||||
|
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||||
|
|
||||||
|
client.prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
|
||||||
|
.field("num", 1)
|
||||||
|
.startArray("location")
|
||||||
|
// to NY: 0
|
||||||
|
.startObject().field("lat", 40.7143528).field("lon", -74.0059731).endObject()
|
||||||
|
// to NY: 5.286 km
|
||||||
|
.startObject().field("lat", 40.759011).field("lon", -73.9844722).endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject()).execute().actionGet();
|
||||||
|
|
||||||
|
client.prepareIndex("test", "type1", "3").setSource(jsonBuilder().startObject()
|
||||||
|
.field("num", 3)
|
||||||
|
.startArray("location")
|
||||||
|
// to NY: 0.4621 km
|
||||||
|
.startObject().field("lat", 40.718266).field("lon", -74.007819).endObject()
|
||||||
|
// to NY: 1.055 km
|
||||||
|
.startObject().field("lat", 40.7051157).field("lon", -74.0088305).endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject()).execute().actionGet();
|
||||||
|
|
||||||
|
|
||||||
|
client.admin().indices().prepareRefresh().execute().actionGet();
|
||||||
|
|
||||||
|
SearchResponse searchResponse = client.prepareSearch() // from NY
|
||||||
|
.setQuery(matchAllQuery())
|
||||||
|
.addFacet(geoDistanceFacet("geo1").field("location").point(40.7143528, -74.0059731).unit(DistanceUnit.KILOMETERS)
|
||||||
|
.addRange(0, 2)
|
||||||
|
.addRange(2, 10)
|
||||||
|
)
|
||||||
|
.execute().actionGet();
|
||||||
|
|
||||||
|
assertThat(Arrays.toString(searchResponse.shardFailures()), searchResponse.failedShards(), equalTo(0));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
|
||||||
|
GeoDistanceFacet facet = searchResponse.facets().facet("geo1");
|
||||||
|
assertThat(facet.entries().size(), equalTo(2));
|
||||||
|
|
||||||
|
assertThat(facet.entries().get(0).from(), closeTo(0, 0.000001));
|
||||||
|
assertThat(facet.entries().get(0).to(), closeTo(2, 0.000001));
|
||||||
|
assertThat(facet.entries().get(0).count(), equalTo(2l));
|
||||||
|
|
||||||
|
assertThat(facet.entries().get(1).from(), closeTo(2, 0.000001));
|
||||||
|
assertThat(facet.entries().get(1).to(), closeTo(10, 0.000001));
|
||||||
|
assertThat(facet.entries().get(1).count(), equalTo(1l));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue