org.elasticsearch.index.fielddata.ScriptDocValues Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch - Open Source, Distributed, RESTful Search Engine
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.index.fielddata;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.elasticsearch.common.geo.BoundingBox;
import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.geo.SpatialPoint;
import org.elasticsearch.geometry.utils.Geohash;
import org.elasticsearch.script.field.DocValuesScriptFieldFactory;
import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.AbstractList;
import java.util.Comparator;
import java.util.function.UnaryOperator;
/**
* Script level doc values, the assumption is that any implementation will
* implement a {@link Longs#getValue getValue} method.
*
* Implementations should not internally re-use objects for the values that they
* return as a single {@link ScriptDocValues} instance can be reused to return
* values form multiple documents.
*/
public abstract class ScriptDocValues extends AbstractList {
/**
* Supplies values to different ScriptDocValues as we
* convert them to wrappers around {@link DocValuesScriptFieldFactory}.
* This allows for different {@link DocValuesScriptFieldFactory} to implement
* this supplier class in many-to-one relationship since
* {@link DocValuesScriptFieldFactory} are more specific where
* ({byte, short, int, long, _version, murmur3, etc.} -> {long})
*/
public interface Supplier {
void setNextDocId(int docId) throws IOException;
T getInternal(int index);
int size();
}
protected final Supplier supplier;
public ScriptDocValues(Supplier supplier) {
this.supplier = supplier;
}
public Supplier getSupplier() {
return supplier;
}
// Throw meaningful exceptions if someone tries to modify the ScriptDocValues.
@Override
public final void add(int index, T element) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}
@Override
public final boolean remove(Object o) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}
@Override
public final void replaceAll(UnaryOperator operator) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}
@Override
public final T set(int index, T element) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}
@Override
public final void sort(Comparator super T> c) {
throw new UnsupportedOperationException("doc values are unmodifiable");
}
protected void throwIfEmpty() {
if (size() == 0) {
throw new IllegalStateException(
"A document doesn't have a value for a field! " + "Use doc[].size()==0 to check if a document is missing a field!"
);
}
}
public static class Longs extends ScriptDocValues {
public Longs(Supplier supplier) {
super(supplier);
}
public long getValue() {
return get(0);
}
@Override
public Long get(int index) {
throwIfEmpty();
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
public static class Dates extends ScriptDocValues {
public Dates(Supplier supplier) {
super(supplier);
}
/**
* Fetch the first field value or 0 millis after epoch if there are no
* in.
*/
public ZonedDateTime getValue() {
return get(0);
}
@Override
public ZonedDateTime get(int index) {
if (supplier.size() == 0) {
throw new IllegalStateException(
"A document doesn't have a value for a field! "
+ "Use doc[].size()==0 to check if a document is missing a field!"
);
}
if (index >= supplier.size()) {
throw new IndexOutOfBoundsException(
"attempted to fetch the [" + index + "] date when there are only [" + supplier.size() + "] dates."
);
}
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
public static class DoublesSupplier implements Supplier {
private final SortedNumericDoubleValues in;
private double[] values = new double[0];
private int count;
public DoublesSupplier(SortedNumericDoubleValues in) {
this.in = in;
}
@Override
public void setNextDocId(int docId) throws IOException {
if (in.advanceExact(docId)) {
resize(in.docValueCount());
for (int i = 0; i < count; i++) {
values[i] = in.nextValue();
}
} else {
resize(0);
}
}
/**
* Set the {@link #size()} and ensure that the {@link #values} array can
* store at least that many entries.
*/
private void resize(int newSize) {
count = newSize;
values = ArrayUtil.grow(values, count);
}
@Override
public Double getInternal(int index) {
return values[index];
}
@Override
public int size() {
return count;
}
}
public static class Doubles extends ScriptDocValues {
public Doubles(Supplier supplier) {
super(supplier);
}
public double getValue() {
return get(0);
}
@Override
public Double get(int index) {
if (supplier.size() == 0) {
throw new IllegalStateException(
"A document doesn't have a value for a field! "
+ "Use doc[].size()==0 to check if a document is missing a field!"
);
}
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
public abstract static class BaseGeometry extends ScriptDocValues {
public BaseGeometry(Supplier supplier) {
super(supplier);
}
/** Returns the dimensional type of this geometry */
public abstract int getDimensionalType();
/** Returns the bounding box of this geometry */
public abstract BoundingBox getBoundingBox();
/** Returns the suggested label position */
public abstract T getLabelPosition();
/** Returns the centroid of this geometry */
public abstract T getCentroid();
}
public interface Geometry {
/** Returns the dimensional type of this geometry */
int getDimensionalType();
/** Returns the bounding box of this geometry */
GeoBoundingBox getBoundingBox();
/** Returns the suggested label position */
GeoPoint getLabelPosition();
/** Returns the centroid of this geometry */
GeoPoint getCentroid();
/** returns the size of the geometry */
int size();
/** Returns the width of the bounding box diagonal in the spherical Mercator projection (meters) */
double getMercatorWidth();
/** Returns the height of the bounding box diagonal in the spherical Mercator projection (meters) */
double getMercatorHeight();
}
public interface GeometrySupplier extends Supplier {
T getInternalCentroid();
BoundingBox getInternalBoundingBox();
T getInternalLabelPosition();
}
public static class GeoPoints extends BaseGeometry implements Geometry {
private final GeometrySupplier geometrySupplier;
public GeoPoints(GeometrySupplier supplier) {
super(supplier);
geometrySupplier = supplier;
}
public GeoPoint getValue() {
return get(0);
}
public double getLat() {
return getValue().lat();
}
public double[] getLats() {
double[] lats = new double[size()];
for (int i = 0; i < size(); i++) {
lats[i] = get(i).lat();
}
return lats;
}
public double[] getLons() {
double[] lons = new double[size()];
for (int i = 0; i < size(); i++) {
lons[i] = get(i).lon();
}
return lons;
}
public double getLon() {
return getValue().lon();
}
@Override
public GeoPoint get(int index) {
if (supplier.size() == 0) {
throw new IllegalStateException(
"A document doesn't have a value for a field! "
+ "Use doc[].size()==0 to check if a document is missing a field!"
);
}
final GeoPoint point = supplier.getInternal(index);
return new GeoPoint(point.lat(), point.lon());
}
@Override
public int size() {
return supplier.size();
}
public double arcDistance(double lat, double lon) {
GeoPoint point = getValue();
return GeoUtils.arcDistance(point.lat(), point.lon(), lat, lon);
}
public double arcDistanceWithDefault(double lat, double lon, double defaultValue) {
if (isEmpty()) {
return defaultValue;
}
return arcDistance(lat, lon);
}
public double planeDistance(double lat, double lon) {
GeoPoint point = getValue();
return GeoUtils.planeDistance(point.lat(), point.lon(), lat, lon);
}
public double planeDistanceWithDefault(double lat, double lon, double defaultValue) {
if (isEmpty()) {
return defaultValue;
}
return planeDistance(lat, lon);
}
public double geohashDistance(String geohash) {
GeoPoint point = getValue();
return GeoUtils.arcDistance(point.lat(), point.lon(), Geohash.decodeLatitude(geohash), Geohash.decodeLongitude(geohash));
}
public double geohashDistanceWithDefault(String geohash, double defaultValue) {
if (isEmpty()) {
return defaultValue;
}
return geohashDistance(geohash);
}
@Override
public int getDimensionalType() {
return size() == 0 ? -1 : 0;
}
@Override
public GeoPoint getCentroid() {
return size() == 0 ? null : geometrySupplier.getInternalCentroid();
}
@Override
public double getMercatorWidth() {
return 0;
}
@Override
public double getMercatorHeight() {
return 0;
}
@Override
public GeoBoundingBox getBoundingBox() {
return size() == 0 ? null : (GeoBoundingBox) geometrySupplier.getInternalBoundingBox();
}
@Override
public GeoPoint getLabelPosition() {
return size() == 0 ? null : geometrySupplier.getInternalLabelPosition();
}
}
public static class Booleans extends ScriptDocValues {
public Booleans(Supplier supplier) {
super(supplier);
}
public boolean getValue() {
throwIfEmpty();
return get(0);
}
@Override
public Boolean get(int index) {
throwIfEmpty();
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
public static class StringsSupplier implements Supplier {
private final SortedBinaryDocValues in;
private BytesRefBuilder[] values = new BytesRefBuilder[0];
private int count;
public StringsSupplier(SortedBinaryDocValues in) {
this.in = in;
}
@Override
public void setNextDocId(int docId) throws IOException {
if (in.advanceExact(docId)) {
resize(in.docValueCount());
for (int i = 0; i < count; i++) {
// We need to make a copy here, because BytesBinaryDVLeafFieldData's SortedBinaryDocValues
// implementation reuses the returned BytesRef. Otherwise we would end up with the same BytesRef
// instance for all slots in the values array.
values[i].copyBytes(in.nextValue());
}
} else {
resize(0);
}
}
/**
* Set the {@link #size()} and ensure that the {@link #values} array can
* store at least that many entries.
*/
private void resize(int newSize) {
count = newSize;
if (newSize > values.length) {
final int oldLength = values.length;
values = ArrayUtil.grow(values, count);
for (int i = oldLength; i < values.length; ++i) {
values[i] = new BytesRefBuilder();
}
}
}
protected static String bytesToString(BytesRef bytesRef) {
return bytesRef.utf8ToString();
}
@Override
public String getInternal(int index) {
return bytesToString(values[index].toBytesRef());
}
@Override
public int size() {
return count;
}
}
public static class Strings extends ScriptDocValues {
public Strings(Supplier supplier) {
super(supplier);
}
public String getValue() {
return get(0);
}
@Override
public String get(int index) {
if (supplier.size() == 0) {
throw new IllegalStateException(
"A document doesn't have a value for a field! "
+ "Use doc[].size()==0 to check if a document is missing a field!"
);
}
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
public static final class BytesRefs extends ScriptDocValues {
public BytesRefs(Supplier supplier) {
super(supplier);
}
public BytesRef getValue() {
throwIfEmpty();
return get(0);
}
@Override
public BytesRef get(int index) {
throwIfEmpty();
return supplier.getInternal(index);
}
@Override
public int size() {
return supplier.size();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy