geo point new field mapper with geo distance facet based impl

This commit is contained in:
Shay Banon 2013-01-20 17:40:52 +01:00
parent 2e86081f7b
commit d82859c82b
14 changed files with 1397 additions and 66 deletions

View File

@ -0,0 +1,27 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata;
/**
*/
public interface AtomicGeoPointFieldData<Script extends ScriptDocValues> extends AtomicFieldData<Script> {
GeoPointValues getGeoPointValues();
}

View File

@ -0,0 +1,122 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata;
import org.elasticsearch.ElasticSearchIllegalStateException;
import org.elasticsearch.index.fielddata.util.GeoPointArrayRef;
import org.elasticsearch.index.mapper.geo.GeoPoint;
/**
*/
public interface GeoPointValues {
/**
* Is one of the documents in this field data values is multi valued?
*/
boolean isMultiValued();
/**
* Is there a value for this doc?
*/
boolean hasValue(int docId);
GeoPoint getValue(int docId);
GeoPoint getValueSafe(int docId);
GeoPointArrayRef getValues(int docId);
Iter getIter(int docId);
Iter getIterSafe(int docId);
/**
* Go over all the possible values in their geo point format for a specific doc.
*/
void forEachValueInDoc(int docId, ValueInDocProc proc);
/**
* Go over all the possible values in their geo point format for a specific doc.
*/
void forEachSafeValueInDoc(int docId, ValueInDocProc proc);
public static interface ValueInDocProc {
void onValue(int docId, GeoPoint value);
void onMissing(int docId);
}
/**
* Go over all the possible values in their geo point format for a specific doc.
*/
void forEachLatLonValueInDoc(int docId, LatLonValueInDocProc proc);
public static interface LatLonValueInDocProc {
void onValue(int docId, double lat, double lon);
void onMissing(int docId);
}
static interface Iter {
boolean hasNext();
GeoPoint next();
static class Empty implements Iter {
public static final Empty INSTANCE = new Empty();
@Override
public boolean hasNext() {
return false;
}
@Override
public GeoPoint next() {
throw new ElasticSearchIllegalStateException();
}
}
static class Single implements Iter {
public GeoPoint value;
public boolean done;
public Single reset(GeoPoint value) {
this.value = value;
this.done = false;
return this;
}
@Override
public boolean hasNext() {
return !done;
}
@Override
public GeoPoint next() {
assert !done;
done = true;
return value;
}
}
}
}

View File

@ -31,6 +31,7 @@ import org.elasticsearch.index.AbstractIndexComponent;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.plain.ConcreteBytesRefIndexFieldData; import org.elasticsearch.index.fielddata.plain.ConcreteBytesRefIndexFieldData;
import org.elasticsearch.index.fielddata.plain.DoubleArrayIndexFieldData; import org.elasticsearch.index.fielddata.plain.DoubleArrayIndexFieldData;
import org.elasticsearch.index.fielddata.plain.GeoPointDoubleArrayIndexFieldData;
import org.elasticsearch.index.fielddata.plain.LongArrayIndexFieldData; import org.elasticsearch.index.fielddata.plain.LongArrayIndexFieldData;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -49,12 +50,14 @@ public class IndexFieldDataService extends AbstractIndexComponent {
.put("string", new ConcreteBytesRefIndexFieldData.Builder()) .put("string", new ConcreteBytesRefIndexFieldData.Builder())
.put("double", new DoubleArrayIndexFieldData.Builder()) .put("double", new DoubleArrayIndexFieldData.Builder())
.put("long", new LongArrayIndexFieldData.Builder()) .put("long", new LongArrayIndexFieldData.Builder())
.put("geo_point", new GeoPointDoubleArrayIndexFieldData.Builder())
.immutableMap(); .immutableMap();
buildersByTypeAndFormat = MapBuilder.<Tuple<String, String>, IndexFieldData.Builder>newMapBuilder() buildersByTypeAndFormat = MapBuilder.<Tuple<String, String>, IndexFieldData.Builder>newMapBuilder()
.put(Tuple.tuple("string", "concrete_bytes"), new ConcreteBytesRefIndexFieldData.Builder()) .put(Tuple.tuple("string", "concrete_bytes"), new ConcreteBytesRefIndexFieldData.Builder())
.put(Tuple.tuple("double", "array"), new DoubleArrayIndexFieldData.Builder()) .put(Tuple.tuple("double", "array"), new DoubleArrayIndexFieldData.Builder())
.put(Tuple.tuple("long", "array"), new LongArrayIndexFieldData.Builder()) .put(Tuple.tuple("long", "array"), new LongArrayIndexFieldData.Builder())
.put(Tuple.tuple("geo_point", "array"), new GeoPointDoubleArrayIndexFieldData.Builder())
.immutableMap(); .immutableMap();
} }

View File

@ -0,0 +1,37 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata;
import org.apache.lucene.index.AtomicReaderContext;
/**
*/
public interface IndexGeoPointFieldData<FD extends AtomicGeoPointFieldData> extends IndexFieldData<FD> {
/**
* Loads the atomic field data for the reader, possibly cached.
*/
FD load(AtomicReaderContext context);
/**
* Loads directly the atomic field data for the reader, ignoring any caching involved.
*/
FD loadDirect(AtomicReaderContext context) throws Exception;
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.index.fielddata;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.elasticsearch.index.fielddata.util.*; import org.elasticsearch.index.fielddata.util.*;
import org.elasticsearch.index.mapper.geo.GeoPoint;
/** /**
* Script level doc values, the assumption is that any implementation will implement a <code>getValue</code> * Script level doc values, the assumption is that any implementation will implement a <code>getValue</code>
@ -255,4 +256,32 @@ public interface ScriptDocValues {
return values.getValues(docId); return values.getValues(docId);
} }
} }
static class GeoPoints implements ScriptDocValues {
private final GeoPointValues values;
private int docId;
public GeoPoints(GeoPointValues values) {
this.values = values;
}
@Override
public void setNextDocId(int docId) {
this.docId = docId;
}
@Override
public boolean isEmpty() {
return !values.hasValue(docId);
}
public GeoPoint getValue() {
return values.getValue(docId);
}
public GeoPointArrayRef getValues() {
return values.getValues(docId);
}
}
} }

View File

@ -0,0 +1,756 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata.plain;
import org.apache.lucene.util.FixedBitSet;
import org.elasticsearch.common.RamUsage;
import org.elasticsearch.index.fielddata.AtomicGeoPointFieldData;
import org.elasticsearch.index.fielddata.BytesValues;
import org.elasticsearch.index.fielddata.HashedBytesValues;
import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.util.GeoPointArrayRef;
import org.elasticsearch.index.fielddata.util.IntArrayRef;
import org.elasticsearch.index.fielddata.util.StringArrayRef;
import org.elasticsearch.index.mapper.geo.GeoPoint;
import org.elasticsearch.index.search.geo.GeoHashUtils;
/**
*/
public abstract class GeoPointDoubleArrayAtomicFieldData implements AtomicGeoPointFieldData {
protected final double[] lon;
protected final double[] lat;
private final int numDocs;
protected long size = -1;
public GeoPointDoubleArrayAtomicFieldData(double[] lon, double[] lat, int numDocs) {
this.lon = lon;
this.lat = lat;
this.numDocs = numDocs;
}
@Override
public int getNumDocs() {
return numDocs;
}
@Override
public ScriptDocValues getScriptValues() {
return new ScriptDocValues.GeoPoints(getGeoPointValues());
}
public static class WithOrdinals extends GeoPointDoubleArrayAtomicFieldData {
private final Ordinals ordinals;
public WithOrdinals(double[] lon, double[] lat, int numDocs, Ordinals ordinals) {
super(lon, lat, numDocs);
this.ordinals = ordinals;
}
@Override
public boolean isMultiValued() {
return ordinals.isMultiValued();
}
@Override
public boolean isValuesOrdered() {
return true;
}
@Override
public long getMemorySizeInBytes() {
if (size == -1) {
size = RamUsage.NUM_BYTES_INT/*size*/ + RamUsage.NUM_BYTES_INT/*numDocs*/ + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lon.length * RamUsage.NUM_BYTES_DOUBLE)) + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lat.length * RamUsage.NUM_BYTES_DOUBLE)) + ordinals.getMemorySizeInBytes();
}
return size;
}
@Override
public BytesValues getBytesValues() {
return new BytesValues.StringBased(getStringValues());
}
@Override
public HashedBytesValues getHashedBytesValues() {
return new HashedBytesValues.StringBased(getStringValues());
}
@Override
public StringValues getStringValues() {
return new StringValues(lon, lat, ordinals.ordinals());
}
@Override
public GeoPointValues getGeoPointValues() {
return new GeoPointValues(lon, lat, ordinals.ordinals());
}
static class StringValues implements org.elasticsearch.index.fielddata.StringValues {
private final double[] lon;
private final double[] lat;
private final Ordinals.Docs ordinals;
private final StringArrayRef arrayScratch = new StringArrayRef(new String[1], 1);
private final ValuesIter valuesIter;
StringValues(double[] lon, double[] lat, Ordinals.Docs ordinals) {
this.lon = lon;
this.lat = lat;
this.ordinals = ordinals;
this.valuesIter = new ValuesIter(lon, lat);
}
@Override
public boolean isMultiValued() {
return ordinals.isMultiValued();
}
@Override
public boolean hasValue(int docId) {
return ordinals.getOrd(docId) != 0;
}
@Override
public String getValue(int docId) {
int ord = ordinals.getOrd(docId);
if (ord == 0) {
return null;
}
return GeoHashUtils.encode(lat[ord], lon[ord]);
}
@Override
public StringArrayRef getValues(int docId) {
IntArrayRef ords = ordinals.getOrds(docId);
int size = ords.size();
if (size == 0) return StringArrayRef.EMPTY;
arrayScratch.reset(size);
for (int i = ords.start; i < ords.end; i++) {
int ord = ords.values[i];
arrayScratch.values[arrayScratch.end++] = GeoHashUtils.encode(lat[ord], lon[ord]);
}
return arrayScratch;
}
@Override
public Iter getIter(int docId) {
return valuesIter.reset(ordinals.getIter(docId));
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
Ordinals.Docs.Iter iter = ordinals.getIter(docId);
int ord = iter.next();
if (ord == 0) {
proc.onMissing(docId);
return;
}
do {
proc.onValue(docId, GeoHashUtils.encode(lat[ord], lon[ord]));
} while ((ord = iter.next()) != 0);
}
static class ValuesIter implements Iter {
private final double[] lon;
private final double[] lat;
private Ordinals.Docs.Iter ordsIter;
private int ord;
ValuesIter(double[] lon, double[] lat) {
this.lon = lon;
this.lat = lat;
}
public ValuesIter reset(Ordinals.Docs.Iter ordsIter) {
this.ordsIter = ordsIter;
this.ord = ordsIter.next();
return this;
}
@Override
public boolean hasNext() {
return ord != 0;
}
@Override
public String next() {
String value = GeoHashUtils.encode(lat[ord], lon[ord]);
ord = ordsIter.next();
return value;
}
}
}
static class GeoPointValues implements org.elasticsearch.index.fielddata.GeoPointValues {
private final double[] lon;
private final double[] lat;
private final Ordinals.Docs ordinals;
private final GeoPoint scratch = new GeoPoint();
private final GeoPointArrayRef arrayScratch = new GeoPointArrayRef(new GeoPoint[1], 1);
private final ValuesIter valuesIter;
private final SafeValuesIter safeValuesIter;
GeoPointValues(double[] lon, double[] lat, Ordinals.Docs ordinals) {
this.lon = lon;
this.lat = lat;
this.ordinals = ordinals;
this.valuesIter = new ValuesIter(lon, lat);
this.safeValuesIter = new SafeValuesIter(lon, lat);
}
@Override
public boolean isMultiValued() {
return ordinals.isMultiValued();
}
@Override
public boolean hasValue(int docId) {
return ordinals.getOrd(docId) != 0;
}
@Override
public GeoPoint getValue(int docId) {
int ord = ordinals.getOrd(docId);
if (ord == 0) {
return null;
}
return scratch.reset(lat[ord], lon[ord]);
}
@Override
public GeoPoint getValueSafe(int docId) {
int ord = ordinals.getOrd(docId);
if (ord == 0) {
return null;
}
return new GeoPoint(lat[ord], lon[ord]);
}
@Override
public GeoPointArrayRef getValues(int docId) {
IntArrayRef ords = ordinals.getOrds(docId);
int size = ords.size();
if (size == 0) return GeoPointArrayRef.EMPTY;
arrayScratch.reset(size);
for (int i = ords.start; i < ords.end; i++) {
int ord = ords.values[i];
arrayScratch.values[arrayScratch.end++].reset(lat[ord], lon[ord]);
}
return arrayScratch;
}
@Override
public Iter getIter(int docId) {
return valuesIter.reset(ordinals.getIter(docId));
}
@Override
public Iter getIterSafe(int docId) {
return safeValuesIter.reset(ordinals.getIter(docId));
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
Ordinals.Docs.Iter iter = ordinals.getIter(docId);
int ord = iter.next();
if (ord == 0) {
proc.onMissing(docId);
return;
}
do {
proc.onValue(docId, scratch.reset(lat[ord], lon[ord]));
} while ((ord = iter.next()) != 0);
}
@Override
public void forEachSafeValueInDoc(int docId, ValueInDocProc proc) {
Ordinals.Docs.Iter iter = ordinals.getIter(docId);
int ord = iter.next();
if (ord == 0) {
proc.onMissing(docId);
return;
}
do {
proc.onValue(docId, new GeoPoint(lat[ord], lon[ord]));
} while ((ord = iter.next()) != 0);
}
@Override
public void forEachLatLonValueInDoc(int docId, LatLonValueInDocProc proc) {
Ordinals.Docs.Iter iter = ordinals.getIter(docId);
int ord = iter.next();
if (ord == 0) {
proc.onMissing(docId);
return;
}
do {
proc.onValue(docId, lat[ord], lon[ord]);
} while ((ord = iter.next()) != 0);
}
static class ValuesIter implements Iter {
private final double[] lon;
private final double[] lat;
private final GeoPoint scratch = new GeoPoint();
private Ordinals.Docs.Iter ordsIter;
private int ord;
ValuesIter(double[] lon, double[] lat) {
this.lon = lon;
this.lat = lat;
}
public ValuesIter reset(Ordinals.Docs.Iter ordsIter) {
this.ordsIter = ordsIter;
this.ord = ordsIter.next();
return this;
}
@Override
public boolean hasNext() {
return ord != 0;
}
@Override
public GeoPoint next() {
scratch.reset(lat[ord], lon[ord]);
ord = ordsIter.next();
return scratch;
}
}
static class SafeValuesIter implements Iter {
private final double[] lon;
private final double[] lat;
private Ordinals.Docs.Iter ordsIter;
private int ord;
SafeValuesIter(double[] lon, double[] lat) {
this.lon = lon;
this.lat = lat;
}
public SafeValuesIter reset(Ordinals.Docs.Iter ordsIter) {
this.ordsIter = ordsIter;
this.ord = ordsIter.next();
return this;
}
@Override
public boolean hasNext() {
return ord != 0;
}
@Override
public GeoPoint next() {
GeoPoint value = new GeoPoint(lat[ord], lon[ord]);
ord = ordsIter.next();
return value;
}
}
}
}
/**
* Assumes unset values are marked in bitset, and docId is used as the index to the value array.
*/
public static class SingleFixedSet extends GeoPointDoubleArrayAtomicFieldData {
private final FixedBitSet set;
public SingleFixedSet(double[] lon, double[] lat, int numDocs, FixedBitSet set) {
super(lon, lat, numDocs);
this.set = set;
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean isValuesOrdered() {
return false;
}
@Override
public long getMemorySizeInBytes() {
if (size == -1) {
size = RamUsage.NUM_BYTES_INT/*size*/ + RamUsage.NUM_BYTES_INT/*numDocs*/ + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lon.length * RamUsage.NUM_BYTES_DOUBLE)) + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lat.length * RamUsage.NUM_BYTES_DOUBLE)) + (set.getBits().length * RamUsage.NUM_BYTES_LONG);
}
return size;
}
@Override
public BytesValues getBytesValues() {
return new BytesValues.StringBased(getStringValues());
}
@Override
public HashedBytesValues getHashedBytesValues() {
return new HashedBytesValues.StringBased(getStringValues());
}
@Override
public StringValues getStringValues() {
return new StringValues(lon, lat, set);
}
@Override
public GeoPointValues getGeoPointValues() {
return new GeoPointValues(lon, lat, set);
}
static class StringValues implements org.elasticsearch.index.fielddata.StringValues {
private final double[] lon;
private final double[] lat;
private final FixedBitSet set;
private final StringArrayRef arrayScratch = new StringArrayRef(new String[1], 1);
private final Iter.Single iter = new Iter.Single();
StringValues(double[] lon, double[] lat, FixedBitSet set) {
this.lon = lon;
this.lat = lat;
this.set = set;
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean hasValue(int docId) {
return set.get(docId);
}
@Override
public String getValue(int docId) {
if (set.get(docId)) {
return GeoHashUtils.encode(lat[docId], lon[docId]);
} else {
return null;
}
}
@Override
public StringArrayRef getValues(int docId) {
if (set.get(docId)) {
arrayScratch.values[0] = GeoHashUtils.encode(lat[docId], lon[docId]);
return arrayScratch;
} else {
return StringArrayRef.EMPTY;
}
}
@Override
public Iter getIter(int docId) {
if (set.get(docId)) {
return iter.reset(GeoHashUtils.encode(lat[docId], lon[docId]));
} else {
return Iter.Empty.INSTANCE;
}
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
if (set.get(docId)) {
proc.onValue(docId, GeoHashUtils.encode(lat[docId], lon[docId]));
} else {
proc.onMissing(docId);
}
}
}
static class GeoPointValues implements org.elasticsearch.index.fielddata.GeoPointValues {
private final double[] lon;
private final double[] lat;
private final FixedBitSet set;
private final GeoPoint scratch = new GeoPoint();
private final GeoPointArrayRef arrayScratch = new GeoPointArrayRef(new GeoPoint[1]);
private final Iter.Single iter = new Iter.Single();
GeoPointValues(double[] lon, double[] lat, FixedBitSet set) {
this.lon = lon;
this.lat = lat;
this.set = set;
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean hasValue(int docId) {
return set.get(docId);
}
@Override
public GeoPoint getValue(int docId) {
if (set.get(docId)) {
return scratch.reset(lat[docId], lon[docId]);
} else {
return null;
}
}
@Override
public GeoPoint getValueSafe(int docId) {
if (set.get(docId)) {
return new GeoPoint(lat[docId], lon[docId]);
} else {
return null;
}
}
@Override
public GeoPointArrayRef getValues(int docId) {
if (set.get(docId)) {
arrayScratch.values[0].reset(lat[docId], lon[docId]);
return arrayScratch;
} else {
return GeoPointArrayRef.EMPTY;
}
}
@Override
public Iter getIter(int docId) {
if (set.get(docId)) {
return iter.reset(scratch.reset(lat[docId], lon[docId]));
} else {
return Iter.Empty.INSTANCE;
}
}
@Override
public Iter getIterSafe(int docId) {
if (set.get(docId)) {
return iter.reset(new GeoPoint(lat[docId], lon[docId]));
} else {
return Iter.Empty.INSTANCE;
}
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
if (set.get(docId)) {
proc.onValue(docId, scratch.reset(lat[docId], lon[docId]));
} else {
proc.onMissing(docId);
}
}
@Override
public void forEachSafeValueInDoc(int docId, ValueInDocProc proc) {
if (set.get(docId)) {
proc.onValue(docId, new GeoPoint(lat[docId], lon[docId]));
} else {
proc.onMissing(docId);
}
}
@Override
public void forEachLatLonValueInDoc(int docId, LatLonValueInDocProc proc) {
if (set.get(docId)) {
proc.onValue(docId, lat[docId], lon[docId]);
} else {
proc.onMissing(docId);
}
}
}
}
/**
* Assumes all the values are "set", and docId is used as the index to the value array.
*/
public static class Single extends GeoPointDoubleArrayAtomicFieldData {
public Single(double[] lon, double[] lat, int numDocs) {
super(lon, lat, numDocs);
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean isValuesOrdered() {
return false;
}
@Override
public long getMemorySizeInBytes() {
if (size == -1) {
size = RamUsage.NUM_BYTES_INT/*size*/ + RamUsage.NUM_BYTES_INT/*numDocs*/ + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lon.length * RamUsage.NUM_BYTES_DOUBLE)) + (RamUsage.NUM_BYTES_ARRAY_HEADER + (lat.length * RamUsage.NUM_BYTES_DOUBLE));
}
return size;
}
@Override
public BytesValues getBytesValues() {
return new BytesValues.StringBased(getStringValues());
}
@Override
public HashedBytesValues getHashedBytesValues() {
return new HashedBytesValues.StringBased(getStringValues());
}
@Override
public StringValues getStringValues() {
return new StringValues(lon, lat);
}
@Override
public GeoPointValues getGeoPointValues() {
return new GeoPointValues(lon, lat);
}
static class StringValues implements org.elasticsearch.index.fielddata.StringValues {
private final double[] lon;
private final double[] lat;
private final StringArrayRef arrayScratch = new StringArrayRef(new String[1], 1);
private final Iter.Single iter = new Iter.Single();
StringValues(double[] lon, double[] lat) {
this.lon = lon;
this.lat = lat;
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean hasValue(int docId) {
return true;
}
@Override
public String getValue(int docId) {
return GeoHashUtils.encode(lat[docId], lon[docId]);
}
@Override
public StringArrayRef getValues(int docId) {
arrayScratch.values[0] = GeoHashUtils.encode(lat[docId], lon[docId]);
return arrayScratch;
}
@Override
public Iter getIter(int docId) {
return iter.reset(GeoHashUtils.encode(lat[docId], lon[docId]));
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
proc.onValue(docId, GeoHashUtils.encode(lat[docId], lon[docId]));
}
}
static class GeoPointValues implements org.elasticsearch.index.fielddata.GeoPointValues {
private final double[] lon;
private final double[] lat;
private final GeoPoint scratch = new GeoPoint();
private final GeoPointArrayRef arrayScratch = new GeoPointArrayRef(new GeoPoint[1]);
private final Iter.Single iter = new Iter.Single();
GeoPointValues(double[] lon, double[] lat) {
this.lon = lon;
this.lat = lat;
}
@Override
public boolean isMultiValued() {
return false;
}
@Override
public boolean hasValue(int docId) {
return true;
}
@Override
public GeoPoint getValue(int docId) {
return scratch.reset(lat[docId], lon[docId]);
}
@Override
public GeoPoint getValueSafe(int docId) {
return new GeoPoint(lat[docId], lon[docId]);
}
@Override
public GeoPointArrayRef getValues(int docId) {
arrayScratch.values[0].reset(lat[docId], lon[docId]);
return arrayScratch;
}
@Override
public Iter getIter(int docId) {
return iter.reset(scratch.reset(lat[docId], lon[docId]));
}
@Override
public Iter getIterSafe(int docId) {
return iter.reset(new GeoPoint(lat[docId], lon[docId]));
}
@Override
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
proc.onValue(docId, scratch.reset(lat[docId], lon[docId]));
}
@Override
public void forEachSafeValueInDoc(int docId, ValueInDocProc proc) {
proc.onValue(docId, new GeoPoint(lat[docId], lon[docId]));
}
@Override
public void forEachLatLonValueInDoc(int docId, LatLonValueInDocProc proc) {
proc.onValue(docId, lat[docId], lon[docId]);
}
}
}
}

View File

@ -0,0 +1,160 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TDoubleArrayList;
import org.apache.lucene.index.*;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FixedBitSet;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.ordinals.MultiFlatArrayOrdinals;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings;
import java.util.ArrayList;
/**
*/
public class GeoPointDoubleArrayIndexFieldData extends AbstractIndexFieldData<GeoPointDoubleArrayAtomicFieldData> implements IndexGeoPointFieldData<GeoPointDoubleArrayAtomicFieldData> {
public static class Builder implements IndexFieldData.Builder {
@Override
public IndexFieldData build(Index index, @IndexSettings Settings indexSettings, FieldMapper.Names fieldNames, FieldDataType type, IndexFieldDataCache cache) {
return new GeoPointDoubleArrayIndexFieldData(index, indexSettings, fieldNames, type, cache);
}
}
public GeoPointDoubleArrayIndexFieldData(Index index, @IndexSettings Settings indexSettings, FieldMapper.Names fieldNames, FieldDataType fieldDataType, IndexFieldDataCache cache) {
super(index, indexSettings, fieldNames, fieldDataType, cache);
}
@Override
public boolean valuesOrdered() {
// because we might have single values? we can dynamically update a flag to reflect that
// based on the atomic field data loaded
return false;
}
@Override
public GeoPointDoubleArrayAtomicFieldData load(AtomicReaderContext context) {
try {
return cache.load(context, this);
} catch (Throwable e) {
if (e instanceof ElasticSearchException) {
throw (ElasticSearchException) e;
} else {
throw new ElasticSearchException(e.getMessage(), e);
}
}
}
@Override
public GeoPointDoubleArrayAtomicFieldData loadDirect(AtomicReaderContext context) throws Exception {
AtomicReader reader = context.reader();
Terms terms = reader.terms(getFieldNames().indexName());
if (terms == null) {
return new GeoPointDoubleArrayAtomicFieldData.Single(new double[0], new double[0], 0);
}
// TODO: how can we guess the number of terms? numerics end up creating more terms per value...
final TDoubleArrayList lat = new TDoubleArrayList();
final TDoubleArrayList lon = new TDoubleArrayList();
ArrayList<int[]> ordinals = new ArrayList<int[]>();
int[] idx = new int[reader.maxDoc()];
ordinals.add(new int[reader.maxDoc()]);
lat.add(0); // first "t" indicates null value
lon.add(0); // first "t" indicates null value
int termOrd = 1; // current term number
TermsEnum termsEnum = terms.iterator(null);
try {
DocsEnum docsEnum = null;
for (BytesRef term = termsEnum.next(); term != null; term = termsEnum.next()) {
String location = term.utf8ToString();
int comma = location.indexOf(',');
lat.add(Double.parseDouble(location.substring(0, comma)));
lon.add(Double.parseDouble(location.substring(comma + 1)));
docsEnum = termsEnum.docs(reader.getLiveDocs(), docsEnum, 0);
for (int docId = docsEnum.nextDoc(); docId != DocsEnum.NO_MORE_DOCS; docId = docsEnum.nextDoc()) {
int[] ordinal;
if (idx[docId] >= ordinals.size()) {
ordinal = new int[reader.maxDoc()];
ordinals.add(ordinal);
} else {
ordinal = ordinals.get(idx[docId]);
}
ordinal[docId] = termOrd;
idx[docId]++;
}
termOrd++;
}
} catch (RuntimeException e) {
if (e.getClass().getName().endsWith("StopFillCacheException")) {
// all is well, in case numeric parsers are used.
} else {
throw e;
}
}
if (ordinals.size() == 1) {
int[] nativeOrdinals = ordinals.get(0);
FixedBitSet set = new FixedBitSet(reader.maxDoc());
double[] sLat = new double[reader.maxDoc()];
double[] sLon = new double[reader.maxDoc()];
boolean allHaveValue = true;
for (int i = 0; i < nativeOrdinals.length; i++) {
int nativeOrdinal = nativeOrdinals[i];
if (nativeOrdinal == 0) {
allHaveValue = false;
} else {
set.set(i);
sLat[i] = lat.get(nativeOrdinal);
sLon[i] = lon.get(nativeOrdinal);
}
}
if (allHaveValue) {
return new GeoPointDoubleArrayAtomicFieldData.Single(sLon, sLat, reader.maxDoc());
} else {
return new GeoPointDoubleArrayAtomicFieldData.SingleFixedSet(sLon, sLat, reader.maxDoc(), set);
}
} else {
int[][] nativeOrdinals = new int[ordinals.size()][];
for (int i = 0; i < nativeOrdinals.length; i++) {
nativeOrdinals[i] = ordinals.get(i);
}
return new GeoPointDoubleArrayAtomicFieldData.WithOrdinals(lon.toArray(new double[lon.size()]), lat.toArray(new double[lat.size()]), reader.maxDoc(), new MultiFlatArrayOrdinals(nativeOrdinals, termOrd));
}
}
@Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) {
throw new ElasticSearchIllegalArgumentException("can't sort on geo_point field without using specific sorting feature, like geo_distance");
}
}

View File

@ -0,0 +1,164 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata.util;
import org.apache.lucene.util.ArrayUtil;
import org.elasticsearch.index.mapper.geo.GeoPoint;
import java.util.AbstractList;
import java.util.RandomAccess;
/**
*/
public class GeoPointArrayRef extends AbstractList<GeoPoint> implements RandomAccess {
public static final GeoPointArrayRef EMPTY = new GeoPointArrayRef(new GeoPoint[0]);
public GeoPoint[] values;
public int start;
public int end;
public GeoPointArrayRef(GeoPoint[] values) {
this(values, 0, values.length);
}
public GeoPointArrayRef(GeoPoint[] values, int length) {
this(values, 0, length);
}
public GeoPointArrayRef(GeoPoint[] values, int start, int end) {
this.values = values;
this.start = start;
this.end = end;
for (int i = start; i < end; i++) {
this.values[i] = new GeoPoint();
}
}
public void reset(int newLength) {
assert start == 0; // NOTE: senseless if offset != 0
end = 0;
if (values.length < newLength) {
values = new GeoPoint[ArrayUtil.oversize(newLength, 32)];
for (int i = 0; i < values.length; i++) {
values[i] = new GeoPoint();
}
}
}
@Override
public int size() {
return end - start;
}
@Override
public boolean isEmpty() {
return size() != 0;
}
@Override
public GeoPoint get(int index) {
assert index >= 0 && index < size();
return values[start + index];
}
@Override
public boolean contains(Object target) {
if (!(target instanceof GeoPoint)) {
return false;
}
for (int i = start; i < end; i++) {
if (values[i].equals((GeoPoint) target)) return true;
}
return false;
}
@Override
public int indexOf(Object target) {
if (!(target instanceof GeoPoint)) {
return -1;
}
GeoPoint geoPoint = (GeoPoint) target;
for (int i = start; i < end; i++) {
if (values[i].equals(geoPoint)) return (i - start);
}
return -1;
}
@Override
public int lastIndexOf(Object target) {
if (!(target instanceof GeoPoint)) {
return -1;
}
GeoPoint geoPoint = (GeoPoint) target;
for (int i = end - 1; i >= start; i--) {
if (values[i].equals(target)) return (i - start);
}
return -1;
}
@Override
public GeoPoint set(int index, GeoPoint element) {
assert index >= 0 && index < size();
GeoPoint oldValue = values[start + index];
values[start + index] = element;
return oldValue;
}
@Override
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (object instanceof GeoPointArrayRef) {
GeoPointArrayRef that = (GeoPointArrayRef) object;
int size = size();
if (that.size() != size) {
return false;
}
for (int i = 0; i < size; i++) {
if (!values[start + i].equals(that.values[that.start + i])) {
return false;
}
}
return true;
}
return super.equals(object);
}
@Override
public int hashCode() {
int result = 1;
for (int i = start; i < end; i++) {
result = 31 * result + values[i].hashCode();
}
return result;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(size() * 10);
builder.append('[').append(values[start]);
for (int i = start + 1; i < end; i++) {
builder.append(", ").append(values[i]);
}
return builder.append(']').toString();
}
}

View File

@ -27,10 +27,9 @@ import org.elasticsearch.index.search.geo.GeoHashUtils;
public class GeoPoint { public class GeoPoint {
private double lat; private double lat;
private double lon; private double lon;
GeoPoint() { public GeoPoint() {
} }
public GeoPoint(double lat, double lon) { public GeoPoint(double lat, double lon) {
@ -38,6 +37,12 @@ public class GeoPoint {
this.lon = lon; this.lon = lon;
} }
public GeoPoint reset(double lat, double lon) {
this.lat = lat;
this.lon = lon;
return this;
}
void latlon(double lat, double lon) { void latlon(double lat, double lon) {
this.lat = lat; this.lat = lat;
this.lon = lon; this.lon = lon;
@ -66,4 +71,28 @@ public class GeoPoint {
public final String getGeohash() { public final String getGeohash() {
return GeoHashUtils.encode(lat, lon); return GeoHashUtils.encode(lat, lon);
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GeoPoint geoPoint = (GeoPoint) o;
if (Double.compare(geoPoint.lat, lat) != 0) return false;
if (Double.compare(geoPoint.lon, lon) != 0) return false;
return true;
}
@Override
public int hashCode() {
int result;
long temp;
temp = lat != +0.0d ? Double.doubleToLongBits(lat) : 0L;
result = (int) (temp ^ (temp >>> 32));
temp = lon != +0.0d ? Double.doubleToLongBits(lon) : 0L;
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
} }

View File

@ -580,6 +580,11 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser {
return GeoPointFieldMapper.Defaults.FIELD_TYPE; return GeoPointFieldMapper.Defaults.FIELD_TYPE;
} }
@Override
public org.elasticsearch.index.fielddata.FieldDataType fieldDataType2() {
return new org.elasticsearch.index.fielddata.FieldDataType("geo_point");
}
@Override @Override
public FieldDataType fieldDataType() { public FieldDataType fieldDataType() {
return GeoPointFieldDataType.TYPE; return GeoPointFieldDataType.TYPE;

View File

@ -21,14 +21,11 @@ package org.elasticsearch.search.facet.geodistance;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.cache.field.data.FieldDataCache; import org.elasticsearch.index.fielddata.GeoPointValues;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.mapper.geo.GeoPointFieldData;
import org.elasticsearch.index.mapper.geo.GeoPointFieldDataType;
import org.elasticsearch.index.search.geo.GeoDistance; import org.elasticsearch.index.search.geo.GeoDistance;
import org.elasticsearch.search.facet.AbstractFacetCollector; import org.elasticsearch.search.facet.AbstractFacetCollector;
import org.elasticsearch.search.facet.Facet; import org.elasticsearch.search.facet.Facet;
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
@ -38,10 +35,9 @@ import java.io.IOException;
*/ */
public class GeoDistanceFacetCollector extends AbstractFacetCollector { public class GeoDistanceFacetCollector extends AbstractFacetCollector {
protected final String indexFieldName; protected final IndexGeoPointFieldData indexFieldData;
protected final double lat; protected final double lat;
protected final double lon; protected final double lon;
protected final DistanceUnit unit; protected final DistanceUnit unit;
@ -49,15 +45,12 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
protected final GeoDistance geoDistance; protected final GeoDistance geoDistance;
protected final GeoDistance.FixedSourceDistance fixedSourceDistance; protected final GeoDistance.FixedSourceDistance fixedSourceDistance;
protected final FieldDataCache fieldDataCache; protected GeoPointValues values;
protected GeoPointFieldData fieldData;
protected final GeoDistanceFacet.Entry[] entries; protected final GeoDistanceFacet.Entry[] entries;
protected GeoPointValues.LatLonValueInDocProc aggregator;
protected GeoPointFieldData.ValueInDocProc aggregator; public GeoDistanceFacetCollector(String facetName, IndexGeoPointFieldData indexFieldData, 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);
this.lat = lat; this.lat = lat;
@ -65,30 +58,14 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
this.unit = unit; this.unit = unit;
this.entries = entries; this.entries = entries;
this.geoDistance = geoDistance; this.geoDistance = geoDistance;
this.fieldDataCache = context.fieldDataCache(); this.indexFieldData = indexFieldData;
this.fixedSourceDistance = geoDistance.fixedSourceDistance(lat, lon, unit); this.fixedSourceDistance = geoDistance.fixedSourceDistance(lat, lon, unit);
MapperService.SmartNameFieldMappers smartMappers = context.smartFieldMappers(fieldName);
if (smartMappers == null || !smartMappers.hasMapper()) {
throw new FacetPhaseExecutionException(facetName, "No mapping found for field [" + fieldName + "]");
}
if (smartMappers.mapper().fieldDataType() != GeoPointFieldDataType.TYPE) {
throw new FacetPhaseExecutionException(facetName, "field [" + fieldName + "] is not a geo_point field");
}
// add type filter if there is exact doc mapper associated with it
if (smartMappers.explicitTypeInNameWithDocMapper()) {
setFilter(context.filterCache().cache(smartMappers.docMapper().typeFilter()));
}
this.indexFieldName = smartMappers.mapper().names().indexName();
this.aggregator = new Aggregator(fixedSourceDistance, entries); this.aggregator = new Aggregator(fixedSourceDistance, entries);
} }
@Override @Override
protected void doSetNextReader(AtomicReaderContext context) throws IOException { protected void doSetNextReader(AtomicReaderContext context) throws IOException {
fieldData = (GeoPointFieldData) fieldDataCache.cache(GeoPointFieldDataType.TYPE, context.reader(), indexFieldName); values = indexFieldData.load(context).getGeoPointValues();
} }
@Override @Override
@ -96,7 +73,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
for (GeoDistanceFacet.Entry entry : entries) { for (GeoDistanceFacet.Entry entry : entries) {
entry.foundInDoc = false; entry.foundInDoc = false;
} }
fieldData.forEachValueInDoc(doc, aggregator); values.forEachLatLonValueInDoc(doc, aggregator);
} }
@Override @Override
@ -104,7 +81,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
return new InternalGeoDistanceFacet(facetName, entries); return new InternalGeoDistanceFacet(facetName, entries);
} }
public static class Aggregator implements GeoPointFieldData.ValueInDocProc { public static class Aggregator implements GeoPointValues.LatLonValueInDocProc {
private final GeoDistance.FixedSourceDistance fixedSourceDistance; private final GeoDistance.FixedSourceDistance fixedSourceDistance;
@ -115,6 +92,10 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
this.entries = entries; this.entries = entries;
} }
@Override
public void onMissing(int docId) {
}
@Override @Override
public void onValue(int docId, double lat, double lon) { public void onValue(int docId, double lat, double lon) {
double distance = fixedSourceDistance.calculate(lat, lon); double distance = fixedSourceDistance.calculate(lat, lon);

View File

@ -25,6 +25,9 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper;
import org.elasticsearch.index.search.geo.GeoDistance; import org.elasticsearch.index.search.geo.GeoDistance;
import org.elasticsearch.index.search.geo.GeoHashUtils; import org.elasticsearch.index.search.geo.GeoHashUtils;
@ -179,17 +182,28 @@ public class GeoDistanceFacetProcessor extends AbstractComponent implements Face
lon = point.lon; lon = point.lon;
} }
FieldMapper keyFieldMapper = context.smartNameFieldMapper(fieldName);
if (keyFieldMapper == null) {
throw new FacetPhaseExecutionException(facetName, "failed to find mapping for [" + fieldName + "]");
}
IndexGeoPointFieldData keyIndexFieldData = context.fieldData().getForField(keyFieldMapper);
if (valueFieldName != null) { if (valueFieldName != null) {
return new ValueGeoDistanceFacetCollector(facetName, fieldName, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]), FieldMapper valueFieldMapper = context.smartNameFieldMapper(valueFieldName);
context, valueFieldName); if (valueFieldMapper == null) {
throw new FacetPhaseExecutionException(facetName, "failed to find mapping for [" + valueFieldName + "]");
}
IndexNumericFieldData valueIndexFieldData = context.fieldData().getForField(valueFieldMapper);
return new ValueGeoDistanceFacetCollector(facetName, keyIndexFieldData, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]),
context, valueIndexFieldData);
} }
if (valueScript != null) { if (valueScript != null) {
return new ScriptGeoDistanceFacetCollector(facetName, fieldName, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]), return new ScriptGeoDistanceFacetCollector(facetName, keyIndexFieldData, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]),
context, scriptLang, valueScript, params); context, scriptLang, valueScript, params);
} }
return new GeoDistanceFacetCollector(facetName, fieldName, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]), return new GeoDistanceFacetCollector(facetName, keyIndexFieldData, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]),
context); context);
} }

View File

@ -22,7 +22,8 @@ package org.elasticsearch.search.facet.geodistance;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
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.geo.GeoPointFieldData; import org.elasticsearch.index.fielddata.GeoPointValues;
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
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;
@ -39,10 +40,10 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
private Aggregator scriptAggregator; private Aggregator scriptAggregator;
public ScriptGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance, public ScriptGeoDistanceFacetCollector(String facetName, IndexGeoPointFieldData indexFieldData, 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, indexFieldData, 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(fixedSourceDistance, entries); this.aggregator = new Aggregator(fixedSourceDistance, entries);
@ -67,7 +68,7 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
super.doCollect(doc); super.doCollect(doc);
} }
public static class Aggregator implements GeoPointFieldData.ValueInDocProc { public static class Aggregator implements GeoPointValues.LatLonValueInDocProc {
private final GeoDistance.FixedSourceDistance fixedSourceDistance; private final GeoDistance.FixedSourceDistance fixedSourceDistance;
@ -80,6 +81,10 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
this.entries = entries; this.entries = entries;
} }
@Override
public void onMissing(int docId) {
}
@Override @Override
public void onValue(int docId, double lat, double lon) { public void onValue(int docId, double lat, double lon) {
double distance = fixedSourceDistance.calculate(lat, lon); double distance = fixedSourceDistance.calculate(lat, lon);

View File

@ -21,12 +21,11 @@ package org.elasticsearch.search.facet.geodistance;
import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.index.AtomicReaderContext;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.field.data.FieldDataType; import org.elasticsearch.index.fielddata.DoubleValues;
import org.elasticsearch.index.field.data.NumericFieldData; import org.elasticsearch.index.fielddata.GeoPointValues;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
import org.elasticsearch.index.mapper.geo.GeoPointFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.search.geo.GeoDistance; import org.elasticsearch.index.search.geo.GeoDistance;
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
@ -36,35 +35,27 @@ import java.io.IOException;
*/ */
public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector { public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
private final String indexValueFieldName; private final IndexNumericFieldData valueIndexFieldData;
private final FieldDataType valueFieldDataType; public ValueGeoDistanceFacetCollector(String facetName, IndexGeoPointFieldData indexFieldData, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
GeoDistanceFacet.Entry[] entries, SearchContext context, IndexNumericFieldData valueIndexFieldData) {
public ValueGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance, super(facetName, indexFieldData, lat, lon, unit, geoDistance, entries, context);
GeoDistanceFacet.Entry[] entries, SearchContext context, String valueFieldName) { this.valueIndexFieldData = valueIndexFieldData;
super(facetName, fieldName, lat, lon, unit, geoDistance, entries, context);
MapperService.SmartNameFieldMappers smartMappers = context.smartFieldMappers(valueFieldName);
if (smartMappers == null || !smartMappers.hasMapper()) {
throw new FacetPhaseExecutionException(facetName, "No mapping found for field [" + valueFieldName + "]");
}
this.indexValueFieldName = smartMappers.mapper().names().indexName();
this.valueFieldDataType = smartMappers.mapper().fieldDataType();
this.aggregator = new Aggregator(fixedSourceDistance, entries); this.aggregator = new Aggregator(fixedSourceDistance, entries);
} }
@Override @Override
protected void doSetNextReader(AtomicReaderContext context) throws IOException { protected void doSetNextReader(AtomicReaderContext context) throws IOException {
super.doSetNextReader(context); super.doSetNextReader(context);
((Aggregator) this.aggregator).valueFieldData = (NumericFieldData) fieldDataCache.cache(valueFieldDataType, context.reader(), indexValueFieldName); ((Aggregator) this.aggregator).valueValues = valueIndexFieldData.load(context).getDoubleValues();
} }
public static class Aggregator implements GeoPointFieldData.ValueInDocProc { public static class Aggregator implements GeoPointValues.LatLonValueInDocProc {
private final GeoDistance.FixedSourceDistance fixedSourceDistance; private final GeoDistance.FixedSourceDistance fixedSourceDistance;
private final GeoDistanceFacet.Entry[] entries; private final GeoDistanceFacet.Entry[] entries;
NumericFieldData valueFieldData; DoubleValues valueValues;
final ValueAggregator valueAggregator = new ValueAggregator(); final ValueAggregator valueAggregator = new ValueAggregator();
@ -73,6 +64,10 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
this.entries = entries; this.entries = entries;
} }
@Override
public void onMissing(int docId) {
}
@Override @Override
public void onValue(int docId, double lat, double lon) { public void onValue(int docId, double lat, double lon) {
double distance = fixedSourceDistance.calculate(lat, lon); double distance = fixedSourceDistance.calculate(lat, lon);
@ -84,16 +79,20 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
entry.foundInDoc = true; entry.foundInDoc = true;
entry.count++; entry.count++;
valueAggregator.entry = entry; valueAggregator.entry = entry;
valueFieldData.forEachValueInDoc(docId, valueAggregator); valueValues.forEachValueInDoc(docId, valueAggregator);
} }
} }
} }
} }
public static class ValueAggregator implements NumericFieldData.DoubleValueInDocProc { public static class ValueAggregator implements DoubleValues.ValueInDocProc {
GeoDistanceFacet.Entry entry; GeoDistanceFacet.Entry entry;
@Override
public void onMissing(int docId) {
}
@Override @Override
public void onValue(int docId, double value) { public void onValue(int docId, double value) {
entry.totalCount++; entry.totalCount++;