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.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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
Loading…
Reference in New Issue