geo point new field mapper with geo distance facet based impl
This commit is contained in:
parent
2e86081f7b
commit
d82859c82b
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ import org.elasticsearch.index.AbstractIndexComponent;
|
|||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.fielddata.plain.ConcreteBytesRefIndexFieldData;
|
||||
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.mapper.FieldMapper;
|
||||
import org.elasticsearch.index.settings.IndexSettings;
|
||||
|
@ -49,12 +50,14 @@ public class IndexFieldDataService extends AbstractIndexComponent {
|
|||
.put("string", new ConcreteBytesRefIndexFieldData.Builder())
|
||||
.put("double", new DoubleArrayIndexFieldData.Builder())
|
||||
.put("long", new LongArrayIndexFieldData.Builder())
|
||||
.put("geo_point", new GeoPointDoubleArrayIndexFieldData.Builder())
|
||||
.immutableMap();
|
||||
|
||||
buildersByTypeAndFormat = MapBuilder.<Tuple<String, String>, IndexFieldData.Builder>newMapBuilder()
|
||||
.put(Tuple.tuple("string", "concrete_bytes"), new ConcreteBytesRefIndexFieldData.Builder())
|
||||
.put(Tuple.tuple("double", "array"), new DoubleArrayIndexFieldData.Builder())
|
||||
.put(Tuple.tuple("long", "array"), new LongArrayIndexFieldData.Builder())
|
||||
.put(Tuple.tuple("geo_point", "array"), new GeoPointDoubleArrayIndexFieldData.Builder())
|
||||
.immutableMap();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.fielddata;
|
|||
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
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>
|
||||
|
@ -255,4 +256,32 @@ public interface ScriptDocValues {
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -27,10 +27,9 @@ import org.elasticsearch.index.search.geo.GeoHashUtils;
|
|||
public class GeoPoint {
|
||||
|
||||
private double lat;
|
||||
|
||||
private double lon;
|
||||
|
||||
GeoPoint() {
|
||||
public GeoPoint() {
|
||||
}
|
||||
|
||||
public GeoPoint(double lat, double lon) {
|
||||
|
@ -38,6 +37,12 @@ public class GeoPoint {
|
|||
this.lon = lon;
|
||||
}
|
||||
|
||||
public GeoPoint reset(double lat, double lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
return this;
|
||||
}
|
||||
|
||||
void latlon(double lat, double lon) {
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
|
@ -66,4 +71,28 @@ public class GeoPoint {
|
|||
public final String getGeohash() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -580,6 +580,11 @@ public class GeoPointFieldMapper implements Mapper, ArrayValueMapperParser {
|
|||
return GeoPointFieldMapper.Defaults.FIELD_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.elasticsearch.index.fielddata.FieldDataType fieldDataType2() {
|
||||
return new org.elasticsearch.index.fielddata.FieldDataType("geo_point");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldDataType fieldDataType() {
|
||||
return GeoPointFieldDataType.TYPE;
|
||||
|
|
|
@ -21,14 +21,11 @@ package org.elasticsearch.search.facet.geodistance;
|
|||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.index.cache.field.data.FieldDataCache;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.geo.GeoPointFieldData;
|
||||
import org.elasticsearch.index.mapper.geo.GeoPointFieldDataType;
|
||||
import org.elasticsearch.index.fielddata.GeoPointValues;
|
||||
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
|
||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||
import org.elasticsearch.search.facet.AbstractFacetCollector;
|
||||
import org.elasticsearch.search.facet.Facet;
|
||||
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -38,10 +35,9 @@ import java.io.IOException;
|
|||
*/
|
||||
public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
||||
|
||||
protected final String indexFieldName;
|
||||
protected final IndexGeoPointFieldData indexFieldData;
|
||||
|
||||
protected final double lat;
|
||||
|
||||
protected final double lon;
|
||||
|
||||
protected final DistanceUnit unit;
|
||||
|
@ -49,15 +45,12 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
|||
protected final GeoDistance geoDistance;
|
||||
protected final GeoDistance.FixedSourceDistance fixedSourceDistance;
|
||||
|
||||
protected final FieldDataCache fieldDataCache;
|
||||
|
||||
protected GeoPointFieldData fieldData;
|
||||
protected GeoPointValues values;
|
||||
|
||||
protected final GeoDistanceFacet.Entry[] entries;
|
||||
protected GeoPointValues.LatLonValueInDocProc aggregator;
|
||||
|
||||
protected GeoPointFieldData.ValueInDocProc aggregator;
|
||||
|
||||
public GeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||
public GeoDistanceFacetCollector(String facetName, IndexGeoPointFieldData indexFieldData, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||
GeoDistanceFacet.Entry[] entries, SearchContext context) {
|
||||
super(facetName);
|
||||
this.lat = lat;
|
||||
|
@ -65,30 +58,14 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
|||
this.unit = unit;
|
||||
this.entries = entries;
|
||||
this.geoDistance = geoDistance;
|
||||
this.fieldDataCache = context.fieldDataCache();
|
||||
|
||||
this.indexFieldData = indexFieldData;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetNextReader(AtomicReaderContext context) throws IOException {
|
||||
fieldData = (GeoPointFieldData) fieldDataCache.cache(GeoPointFieldDataType.TYPE, context.reader(), indexFieldName);
|
||||
values = indexFieldData.load(context).getGeoPointValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,7 +73,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
|||
for (GeoDistanceFacet.Entry entry : entries) {
|
||||
entry.foundInDoc = false;
|
||||
}
|
||||
fieldData.forEachValueInDoc(doc, aggregator);
|
||||
values.forEachLatLonValueInDoc(doc, aggregator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -104,7 +81,7 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
|||
return new InternalGeoDistanceFacet(facetName, entries);
|
||||
}
|
||||
|
||||
public static class Aggregator implements GeoPointFieldData.ValueInDocProc {
|
||||
public static class Aggregator implements GeoPointValues.LatLonValueInDocProc {
|
||||
|
||||
private final GeoDistance.FixedSourceDistance fixedSourceDistance;
|
||||
|
||||
|
@ -115,6 +92,10 @@ public class GeoDistanceFacetCollector extends AbstractFacetCollector {
|
|||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMissing(int docId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValue(int docId, double lat, double lon) {
|
||||
double distance = fixedSourceDistance.calculate(lat, lon);
|
||||
|
|
|
@ -25,6 +25,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
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.search.geo.GeoDistance;
|
||||
import org.elasticsearch.index.search.geo.GeoHashUtils;
|
||||
|
@ -179,17 +182,28 @@ public class GeoDistanceFacetProcessor extends AbstractComponent implements Face
|
|||
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) {
|
||||
return new ValueGeoDistanceFacetCollector(facetName, fieldName, lat, lon, unit, geoDistance, entries.toArray(new GeoDistanceFacet.Entry[entries.size()]),
|
||||
context, valueFieldName);
|
||||
FieldMapper valueFieldMapper = context.smartNameFieldMapper(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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,8 @@ package org.elasticsearch.search.facet.geodistance;
|
|||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
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.script.SearchScript;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
@ -39,10 +40,10 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
|||
|
||||
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,
|
||||
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.aggregator = new Aggregator(fixedSourceDistance, entries);
|
||||
|
@ -67,7 +68,7 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
|||
super.doCollect(doc);
|
||||
}
|
||||
|
||||
public static class Aggregator implements GeoPointFieldData.ValueInDocProc {
|
||||
public static class Aggregator implements GeoPointValues.LatLonValueInDocProc {
|
||||
|
||||
private final GeoDistance.FixedSourceDistance fixedSourceDistance;
|
||||
|
||||
|
@ -80,6 +81,10 @@ public class ScriptGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
|||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMissing(int docId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValue(int docId, double lat, double lon) {
|
||||
double distance = fixedSourceDistance.calculate(lat, lon);
|
||||
|
|
|
@ -21,12 +21,11 @@ package org.elasticsearch.search.facet.geodistance;
|
|||
|
||||
import org.apache.lucene.index.AtomicReaderContext;
|
||||
import org.elasticsearch.common.unit.DistanceUnit;
|
||||
import org.elasticsearch.index.field.data.FieldDataType;
|
||||
import org.elasticsearch.index.field.data.NumericFieldData;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.geo.GeoPointFieldData;
|
||||
import org.elasticsearch.index.fielddata.DoubleValues;
|
||||
import org.elasticsearch.index.fielddata.GeoPointValues;
|
||||
import org.elasticsearch.index.fielddata.IndexGeoPointFieldData;
|
||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||
import org.elasticsearch.index.search.geo.GeoDistance;
|
||||
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -36,35 +35,27 @@ import java.io.IOException;
|
|||
*/
|
||||
public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
||||
|
||||
private final String indexValueFieldName;
|
||||
private final IndexNumericFieldData valueIndexFieldData;
|
||||
|
||||
private final FieldDataType valueFieldDataType;
|
||||
|
||||
public ValueGeoDistanceFacetCollector(String facetName, String fieldName, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||
GeoDistanceFacet.Entry[] entries, SearchContext context, String valueFieldName) {
|
||||
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();
|
||||
public ValueGeoDistanceFacetCollector(String facetName, IndexGeoPointFieldData indexFieldData, double lat, double lon, DistanceUnit unit, GeoDistance geoDistance,
|
||||
GeoDistanceFacet.Entry[] entries, SearchContext context, IndexNumericFieldData valueIndexFieldData) {
|
||||
super(facetName, indexFieldData, lat, lon, unit, geoDistance, entries, context);
|
||||
this.valueIndexFieldData = valueIndexFieldData;
|
||||
this.aggregator = new Aggregator(fixedSourceDistance, entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doSetNextReader(AtomicReaderContext context) throws IOException {
|
||||
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 GeoDistanceFacet.Entry[] entries;
|
||||
|
||||
NumericFieldData valueFieldData;
|
||||
DoubleValues valueValues;
|
||||
|
||||
final ValueAggregator valueAggregator = new ValueAggregator();
|
||||
|
||||
|
@ -73,6 +64,10 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
|||
this.entries = entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMissing(int docId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValue(int docId, double lat, double lon) {
|
||||
double distance = fixedSourceDistance.calculate(lat, lon);
|
||||
|
@ -84,16 +79,20 @@ public class ValueGeoDistanceFacetCollector extends GeoDistanceFacetCollector {
|
|||
entry.foundInDoc = true;
|
||||
entry.count++;
|
||||
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;
|
||||
|
||||
@Override
|
||||
public void onMissing(int docId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValue(int docId, double value) {
|
||||
entry.totalCount++;
|
||||
|
|
Loading…
Reference in New Issue