mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-28 09:28:55 -04:00
Optimize ST_EXTENT_AGG for geo_shape and cartesian_shape (#119889)
Support for `ST_EXTENT_AGG` was added in https://github.com/elastic/elasticsearch/pull/118829, and then partially optimized in https://github.com/elastic/elasticsearch/pull/118829. This optimization worked only for cartesian_shape fields, and worked by extracting the Extent from the doc-values and re-encoding it as a WKB `BBOX` geometry. This does not work for geo_shape, where we need to retain all 6 integers stored in the doc-values, in order to perform the datelline choice only at reduce time during the final phase of the aggregation. Since both geo_shape and cartesian_shape perform the aggregations using integers, and the original Extent values in the doc-values are integers, this PR expands the previous optimization by: * Saving all Extent values into a multi-valued field in an IntBlock for both cartesian_shape and geo_shape * Simplifying the logic around merging intermediate states for all cases (geo/cartesian and grouped and non-grouped aggs) * Widening test cases for testing more combinations of aggregations and types, and fixing a few bugs found * Enhancing cartesian extent to convert from 6 ints to 4 ints at block loading time (for efficiency) * Fixing bugs in both cartesian and geo extents for generating intermediate state with missing groups (flaky tests in serverless) * Moved the int order to always match Rectangle for 4-int and Extent for 6-int cases (improved internal consistency) Since the PR already changed the meaning of the invalid/infinite values of the intermediate state integers, it was already not compatible with the previous cluster versions. We disabled mixed-cluster testing to prevent errors as a result of that. This leaves us the opportunity to make further changes that are mixed-cluster incompatible, hence the decision to perform this consistency update now.
This commit is contained in:
parent
a1e6f5f841
commit
40c34cd896
53 changed files with 2669 additions and 965 deletions
5
docs/changelog/119889.yaml
Normal file
5
docs/changelog/119889.yaml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pr: 119889
|
||||||
|
summary: Optimize ST_EXTENT_AGG for `geo_shape` and `cartesian_shape`
|
||||||
|
area: "ES|QL"
|
||||||
|
type: enhancement
|
||||||
|
issues: []
|
|
@ -116,6 +116,9 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
boolean isValid();
|
boolean isValid();
|
||||||
|
|
||||||
Rectangle getResult();
|
Rectangle getResult();
|
||||||
|
|
||||||
|
/** To allow for memory optimizations through object reuse, the visitor can be reset to its initial state. */
|
||||||
|
void reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -124,18 +127,14 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
*/
|
*/
|
||||||
public static class CartesianPointVisitor implements PointVisitor {
|
public static class CartesianPointVisitor implements PointVisitor {
|
||||||
private double minX = Double.POSITIVE_INFINITY;
|
private double minX = Double.POSITIVE_INFINITY;
|
||||||
private double minY = Double.POSITIVE_INFINITY;
|
|
||||||
private double maxX = Double.NEGATIVE_INFINITY;
|
private double maxX = Double.NEGATIVE_INFINITY;
|
||||||
private double maxY = Double.NEGATIVE_INFINITY;
|
private double maxY = Double.NEGATIVE_INFINITY;
|
||||||
|
private double minY = Double.POSITIVE_INFINITY;
|
||||||
|
|
||||||
public double getMinX() {
|
public double getMinX() {
|
||||||
return minX;
|
return minX;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getMinY() {
|
|
||||||
return minY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getMaxX() {
|
public double getMaxX() {
|
||||||
return maxX;
|
return maxX;
|
||||||
}
|
}
|
||||||
|
@ -144,12 +143,16 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
return maxY;
|
return maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getMinY() {
|
||||||
|
return minY;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitPoint(double x, double y) {
|
public void visitPoint(double x, double y) {
|
||||||
minX = Math.min(minX, x);
|
minX = Math.min(minX, x);
|
||||||
minY = Math.min(minY, y);
|
|
||||||
maxX = Math.max(maxX, x);
|
maxX = Math.max(maxX, x);
|
||||||
maxY = Math.max(maxY, y);
|
maxY = Math.max(maxY, y);
|
||||||
|
minY = Math.min(minY, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -160,9 +163,9 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.minX = Math.min(this.minX, minX);
|
this.minX = Math.min(this.minX, minX);
|
||||||
this.minY = Math.min(this.minY, minY);
|
|
||||||
this.maxX = Math.max(this.maxX, maxX);
|
this.maxX = Math.max(this.maxX, maxX);
|
||||||
this.maxY = Math.max(this.maxY, maxY);
|
this.maxY = Math.max(this.maxY, maxY);
|
||||||
|
this.minY = Math.min(this.minY, minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,6 +177,14 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
public Rectangle getResult() {
|
public Rectangle getResult() {
|
||||||
return new Rectangle(minX, maxX, maxY, minY);
|
return new Rectangle(minX, maxX, maxY, minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
minX = Double.POSITIVE_INFINITY;
|
||||||
|
maxX = Double.NEGATIVE_INFINITY;
|
||||||
|
maxY = Double.NEGATIVE_INFINITY;
|
||||||
|
minY = Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,12 +197,12 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static class GeoPointVisitor implements PointVisitor {
|
public static class GeoPointVisitor implements PointVisitor {
|
||||||
protected double minY = Double.POSITIVE_INFINITY;
|
protected double top = Double.NEGATIVE_INFINITY;
|
||||||
protected double maxY = Double.NEGATIVE_INFINITY;
|
protected double bottom = Double.POSITIVE_INFINITY;
|
||||||
protected double minNegX = Double.POSITIVE_INFINITY;
|
protected double negLeft = Double.POSITIVE_INFINITY;
|
||||||
protected double maxNegX = Double.NEGATIVE_INFINITY;
|
protected double negRight = Double.NEGATIVE_INFINITY;
|
||||||
protected double minPosX = Double.POSITIVE_INFINITY;
|
protected double posLeft = Double.POSITIVE_INFINITY;
|
||||||
protected double maxPosX = Double.NEGATIVE_INFINITY;
|
protected double posRight = Double.NEGATIVE_INFINITY;
|
||||||
|
|
||||||
private final WrapLongitude wrapLongitude;
|
private final WrapLongitude wrapLongitude;
|
||||||
|
|
||||||
|
@ -199,69 +210,104 @@ public class SpatialEnvelopeVisitor implements GeometryVisitor<Boolean, RuntimeE
|
||||||
this.wrapLongitude = wrapLongitude;
|
this.wrapLongitude = wrapLongitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getTop() {
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBottom() {
|
||||||
|
return bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getNegLeft() {
|
||||||
|
return negLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getNegRight() {
|
||||||
|
return negRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPosLeft() {
|
||||||
|
return posLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPosRight() {
|
||||||
|
return posRight;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitPoint(double x, double y) {
|
public void visitPoint(double x, double y) {
|
||||||
minY = Math.min(minY, y);
|
bottom = Math.min(bottom, y);
|
||||||
maxY = Math.max(maxY, y);
|
top = Math.max(top, y);
|
||||||
visitLongitude(x);
|
visitLongitude(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitRectangle(double minX, double maxX, double maxY, double minY) {
|
public void visitRectangle(double minX, double maxX, double maxY, double minY) {
|
||||||
this.minY = Math.min(this.minY, minY);
|
// TODO: Fix bug with rectangle crossing the dateline (see Extent.addRectangle for correct behaviour)
|
||||||
this.maxY = Math.max(this.maxY, maxY);
|
this.bottom = Math.min(this.bottom, minY);
|
||||||
|
this.top = Math.max(this.top, maxY);
|
||||||
visitLongitude(minX);
|
visitLongitude(minX);
|
||||||
visitLongitude(maxX);
|
visitLongitude(maxX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void visitLongitude(double x) {
|
private void visitLongitude(double x) {
|
||||||
if (x >= 0) {
|
if (x >= 0) {
|
||||||
minPosX = Math.min(minPosX, x);
|
posLeft = Math.min(posLeft, x);
|
||||||
maxPosX = Math.max(maxPosX, x);
|
posRight = Math.max(posRight, x);
|
||||||
} else {
|
} else {
|
||||||
minNegX = Math.min(minNegX, x);
|
negLeft = Math.min(negLeft, x);
|
||||||
maxNegX = Math.max(maxNegX, x);
|
negRight = Math.max(negRight, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return minY != Double.POSITIVE_INFINITY;
|
return bottom != Double.POSITIVE_INFINITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Rectangle getResult() {
|
public Rectangle getResult() {
|
||||||
return getResult(minNegX, minPosX, maxNegX, maxPosX, maxY, minY, wrapLongitude);
|
return getResult(top, bottom, negLeft, negRight, posLeft, posRight, wrapLongitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Rectangle getResult(
|
@Override
|
||||||
double minNegX,
|
public void reset() {
|
||||||
double minPosX,
|
bottom = Double.POSITIVE_INFINITY;
|
||||||
double maxNegX,
|
top = Double.NEGATIVE_INFINITY;
|
||||||
double maxPosX,
|
negLeft = Double.POSITIVE_INFINITY;
|
||||||
double maxY,
|
negRight = Double.NEGATIVE_INFINITY;
|
||||||
double minY,
|
posLeft = Double.POSITIVE_INFINITY;
|
||||||
|
posRight = Double.NEGATIVE_INFINITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Rectangle getResult(
|
||||||
|
double top,
|
||||||
|
double bottom,
|
||||||
|
double negLeft,
|
||||||
|
double negRight,
|
||||||
|
double posLeft,
|
||||||
|
double posRight,
|
||||||
WrapLongitude wrapLongitude
|
WrapLongitude wrapLongitude
|
||||||
) {
|
) {
|
||||||
assert Double.isFinite(maxY);
|
assert Double.isFinite(top);
|
||||||
if (Double.isInfinite(minPosX)) {
|
if (posRight == Double.NEGATIVE_INFINITY) {
|
||||||
return new Rectangle(minNegX, maxNegX, maxY, minY);
|
return new Rectangle(negLeft, negRight, top, bottom);
|
||||||
} else if (Double.isInfinite(minNegX)) {
|
} else if (negLeft == Double.POSITIVE_INFINITY) {
|
||||||
return new Rectangle(minPosX, maxPosX, maxY, minY);
|
return new Rectangle(posLeft, posRight, top, bottom);
|
||||||
} else {
|
} else {
|
||||||
return switch (wrapLongitude) {
|
return switch (wrapLongitude) {
|
||||||
case NO_WRAP -> new Rectangle(minNegX, maxPosX, maxY, minY);
|
case NO_WRAP -> new Rectangle(negLeft, posRight, top, bottom);
|
||||||
case WRAP -> maybeWrap(minNegX, minPosX, maxNegX, maxPosX, maxY, minY);
|
case WRAP -> maybeWrap(top, bottom, negLeft, negRight, posLeft, posRight);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rectangle maybeWrap(double minNegX, double minPosX, double maxNegX, double maxPosX, double maxY, double minY) {
|
private static Rectangle maybeWrap(double top, double bottom, double negLeft, double negRight, double posLeft, double posRight) {
|
||||||
double unwrappedWidth = maxPosX - minNegX;
|
double unwrappedWidth = posRight - negLeft;
|
||||||
double wrappedWidth = 360 + maxNegX - minPosX;
|
double wrappedWidth = 360 + negRight - posLeft;
|
||||||
return unwrappedWidth <= wrappedWidth
|
return unwrappedWidth <= wrappedWidth
|
||||||
? new Rectangle(minNegX, maxPosX, maxY, minY)
|
? new Rectangle(negLeft, posRight, top, bottom)
|
||||||
: new Rectangle(minPosX, maxNegX, maxY, minY);
|
: new Rectangle(posLeft, negRight, top, bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.elasticsearch.index.IndexVersion;
|
||||||
import org.elasticsearch.index.IndexVersions;
|
import org.elasticsearch.index.IndexVersions;
|
||||||
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
import org.elasticsearch.index.analysis.NamedAnalyzer;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
|
import org.elasticsearch.index.mapper.BlockLoader;
|
||||||
import org.elasticsearch.index.mapper.DocumentParserContext;
|
import org.elasticsearch.index.mapper.DocumentParserContext;
|
||||||
import org.elasticsearch.index.mapper.DocumentParsingException;
|
import org.elasticsearch.index.mapper.DocumentParsingException;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
|
@ -46,7 +47,6 @@ import org.elasticsearch.legacygeo.XShapeCollection;
|
||||||
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
|
import org.elasticsearch.legacygeo.builders.ShapeBuilder;
|
||||||
import org.elasticsearch.legacygeo.parsers.ShapeParser;
|
import org.elasticsearch.legacygeo.parsers.ShapeParser;
|
||||||
import org.elasticsearch.legacygeo.query.LegacyGeoShapeQueryProcessor;
|
import org.elasticsearch.legacygeo.query.LegacyGeoShapeQueryProcessor;
|
||||||
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
|
||||||
import org.elasticsearch.xcontent.XContentBuilder;
|
import org.elasticsearch.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.xcontent.XContentParser;
|
import org.elasticsearch.xcontent.XContentParser;
|
||||||
import org.locationtech.spatial4j.shape.Point;
|
import org.locationtech.spatial4j.shape.Point;
|
||||||
|
@ -84,6 +84,7 @@ import java.util.stream.Collectors;
|
||||||
* "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
|
* "field" : "POLYGON ((100.0 0.0, 101.0 0.0, 101.0 1.0, 100.0 1.0, 100.0 0.0))
|
||||||
*
|
*
|
||||||
* @deprecated use the field mapper in the spatial module
|
* @deprecated use the field mapper in the spatial module
|
||||||
|
* TODO: Remove this class once we no longer need to supported reading 7.x indices that might have this field type
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<ShapeBuilder<?, ?, ?>> {
|
public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<ShapeBuilder<?, ?, ?>> {
|
||||||
|
@ -533,14 +534,9 @@ public class LegacyGeoShapeFieldMapper extends AbstractShapeGeometryFieldMapper<
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isBoundsExtractionSupported() {
|
public BlockLoader blockLoader(BlockLoaderContext blContext) {
|
||||||
// Extracting bounds for geo shapes is not implemented yet.
|
// Legacy geo-shapes do not support doc-values, we can only lead from source in ES|QL
|
||||||
return false;
|
return blockLoaderFromSource(blContext);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected CoordinateEncoder coordinateEncoder() {
|
|
||||||
return CoordinateEncoder.GEO;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,12 +180,6 @@ public abstract class AbstractGeometryFieldMapper<T> extends FieldMapper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockLoader blockLoader(BlockLoaderContext blContext) {
|
|
||||||
// Currently we can only load from source in ESQL
|
|
||||||
return blockLoaderFromSource(blContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BlockLoader blockLoaderFromSource(BlockLoaderContext blContext) {
|
protected BlockLoader blockLoaderFromSource(BlockLoaderContext blContext) {
|
||||||
ValueFetcher fetcher = valueFetcher(blContext.sourcePaths(name()), nullValue, GeometryFormatterFactory.WKB);
|
ValueFetcher fetcher = valueFetcher(blContext.sourcePaths(name()), nullValue, GeometryFormatterFactory.WKB);
|
||||||
// TODO consider optimization using BlockSourceReader.lookupFromFieldNames(blContext.fieldNames(), name())
|
// TODO consider optimization using BlockSourceReader.lookupFromFieldNames(blContext.fieldNames(), name())
|
||||||
|
|
|
@ -10,16 +10,12 @@ package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
import org.apache.lucene.index.BinaryDocValues;
|
import org.apache.lucene.index.BinaryDocValues;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.elasticsearch.common.Explicit;
|
import org.elasticsearch.common.Explicit;
|
||||||
import org.elasticsearch.common.geo.Orientation;
|
import org.elasticsearch.common.geo.Orientation;
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
import org.elasticsearch.lucene.spatial.Extent;
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
|
||||||
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
|
||||||
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@ -75,29 +71,27 @@ public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeomet
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object nullValueAsSource(T nullValue) {
|
protected Object nullValueAsSource(T nullValue) {
|
||||||
// we don't support null value fors shapes
|
// we don't support null value for shapes
|
||||||
return nullValue;
|
return nullValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected static class BoundsBlockLoader extends BlockDocValuesReader.DocValuesBlockLoader {
|
||||||
public BlockLoader blockLoader(BlockLoaderContext blContext) {
|
private final String fieldName;
|
||||||
return blContext.fieldExtractPreference() == FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS && isBoundsExtractionSupported()
|
|
||||||
? new BoundsBlockLoader(name(), coordinateEncoder())
|
protected BoundsBlockLoader(String fieldName) {
|
||||||
: blockLoaderFromSource(blContext);
|
this.fieldName = fieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean isBoundsExtractionSupported();
|
protected void writeExtent(BlockLoader.IntBuilder builder, Extent extent) {
|
||||||
|
// We store the 6 values as a single multi-valued field, in the same order as the fields in the Extent class
|
||||||
protected abstract CoordinateEncoder coordinateEncoder();
|
builder.beginPositionEntry();
|
||||||
|
builder.appendInt(extent.top);
|
||||||
// Visible for testing
|
builder.appendInt(extent.bottom);
|
||||||
static class BoundsBlockLoader extends BlockDocValuesReader.DocValuesBlockLoader {
|
builder.appendInt(extent.negLeft);
|
||||||
private final String fieldName;
|
builder.appendInt(extent.negRight);
|
||||||
private final CoordinateEncoder encoder;
|
builder.appendInt(extent.posLeft);
|
||||||
|
builder.appendInt(extent.posRight);
|
||||||
BoundsBlockLoader(String fieldName, CoordinateEncoder encoder) {
|
builder.endPositionEntry();
|
||||||
this.fieldName = fieldName;
|
|
||||||
this.encoder = encoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -107,7 +101,7 @@ public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeomet
|
||||||
public BlockLoader.Block read(BlockLoader.BlockFactory factory, BlockLoader.Docs docs) throws IOException {
|
public BlockLoader.Block read(BlockLoader.BlockFactory factory, BlockLoader.Docs docs) throws IOException {
|
||||||
var binaryDocValues = context.reader().getBinaryDocValues(fieldName);
|
var binaryDocValues = context.reader().getBinaryDocValues(fieldName);
|
||||||
var reader = new GeometryDocValueReader();
|
var reader = new GeometryDocValueReader();
|
||||||
try (var builder = factory.bytesRefs(docs.count())) {
|
try (var builder = factory.ints(docs.count())) {
|
||||||
for (int i = 0; i < docs.count(); i++) {
|
for (int i = 0; i < docs.count(); i++) {
|
||||||
read(binaryDocValues, docs.get(i), reader, builder);
|
read(binaryDocValues, docs.get(i), reader, builder);
|
||||||
}
|
}
|
||||||
|
@ -119,27 +113,17 @@ public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeomet
|
||||||
public void read(int docId, BlockLoader.StoredFields storedFields, BlockLoader.Builder builder) throws IOException {
|
public void read(int docId, BlockLoader.StoredFields storedFields, BlockLoader.Builder builder) throws IOException {
|
||||||
var binaryDocValues = context.reader().getBinaryDocValues(fieldName);
|
var binaryDocValues = context.reader().getBinaryDocValues(fieldName);
|
||||||
var reader = new GeometryDocValueReader();
|
var reader = new GeometryDocValueReader();
|
||||||
read(binaryDocValues, docId, reader, (BytesRefBuilder) builder);
|
read(binaryDocValues, docId, reader, (IntBuilder) builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void read(BinaryDocValues binaryDocValues, int doc, GeometryDocValueReader reader, BytesRefBuilder builder)
|
private void read(BinaryDocValues binaryDocValues, int doc, GeometryDocValueReader reader, IntBuilder builder)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (binaryDocValues.advanceExact(doc) == false) {
|
if (binaryDocValues.advanceExact(doc) == false) {
|
||||||
builder.appendNull();
|
builder.appendNull();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
reader.reset(binaryDocValues.binaryValue());
|
reader.reset(binaryDocValues.binaryValue());
|
||||||
var extent = reader.getExtent();
|
writeExtent(builder, reader.getExtent());
|
||||||
// This is rather silly: an extent is already encoded as ints, but we convert it to Rectangle to
|
|
||||||
// preserve its properties as a WKB shape, only to convert it back to ints when we compute the
|
|
||||||
// aggregation. An obvious optimization would be to avoid this back-and-forth conversion.
|
|
||||||
var rectangle = new Rectangle(
|
|
||||||
encoder.decodeX(extent.minX()),
|
|
||||||
encoder.decodeX(extent.maxX()),
|
|
||||||
encoder.decodeY(extent.maxY()),
|
|
||||||
encoder.decodeY(extent.minY())
|
|
||||||
);
|
|
||||||
builder.appendBytesRef(new BytesRef(WellKnownBinary.toWKB(rectangle, ByteOrder.LITTLE_ENDIAN)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -151,7 +135,7 @@ public abstract class AbstractShapeGeometryFieldMapper<T> extends AbstractGeomet
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockLoader.Builder builder(BlockLoader.BlockFactory factory, int expectedCount) {
|
public BlockLoader.Builder builder(BlockLoader.BlockFactory factory, int expectedCount) {
|
||||||
return factory.bytesRefs(expectedCount);
|
return factory.ints(expectedCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
/*
|
|
||||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.index.mapper;
|
|
||||||
|
|
||||||
import org.apache.lucene.document.Document;
|
|
||||||
import org.apache.lucene.index.DirectoryReader;
|
|
||||||
import org.apache.lucene.index.LeafReader;
|
|
||||||
import org.apache.lucene.store.Directory;
|
|
||||||
import org.apache.lucene.tests.index.RandomIndexWriter;
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.elasticsearch.common.geo.Orientation;
|
|
||||||
import org.elasticsearch.core.Strings;
|
|
||||||
import org.elasticsearch.geo.GeometryTestUtils;
|
|
||||||
import org.elasticsearch.geo.ShapeTestUtils;
|
|
||||||
import org.elasticsearch.geometry.Geometry;
|
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
|
||||||
import org.elasticsearch.lucene.spatial.BinaryShapeDocValuesField;
|
|
||||||
import org.elasticsearch.lucene.spatial.CartesianShapeIndexer;
|
|
||||||
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
|
||||||
import org.elasticsearch.test.ESTestCase;
|
|
||||||
import org.elasticsearch.test.hamcrest.RectangleMatcher;
|
|
||||||
import org.elasticsearch.test.hamcrest.WellKnownBinaryBytesRefMatcher;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
public class AbstractShapeGeometryFieldMapperTests extends ESTestCase {
|
|
||||||
public void testCartesianBoundsBlockLoader() throws IOException {
|
|
||||||
testBoundsBlockLoaderAux(
|
|
||||||
CoordinateEncoder.CARTESIAN,
|
|
||||||
() -> ShapeTestUtils.randomGeometryWithoutCircle(0, false),
|
|
||||||
CartesianShapeIndexer::new,
|
|
||||||
SpatialEnvelopeVisitor::visitCartesian
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO when we turn this optimization on for geo, this test should pass.
|
|
||||||
public void ignoreTestGeoBoundsBlockLoader() throws IOException {
|
|
||||||
testBoundsBlockLoaderAux(
|
|
||||||
CoordinateEncoder.GEO,
|
|
||||||
() -> GeometryTestUtils.randomGeometryWithoutCircle(0, false),
|
|
||||||
field -> new GeoShapeIndexer(Orientation.RIGHT, field),
|
|
||||||
g -> SpatialEnvelopeVisitor.visitGeo(g, SpatialEnvelopeVisitor.WrapLongitude.WRAP)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testBoundsBlockLoaderAux(
|
|
||||||
CoordinateEncoder encoder,
|
|
||||||
Supplier<Geometry> generator,
|
|
||||||
Function<String, ShapeIndexer> indexerFactory,
|
|
||||||
Function<Geometry, Optional<Rectangle>> visitor
|
|
||||||
) throws IOException {
|
|
||||||
var geometries = IntStream.range(0, 50).mapToObj(i -> generator.get()).toList();
|
|
||||||
var loader = new AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType.BoundsBlockLoader("field", encoder);
|
|
||||||
try (Directory directory = newDirectory()) {
|
|
||||||
try (var iw = new RandomIndexWriter(random(), directory)) {
|
|
||||||
for (Geometry geometry : geometries) {
|
|
||||||
var shape = new BinaryShapeDocValuesField("field", encoder);
|
|
||||||
shape.add(indexerFactory.apply("field").indexShape(geometry), geometry);
|
|
||||||
var doc = new Document();
|
|
||||||
doc.add(shape);
|
|
||||||
iw.addDocument(doc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var expected = new ArrayList<Rectangle>();
|
|
||||||
var byteRefResults = new ArrayList<BytesRef>();
|
|
||||||
int currentIndex = 0;
|
|
||||||
try (DirectoryReader reader = DirectoryReader.open(directory)) {
|
|
||||||
for (var leaf : reader.leaves()) {
|
|
||||||
LeafReader leafReader = leaf.reader();
|
|
||||||
int numDocs = leafReader.numDocs();
|
|
||||||
// We specifically check just the even indices, to verify the loader can skip documents correctly.
|
|
||||||
int[] array = evenArray(numDocs);
|
|
||||||
for (int i = 0; i < array.length; i += 1) {
|
|
||||||
expected.add(visitor.apply(geometries.get(array[i] + currentIndex)).get());
|
|
||||||
}
|
|
||||||
try (var block = (TestBlock) loader.reader(leaf).read(TestBlock.factory(leafReader.numDocs()), TestBlock.docs(array))) {
|
|
||||||
for (int i = 0; i < block.size(); i++) {
|
|
||||||
byteRefResults.add((BytesRef) block.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentIndex += numDocs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < expected.size(); i++) {
|
|
||||||
Rectangle rectangle = expected.get(i);
|
|
||||||
var geoString = rectangle.toString();
|
|
||||||
assertThat(
|
|
||||||
Strings.format("geometry '%s' wasn't extracted correctly", geoString),
|
|
||||||
byteRefResults.get(i),
|
|
||||||
WellKnownBinaryBytesRefMatcher.encodes(RectangleMatcher.closeToFloat(rectangle, 1e-3, encoder))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] evenArray(int maxIndex) {
|
|
||||||
return IntStream.range(0, maxIndex / 2).map(x -> x * 2).toArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||||
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.index.mapper;
|
||||||
|
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.geo.GeoEncodingUtils;
|
||||||
|
import org.apache.lucene.index.DirectoryReader;
|
||||||
|
import org.apache.lucene.index.LeafReader;
|
||||||
|
import org.apache.lucene.store.Directory;
|
||||||
|
import org.apache.lucene.tests.index.RandomIndexWriter;
|
||||||
|
import org.elasticsearch.common.geo.GeometryNormalizer;
|
||||||
|
import org.elasticsearch.core.Strings;
|
||||||
|
import org.elasticsearch.geo.GeometryTestUtils;
|
||||||
|
import org.elasticsearch.geo.ShapeTestUtils;
|
||||||
|
import org.elasticsearch.geometry.Geometry;
|
||||||
|
import org.elasticsearch.geometry.Rectangle;
|
||||||
|
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
||||||
|
import org.elasticsearch.lucene.spatial.BinaryShapeDocValuesField;
|
||||||
|
import org.elasticsearch.lucene.spatial.CartesianShapeIndexer;
|
||||||
|
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.test.hamcrest.RectangleMatcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
|
||||||
|
import static org.elasticsearch.common.geo.Orientation.RIGHT;
|
||||||
|
|
||||||
|
public class ShapeGeometryFieldMapperTests extends ESTestCase {
|
||||||
|
public void testCartesianBoundsBlockLoader() throws IOException {
|
||||||
|
testBoundsBlockLoader(
|
||||||
|
CoordinateEncoder.CARTESIAN,
|
||||||
|
() -> ShapeTestUtils.randomGeometryWithoutCircle(0, false),
|
||||||
|
CartesianShapeIndexer::new,
|
||||||
|
SpatialEnvelopeVisitor::visitCartesian,
|
||||||
|
ShapeGeometryFieldMapperTests::makeCartesianRectangle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Re-enable this test after fixing the bug in the ShapeEnvelopeVisitor regarding Rectangle crossing the dateline
|
||||||
|
// Currently it is flaky if the geometries include a Rectangle like one defined in the test below
|
||||||
|
public void ignoreTestGeoBoundsBlockLoader() throws IOException {
|
||||||
|
testBoundsBlockLoader(
|
||||||
|
CoordinateEncoder.GEO,
|
||||||
|
() -> normalize(GeometryTestUtils.randomGeometryWithoutCircle(0, false)),
|
||||||
|
field -> new GeoShapeIndexer(RIGHT, field),
|
||||||
|
g -> SpatialEnvelopeVisitor.visitGeo(g, SpatialEnvelopeVisitor.WrapLongitude.WRAP),
|
||||||
|
ShapeGeometryFieldMapperTests::makeGeoRectangle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Re-enable this test after fixing the bug in the SpatialEnvelopeVisitor regarding Rectangle crossing the dateline
|
||||||
|
// See the difference between GeoShapeIndexer.visitRectangle() and SpatialEnvelopeVisitor.GeoPointVisitor.visitRectangle()
|
||||||
|
public void ignoreTestRectangleCrossingDateline() throws IOException {
|
||||||
|
var geometries = new ArrayList<Geometry>();
|
||||||
|
geometries.add(new Rectangle(180, 51.62247094594227, -18.5, -24.902304006345503));
|
||||||
|
testBoundsBlockLoaderAux(
|
||||||
|
CoordinateEncoder.GEO,
|
||||||
|
geometries,
|
||||||
|
field -> new GeoShapeIndexer(RIGHT, field),
|
||||||
|
g -> SpatialEnvelopeVisitor.visitGeo(g, SpatialEnvelopeVisitor.WrapLongitude.WRAP),
|
||||||
|
ShapeGeometryFieldMapperTests::makeGeoRectangle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Geometry normalize(Geometry geometry) {
|
||||||
|
return GeometryNormalizer.needsNormalize(RIGHT, geometry) ? GeometryNormalizer.apply(RIGHT, geometry) : geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testBoundsBlockLoader(
|
||||||
|
CoordinateEncoder encoder,
|
||||||
|
Supplier<Geometry> generator,
|
||||||
|
Function<String, ShapeIndexer> indexerFactory,
|
||||||
|
Function<Geometry, Optional<Rectangle>> visitor,
|
||||||
|
BiFunction<CoordinateEncoder, Object, Rectangle> rectangleMaker
|
||||||
|
) throws IOException {
|
||||||
|
var geometries = IntStream.range(0, 50).mapToObj(i -> generator.get()).toList();
|
||||||
|
testBoundsBlockLoaderAux(encoder, geometries, indexerFactory, visitor, rectangleMaker);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testBoundsBlockLoaderAux(
|
||||||
|
CoordinateEncoder encoder,
|
||||||
|
java.util.List<Geometry> geometries,
|
||||||
|
Function<String, ShapeIndexer> indexerFactory,
|
||||||
|
Function<Geometry, Optional<Rectangle>> visitor,
|
||||||
|
BiFunction<CoordinateEncoder, Object, Rectangle> rectangleMaker
|
||||||
|
) throws IOException {
|
||||||
|
var loader = new AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType.BoundsBlockLoader("field");
|
||||||
|
try (Directory directory = newDirectory()) {
|
||||||
|
try (var iw = new RandomIndexWriter(random(), directory)) {
|
||||||
|
for (Geometry geometry : geometries) {
|
||||||
|
var shape = new BinaryShapeDocValuesField("field", encoder);
|
||||||
|
shape.add(indexerFactory.apply("field").indexShape(geometry), geometry);
|
||||||
|
var doc = new Document();
|
||||||
|
doc.add(shape);
|
||||||
|
iw.addDocument(doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = new ArrayList<Rectangle>();
|
||||||
|
ArrayList<Object> intArrayResults = new ArrayList<>();
|
||||||
|
int currentIndex = 0;
|
||||||
|
try (DirectoryReader reader = DirectoryReader.open(directory)) {
|
||||||
|
for (var leaf : reader.leaves()) {
|
||||||
|
LeafReader leafReader = leaf.reader();
|
||||||
|
int numDocs = leafReader.numDocs();
|
||||||
|
// We specifically check just the even indices, to verify the loader can skip documents correctly.
|
||||||
|
int[] array = evenArray(numDocs);
|
||||||
|
for (int j : array) {
|
||||||
|
expected.add(visitor.apply(geometries.get(j + currentIndex)).get());
|
||||||
|
}
|
||||||
|
try (var block = (TestBlock) loader.reader(leaf).read(TestBlock.factory(leafReader.numDocs()), TestBlock.docs(array))) {
|
||||||
|
for (int i = 0; i < block.size(); i++) {
|
||||||
|
intArrayResults.add(block.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentIndex += numDocs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < expected.size(); i++) {
|
||||||
|
Rectangle rectangle = expected.get(i);
|
||||||
|
var geoString = rectangle.toString();
|
||||||
|
Rectangle result = rectangleMaker.apply(encoder, intArrayResults.get(i));
|
||||||
|
assertThat(
|
||||||
|
Strings.format("geometry[%d] '%s' wasn't extracted correctly", i, geoString),
|
||||||
|
result,
|
||||||
|
RectangleMatcher.closeToFloat(rectangle, 1e-3, encoder)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rectangle makeCartesianRectangle(CoordinateEncoder encoder, Object integers) {
|
||||||
|
if (integers instanceof ArrayList<?> list) {
|
||||||
|
int[] ints = list.stream().mapToInt(x -> (int) x).toArray();
|
||||||
|
if (list.size() == 6) {
|
||||||
|
// Data in order defined by Extent class
|
||||||
|
double top = encoder.decodeY(ints[0]);
|
||||||
|
double bottom = encoder.decodeY(ints[1]);
|
||||||
|
double negLeft = encoder.decodeX(ints[2]);
|
||||||
|
double negRight = encoder.decodeX(ints[3]);
|
||||||
|
double posLeft = encoder.decodeX(ints[4]);
|
||||||
|
double posRight = encoder.decodeX(ints[5]);
|
||||||
|
return new Rectangle(Math.min(negLeft, posLeft), Math.max(negRight, posRight), top, bottom);
|
||||||
|
} else if (list.size() == 4) {
|
||||||
|
// Data in order defined by Rectangle class
|
||||||
|
return new Rectangle(
|
||||||
|
encoder.decodeX(ints[0]),
|
||||||
|
encoder.decodeX(ints[1]),
|
||||||
|
encoder.decodeY(ints[2]),
|
||||||
|
encoder.decodeY(ints[3])
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Expected 4 or 6 integers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Expected an array of integers");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rectangle makeGeoRectangle(CoordinateEncoder encoder, Object integers) {
|
||||||
|
if (integers instanceof ArrayList<?> list) {
|
||||||
|
int[] ints = list.stream().mapToInt(x -> (int) x).toArray();
|
||||||
|
if (list.size() != 6) {
|
||||||
|
throw new IllegalArgumentException("Expected 6 integers");
|
||||||
|
}
|
||||||
|
// Data in order defined by Extent class
|
||||||
|
return asGeoRectangle(ints[0], ints[1], ints[2], ints[3], ints[4], ints[5]);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Expected an array of integers");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rectangle asGeoRectangle(int top, int bottom, int negLeft, int negRight, int posLeft, int posRight) {
|
||||||
|
return SpatialEnvelopeVisitor.GeoPointVisitor.getResult(
|
||||||
|
GeoEncodingUtils.decodeLatitude(top),
|
||||||
|
GeoEncodingUtils.decodeLatitude(bottom),
|
||||||
|
negLeft <= 0 ? decodeLongitude(negLeft) : Double.POSITIVE_INFINITY,
|
||||||
|
negRight <= 0 ? decodeLongitude(negRight) : Double.NEGATIVE_INFINITY,
|
||||||
|
posLeft >= 0 ? decodeLongitude(posLeft) : Double.POSITIVE_INFINITY,
|
||||||
|
posRight >= 0 ? decodeLongitude(posRight) : Double.NEGATIVE_INFINITY,
|
||||||
|
SpatialEnvelopeVisitor.WrapLongitude.WRAP
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int[] evenArray(int maxIndex) {
|
||||||
|
return IntStream.range(0, maxIndex / 2).map(x -> x * 2).toArray();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import java.util.stream.Stream;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
|
|
||||||
|
@ -85,6 +86,7 @@ public class AggregatorImplementer {
|
||||||
private final boolean stateTypeHasSeen;
|
private final boolean stateTypeHasSeen;
|
||||||
private final boolean stateTypeHasFailed;
|
private final boolean stateTypeHasFailed;
|
||||||
private final boolean valuesIsBytesRef;
|
private final boolean valuesIsBytesRef;
|
||||||
|
private final boolean valuesIsArray;
|
||||||
private final List<IntermediateStateDesc> intermediateState;
|
private final List<IntermediateStateDesc> intermediateState;
|
||||||
private final List<Parameter> createParameters;
|
private final List<Parameter> createParameters;
|
||||||
|
|
||||||
|
@ -126,7 +128,8 @@ public class AggregatorImplementer {
|
||||||
elements.getPackageOf(declarationType).toString(),
|
elements.getPackageOf(declarationType).toString(),
|
||||||
(declarationType.getSimpleName() + "AggregatorFunction").replace("AggregatorAggregator", "Aggregator")
|
(declarationType.getSimpleName() + "AggregatorFunction").replace("AggregatorAggregator", "Aggregator")
|
||||||
);
|
);
|
||||||
this.valuesIsBytesRef = BYTES_REF.equals(TypeName.get(combine.getParameters().get(combine.getParameters().size() - 1).asType()));
|
this.valuesIsBytesRef = BYTES_REF.equals(valueTypeName());
|
||||||
|
this.valuesIsArray = TypeKind.ARRAY.equals(valueTypeKind());
|
||||||
intermediateState = Arrays.stream(interStateAnno).map(IntermediateStateDesc::newIntermediateStateDesc).toList();
|
intermediateState = Arrays.stream(interStateAnno).map(IntermediateStateDesc::newIntermediateStateDesc).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,10 +146,11 @@ public class AggregatorImplementer {
|
||||||
if (false == initReturn.isPrimitive()) {
|
if (false == initReturn.isPrimitive()) {
|
||||||
return initReturn;
|
return initReturn;
|
||||||
}
|
}
|
||||||
|
String simpleName = firstUpper(initReturn.toString());
|
||||||
if (warnExceptions.isEmpty()) {
|
if (warnExceptions.isEmpty()) {
|
||||||
return ClassName.get("org.elasticsearch.compute.aggregation", firstUpper(initReturn.toString()) + "State");
|
return ClassName.get("org.elasticsearch.compute.aggregation", simpleName + "State");
|
||||||
}
|
}
|
||||||
return ClassName.get("org.elasticsearch.compute.aggregation", firstUpper(initReturn.toString()) + "FallibleState");
|
return ClassName.get("org.elasticsearch.compute.aggregation", simpleName + "FallibleState");
|
||||||
}
|
}
|
||||||
|
|
||||||
static String valueType(ExecutableElement init, ExecutableElement combine) {
|
static String valueType(ExecutableElement init, ExecutableElement combine) {
|
||||||
|
@ -177,7 +181,7 @@ public class AggregatorImplementer {
|
||||||
case "double" -> DOUBLE_BLOCK;
|
case "double" -> DOUBLE_BLOCK;
|
||||||
case "float" -> FLOAT_BLOCK;
|
case "float" -> FLOAT_BLOCK;
|
||||||
case "long" -> LONG_BLOCK;
|
case "long" -> LONG_BLOCK;
|
||||||
case "int" -> INT_BLOCK;
|
case "int", "int[]" -> INT_BLOCK;
|
||||||
case "org.apache.lucene.util.BytesRef" -> BYTES_REF_BLOCK;
|
case "org.apache.lucene.util.BytesRef" -> BYTES_REF_BLOCK;
|
||||||
default -> throw new IllegalArgumentException("unknown block type for " + valueType(init, combine));
|
default -> throw new IllegalArgumentException("unknown block type for " + valueType(init, combine));
|
||||||
};
|
};
|
||||||
|
@ -189,7 +193,7 @@ public class AggregatorImplementer {
|
||||||
case "double" -> DOUBLE_VECTOR;
|
case "double" -> DOUBLE_VECTOR;
|
||||||
case "float" -> FLOAT_VECTOR;
|
case "float" -> FLOAT_VECTOR;
|
||||||
case "long" -> LONG_VECTOR;
|
case "long" -> LONG_VECTOR;
|
||||||
case "int" -> INT_VECTOR;
|
case "int", "int[]" -> INT_VECTOR;
|
||||||
case "org.apache.lucene.util.BytesRef" -> BYTES_REF_VECTOR;
|
case "org.apache.lucene.util.BytesRef" -> BYTES_REF_VECTOR;
|
||||||
default -> throw new IllegalArgumentException("unknown vector type for " + valueType(init, combine));
|
default -> throw new IllegalArgumentException("unknown vector type for " + valueType(init, combine));
|
||||||
};
|
};
|
||||||
|
@ -390,6 +394,10 @@ public class AggregatorImplementer {
|
||||||
if (masked) {
|
if (masked) {
|
||||||
builder.addParameter(BOOLEAN_VECTOR, "mask");
|
builder.addParameter(BOOLEAN_VECTOR, "mask");
|
||||||
}
|
}
|
||||||
|
if (valuesIsArray) {
|
||||||
|
builder.addComment("This type does not support vectors because all values are multi-valued");
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
if (stateTypeHasSeen) {
|
if (stateTypeHasSeen) {
|
||||||
builder.addStatement("state.seen(true)");
|
builder.addStatement("state.seen(true)");
|
||||||
|
@ -437,10 +445,19 @@ public class AggregatorImplementer {
|
||||||
}
|
}
|
||||||
builder.addStatement("int start = block.getFirstValueIndex(p)");
|
builder.addStatement("int start = block.getFirstValueIndex(p)");
|
||||||
builder.addStatement("int end = start + block.getValueCount(p)");
|
builder.addStatement("int end = start + block.getValueCount(p)");
|
||||||
|
if (valuesIsArray) {
|
||||||
|
String arrayType = valueTypeString();
|
||||||
|
builder.addStatement("$L[] valuesArray = new $L[end - start]", arrayType, arrayType);
|
||||||
|
builder.beginControlFlow("for (int i = start; i < end; i++)");
|
||||||
|
builder.addStatement("valuesArray[i-start] = $L.get$L(i)", "block", firstUpper(arrayType));
|
||||||
|
builder.endControlFlow();
|
||||||
|
combineRawInputForArray(builder, "valuesArray");
|
||||||
|
} else {
|
||||||
builder.beginControlFlow("for (int i = start; i < end; i++)");
|
builder.beginControlFlow("for (int i = start; i < end; i++)");
|
||||||
combineRawInput(builder, "block");
|
combineRawInput(builder, "block");
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
if (combineValueCount != null) {
|
if (combineValueCount != null) {
|
||||||
builder.addStatement("$T.combineValueCount(state, block.getTotalValueCount())", declarationType);
|
builder.addStatement("$T.combineValueCount(state, block.getTotalValueCount())", declarationType);
|
||||||
|
@ -450,9 +467,7 @@ public class AggregatorImplementer {
|
||||||
|
|
||||||
private void combineRawInput(MethodSpec.Builder builder, String blockVariable) {
|
private void combineRawInput(MethodSpec.Builder builder, String blockVariable) {
|
||||||
TypeName returnType = TypeName.get(combine.getReturnType());
|
TypeName returnType = TypeName.get(combine.getReturnType());
|
||||||
if (warnExceptions.isEmpty() == false) {
|
warningsBlock(builder, () -> {
|
||||||
builder.beginControlFlow("try");
|
|
||||||
}
|
|
||||||
if (valuesIsBytesRef) {
|
if (valuesIsBytesRef) {
|
||||||
combineRawInputForBytesRef(builder, blockVariable);
|
combineRawInputForBytesRef(builder, blockVariable);
|
||||||
} else if (returnType.isPrimitive()) {
|
} else if (returnType.isPrimitive()) {
|
||||||
|
@ -462,14 +477,7 @@ public class AggregatorImplementer {
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("combine must return void or a primitive");
|
throw new IllegalArgumentException("combine must return void or a primitive");
|
||||||
}
|
}
|
||||||
if (warnExceptions.isEmpty() == false) {
|
});
|
||||||
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
|
||||||
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
|
||||||
builder.addStatement("warnings.registerException(e)");
|
|
||||||
builder.addStatement("state.failed(true)");
|
|
||||||
builder.addStatement("return");
|
|
||||||
builder.endControlFlow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void combineRawInputForPrimitive(TypeName returnType, MethodSpec.Builder builder, String blockVariable) {
|
private void combineRawInputForPrimitive(TypeName returnType, MethodSpec.Builder builder, String blockVariable) {
|
||||||
|
@ -483,6 +491,10 @@ public class AggregatorImplementer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void combineRawInputForArray(MethodSpec.Builder builder, String arrayVariable) {
|
||||||
|
warningsBlock(builder, () -> builder.addStatement("$T.combine(state, $L)", declarationType, arrayVariable));
|
||||||
|
}
|
||||||
|
|
||||||
private void combineRawInputForVoid(MethodSpec.Builder builder, String blockVariable) {
|
private void combineRawInputForVoid(MethodSpec.Builder builder, String blockVariable) {
|
||||||
builder.addStatement(
|
builder.addStatement(
|
||||||
"$T.combine(state, $L.get$L(i))",
|
"$T.combine(state, $L.get$L(i))",
|
||||||
|
@ -497,6 +509,21 @@ public class AggregatorImplementer {
|
||||||
builder.addStatement("$T.combine(state, $L.getBytesRef(i, scratch))", declarationType, blockVariable);
|
builder.addStatement("$T.combine(state, $L.getBytesRef(i, scratch))", declarationType, blockVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void warningsBlock(MethodSpec.Builder builder, Runnable block) {
|
||||||
|
if (warnExceptions.isEmpty() == false) {
|
||||||
|
builder.beginControlFlow("try");
|
||||||
|
}
|
||||||
|
block.run();
|
||||||
|
if (warnExceptions.isEmpty() == false) {
|
||||||
|
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
||||||
|
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
||||||
|
builder.addStatement("warnings.registerException(e)");
|
||||||
|
builder.addStatement("state.failed(true)");
|
||||||
|
builder.addStatement("return");
|
||||||
|
builder.endControlFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MethodSpec addIntermediateInput() {
|
private MethodSpec addIntermediateInput() {
|
||||||
MethodSpec.Builder builder = MethodSpec.methodBuilder("addIntermediateInput");
|
MethodSpec.Builder builder = MethodSpec.methodBuilder("addIntermediateInput");
|
||||||
builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(PAGE, "page");
|
builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(PAGE, "page");
|
||||||
|
@ -529,20 +556,12 @@ public class AggregatorImplementer {
|
||||||
builder.nextControlFlow("else if (seen.getBoolean(0))");
|
builder.nextControlFlow("else if (seen.getBoolean(0))");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warnExceptions.isEmpty() == false) {
|
warningsBlock(builder, () -> {
|
||||||
builder.beginControlFlow("try");
|
|
||||||
}
|
|
||||||
var state = intermediateState.get(0);
|
var state = intermediateState.get(0);
|
||||||
var s = "state.$L($T.combine(state.$L(), " + state.name() + "." + vectorAccessorName(state.elementType()) + "(0)))";
|
var s = "state.$L($T.combine(state.$L(), " + state.name() + "." + vectorAccessorName(state.elementType()) + "(0)))";
|
||||||
builder.addStatement(s, primitiveStateMethod(), declarationType, primitiveStateMethod());
|
builder.addStatement(s, primitiveStateMethod(), declarationType, primitiveStateMethod());
|
||||||
builder.addStatement("state.seen(true)");
|
builder.addStatement("state.seen(true)");
|
||||||
if (warnExceptions.isEmpty() == false) {
|
});
|
||||||
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
|
||||||
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
|
||||||
builder.addStatement("warnings.registerException(e)");
|
|
||||||
builder.addStatement("state.failed(true)");
|
|
||||||
builder.endControlFlow();
|
|
||||||
}
|
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Don't know how to combine intermediate input. Define combineIntermediate");
|
throw new IllegalArgumentException("Don't know how to combine intermediate input. Define combineIntermediate");
|
||||||
|
@ -693,4 +712,21 @@ public class AggregatorImplementer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TypeMirror valueTypeMirror() {
|
||||||
|
return combine.getParameters().get(combine.getParameters().size() - 1).asType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeName valueTypeName() {
|
||||||
|
return TypeName.get(valueTypeMirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeKind valueTypeKind() {
|
||||||
|
return valueTypeMirror().getKind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String valueTypeString() {
|
||||||
|
String valueTypeString = TypeName.get(valueTypeMirror()).toString();
|
||||||
|
return valuesIsArray ? valueTypeString.substring(0, valueTypeString.length() - 2) : valueTypeString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -28,10 +27,12 @@ import java.util.stream.Collectors;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.type.TypeKind;
|
||||||
import javax.lang.model.type.TypeMirror;
|
import javax.lang.model.type.TypeMirror;
|
||||||
import javax.lang.model.util.Elements;
|
import javax.lang.model.util.Elements;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
|
import static org.elasticsearch.compute.gen.AggregatorImplementer.firstUpper;
|
||||||
import static org.elasticsearch.compute.gen.AggregatorImplementer.valueBlockType;
|
import static org.elasticsearch.compute.gen.AggregatorImplementer.valueBlockType;
|
||||||
import static org.elasticsearch.compute.gen.AggregatorImplementer.valueVectorType;
|
import static org.elasticsearch.compute.gen.AggregatorImplementer.valueVectorType;
|
||||||
import static org.elasticsearch.compute.gen.Methods.findMethod;
|
import static org.elasticsearch.compute.gen.Methods.findMethod;
|
||||||
|
@ -74,6 +75,7 @@ public class GroupingAggregatorImplementer {
|
||||||
private final ExecutableElement combineIntermediate;
|
private final ExecutableElement combineIntermediate;
|
||||||
private final TypeName stateType;
|
private final TypeName stateType;
|
||||||
private final boolean valuesIsBytesRef;
|
private final boolean valuesIsBytesRef;
|
||||||
|
private final boolean valuesIsArray;
|
||||||
private final List<Parameter> createParameters;
|
private final List<Parameter> createParameters;
|
||||||
private final ClassName implementation;
|
private final ClassName implementation;
|
||||||
private final List<AggregatorImplementer.IntermediateStateDesc> intermediateState;
|
private final List<AggregatorImplementer.IntermediateStateDesc> intermediateState;
|
||||||
|
@ -102,7 +104,8 @@ public class GroupingAggregatorImplementer {
|
||||||
this.combineStates = findMethod(declarationType, "combineStates");
|
this.combineStates = findMethod(declarationType, "combineStates");
|
||||||
this.combineIntermediate = findMethod(declarationType, "combineIntermediate");
|
this.combineIntermediate = findMethod(declarationType, "combineIntermediate");
|
||||||
this.evaluateFinal = findMethod(declarationType, "evaluateFinal");
|
this.evaluateFinal = findMethod(declarationType, "evaluateFinal");
|
||||||
this.valuesIsBytesRef = BYTES_REF.equals(TypeName.get(combine.getParameters().get(combine.getParameters().size() - 1).asType()));
|
this.valuesIsBytesRef = BYTES_REF.equals(valueTypeName());
|
||||||
|
this.valuesIsArray = TypeKind.ARRAY.equals(valueTypeKind());
|
||||||
this.createParameters = init.getParameters()
|
this.createParameters = init.getParameters()
|
||||||
.stream()
|
.stream()
|
||||||
.map(Parameter::from)
|
.map(Parameter::from)
|
||||||
|
@ -133,12 +136,11 @@ public class GroupingAggregatorImplementer {
|
||||||
if (false == initReturn.isPrimitive()) {
|
if (false == initReturn.isPrimitive()) {
|
||||||
return initReturn;
|
return initReturn;
|
||||||
}
|
}
|
||||||
String head = initReturn.toString().substring(0, 1).toUpperCase(Locale.ROOT);
|
String simpleName = firstUpper(initReturn.toString());
|
||||||
String tail = initReturn.toString().substring(1);
|
|
||||||
if (warnExceptions.isEmpty()) {
|
if (warnExceptions.isEmpty()) {
|
||||||
return ClassName.get("org.elasticsearch.compute.aggregation", head + tail + "ArrayState");
|
return ClassName.get("org.elasticsearch.compute.aggregation", simpleName + "ArrayState");
|
||||||
}
|
}
|
||||||
return ClassName.get("org.elasticsearch.compute.aggregation", head + tail + "FallibleArrayState");
|
return ClassName.get("org.elasticsearch.compute.aggregation", simpleName + "FallibleArrayState");
|
||||||
}
|
}
|
||||||
|
|
||||||
public JavaFile sourceFile() {
|
public JavaFile sourceFile() {
|
||||||
|
@ -364,6 +366,10 @@ public class GroupingAggregatorImplementer {
|
||||||
// Add bytes_ref scratch var that will be used for bytes_ref blocks/vectors
|
// Add bytes_ref scratch var that will be used for bytes_ref blocks/vectors
|
||||||
builder.addStatement("$T scratch = new $T()", BYTES_REF, BYTES_REF);
|
builder.addStatement("$T scratch = new $T()", BYTES_REF, BYTES_REF);
|
||||||
}
|
}
|
||||||
|
if (valuesIsArray && valuesIsBlock == false) {
|
||||||
|
builder.addComment("This type does not support vectors because all values are multi-valued");
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
builder.beginControlFlow("for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++)");
|
builder.beginControlFlow("for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++)");
|
||||||
{
|
{
|
||||||
|
@ -391,9 +397,18 @@ public class GroupingAggregatorImplementer {
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
builder.addStatement("int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset)");
|
builder.addStatement("int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset)");
|
||||||
builder.addStatement("int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset)");
|
builder.addStatement("int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset)");
|
||||||
|
if (valuesIsArray) {
|
||||||
|
String arrayType = valueTypeString();
|
||||||
|
builder.addStatement("$L[] valuesArray = new $L[valuesEnd - valuesStart]", arrayType, arrayType);
|
||||||
|
builder.beginControlFlow("for (int v = valuesStart; v < valuesEnd; v++)");
|
||||||
|
builder.addStatement("valuesArray[v-valuesStart] = $L.get$L(v)", "values", firstUpper(arrayType));
|
||||||
|
builder.endControlFlow();
|
||||||
|
combineRawInputForArray(builder, "valuesArray");
|
||||||
|
} else {
|
||||||
builder.beginControlFlow("for (int v = valuesStart; v < valuesEnd; v++)");
|
builder.beginControlFlow("for (int v = valuesStart; v < valuesEnd; v++)");
|
||||||
combineRawInput(builder, "values", "v");
|
combineRawInput(builder, "values", "v");
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
combineRawInput(builder, "values", "groupPosition + positionOffset");
|
combineRawInput(builder, "values", "groupPosition + positionOffset");
|
||||||
}
|
}
|
||||||
|
@ -407,70 +422,52 @@ public class GroupingAggregatorImplementer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void combineRawInput(MethodSpec.Builder builder, String blockVariable, String offsetVariable) {
|
private void combineRawInput(MethodSpec.Builder builder, String blockVariable, String offsetVariable) {
|
||||||
TypeName valueType = TypeName.get(combine.getParameters().get(combine.getParameters().size() - 1).asType());
|
TypeName valueType = valueTypeName();
|
||||||
String secondParameterGetter = "get"
|
|
||||||
+ valueType.toString().substring(0, 1).toUpperCase(Locale.ROOT)
|
|
||||||
+ valueType.toString().substring(1);
|
|
||||||
TypeName returnType = TypeName.get(combine.getReturnType());
|
TypeName returnType = TypeName.get(combine.getReturnType());
|
||||||
|
|
||||||
if (warnExceptions.isEmpty() == false) {
|
warningsBlock(builder, () -> {
|
||||||
builder.beginControlFlow("try");
|
|
||||||
}
|
|
||||||
if (valuesIsBytesRef) {
|
if (valuesIsBytesRef) {
|
||||||
combineRawInputForBytesRef(builder, blockVariable, offsetVariable);
|
combineRawInputForBytesRef(builder, blockVariable, offsetVariable);
|
||||||
} else if (includeTimestampVector) {
|
} else if (includeTimestampVector) {
|
||||||
combineRawInputWithTimestamp(builder, offsetVariable);
|
combineRawInputWithTimestamp(builder, offsetVariable);
|
||||||
} else if (valueType.isPrimitive() == false) {
|
} else if (valueType.isPrimitive() == false) {
|
||||||
throw new IllegalArgumentException("second parameter to combine must be a primitive");
|
throw new IllegalArgumentException("second parameter to combine must be a primitive, array or BytesRef: " + valueType);
|
||||||
} else if (returnType.isPrimitive()) {
|
} else if (returnType.isPrimitive()) {
|
||||||
combineRawInputForPrimitive(builder, secondParameterGetter, blockVariable, offsetVariable);
|
combineRawInputForPrimitive(builder, blockVariable, offsetVariable);
|
||||||
} else if (returnType == TypeName.VOID) {
|
} else if (returnType == TypeName.VOID) {
|
||||||
combineRawInputForVoid(builder, secondParameterGetter, blockVariable, offsetVariable);
|
combineRawInputForVoid(builder, blockVariable, offsetVariable);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("combine must return void or a primitive");
|
throw new IllegalArgumentException("combine must return void or a primitive");
|
||||||
}
|
}
|
||||||
if (warnExceptions.isEmpty() == false) {
|
});
|
||||||
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
|
||||||
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
|
||||||
builder.addStatement("warnings.registerException(e)");
|
|
||||||
builder.addStatement("state.setFailed(groupId)");
|
|
||||||
builder.endControlFlow();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void combineRawInputForPrimitive(
|
private void combineRawInputForPrimitive(MethodSpec.Builder builder, String blockVariable, String offsetVariable) {
|
||||||
MethodSpec.Builder builder,
|
|
||||||
String secondParameterGetter,
|
|
||||||
String blockVariable,
|
|
||||||
String offsetVariable
|
|
||||||
) {
|
|
||||||
builder.addStatement(
|
builder.addStatement(
|
||||||
"state.set(groupId, $T.combine(state.getOrDefault(groupId), $L.$L($L)))",
|
"state.set(groupId, $T.combine(state.getOrDefault(groupId), $L.get$L($L)))",
|
||||||
declarationType,
|
declarationType,
|
||||||
blockVariable,
|
blockVariable,
|
||||||
secondParameterGetter,
|
firstUpper(valueTypeName().toString()),
|
||||||
offsetVariable
|
offsetVariable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void combineRawInputForVoid(
|
private void combineRawInputForArray(MethodSpec.Builder builder, String arrayVariable) {
|
||||||
MethodSpec.Builder builder,
|
warningsBlock(builder, () -> builder.addStatement("$T.combine(state, groupId, $L)", declarationType, arrayVariable));
|
||||||
String secondParameterGetter,
|
}
|
||||||
String blockVariable,
|
|
||||||
String offsetVariable
|
private void combineRawInputForVoid(MethodSpec.Builder builder, String blockVariable, String offsetVariable) {
|
||||||
) {
|
|
||||||
builder.addStatement(
|
builder.addStatement(
|
||||||
"$T.combine(state, groupId, $L.$L($L))",
|
"$T.combine(state, groupId, $L.get$L($L))",
|
||||||
declarationType,
|
declarationType,
|
||||||
blockVariable,
|
blockVariable,
|
||||||
secondParameterGetter,
|
firstUpper(valueTypeName().toString()),
|
||||||
offsetVariable
|
offsetVariable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void combineRawInputWithTimestamp(MethodSpec.Builder builder, String offsetVariable) {
|
private void combineRawInputWithTimestamp(MethodSpec.Builder builder, String offsetVariable) {
|
||||||
TypeName valueType = TypeName.get(combine.getParameters().get(combine.getParameters().size() - 1).asType());
|
String blockType = firstUpper(valueTypeName().toString());
|
||||||
String blockType = valueType.toString().substring(0, 1).toUpperCase(Locale.ROOT) + valueType.toString().substring(1);
|
|
||||||
if (offsetVariable.contains(" + ")) {
|
if (offsetVariable.contains(" + ")) {
|
||||||
builder.addStatement("var valuePosition = $L", offsetVariable);
|
builder.addStatement("var valuePosition = $L", offsetVariable);
|
||||||
offsetVariable = "valuePosition";
|
offsetVariable = "valuePosition";
|
||||||
|
@ -489,6 +486,20 @@ public class GroupingAggregatorImplementer {
|
||||||
builder.addStatement("$T.combine(state, groupId, $L.getBytesRef($L, scratch))", declarationType, blockVariable, offsetVariable);
|
builder.addStatement("$T.combine(state, groupId, $L.getBytesRef($L, scratch))", declarationType, blockVariable, offsetVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void warningsBlock(MethodSpec.Builder builder, Runnable block) {
|
||||||
|
if (warnExceptions.isEmpty() == false) {
|
||||||
|
builder.beginControlFlow("try");
|
||||||
|
}
|
||||||
|
block.run();
|
||||||
|
if (warnExceptions.isEmpty() == false) {
|
||||||
|
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
||||||
|
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
||||||
|
builder.addStatement("warnings.registerException(e)");
|
||||||
|
builder.addStatement("state.setFailed(groupId)");
|
||||||
|
builder.endControlFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private MethodSpec selectedMayContainUnseenGroups() {
|
private MethodSpec selectedMayContainUnseenGroups() {
|
||||||
MethodSpec.Builder builder = MethodSpec.methodBuilder("selectedMayContainUnseenGroups");
|
MethodSpec.Builder builder = MethodSpec.methodBuilder("selectedMayContainUnseenGroups");
|
||||||
builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC);
|
builder.addAnnotation(Override.class).addModifiers(Modifier.PUBLIC);
|
||||||
|
@ -544,9 +555,7 @@ public class GroupingAggregatorImplementer {
|
||||||
builder.nextControlFlow("else if (seen.getBoolean(groupPosition + positionOffset))");
|
builder.nextControlFlow("else if (seen.getBoolean(groupPosition + positionOffset))");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warnExceptions.isEmpty() == false) {
|
warningsBlock(builder, () -> {
|
||||||
builder.beginControlFlow("try");
|
|
||||||
}
|
|
||||||
var name = intermediateState.get(0).name();
|
var name = intermediateState.get(0).name();
|
||||||
var vectorAccessor = vectorAccessorName(intermediateState.get(0).elementType());
|
var vectorAccessor = vectorAccessorName(intermediateState.get(0).elementType());
|
||||||
builder.addStatement(
|
builder.addStatement(
|
||||||
|
@ -555,13 +564,7 @@ public class GroupingAggregatorImplementer {
|
||||||
name,
|
name,
|
||||||
vectorAccessor
|
vectorAccessor
|
||||||
);
|
);
|
||||||
if (warnExceptions.isEmpty() == false) {
|
});
|
||||||
String catchPattern = "catch (" + warnExceptions.stream().map(m -> "$T").collect(Collectors.joining(" | ")) + " e)";
|
|
||||||
builder.nextControlFlow(catchPattern, warnExceptions.stream().map(TypeName::get).toArray());
|
|
||||||
builder.addStatement("warnings.registerException(e)");
|
|
||||||
builder.addStatement("state.setFailed(groupId)");
|
|
||||||
builder.endControlFlow();
|
|
||||||
}
|
|
||||||
builder.endControlFlow();
|
builder.endControlFlow();
|
||||||
} else {
|
} else {
|
||||||
builder.addStatement("$T.combineIntermediate(state, groupId, " + intermediateStateRowAccess() + ")", declarationType);
|
builder.addStatement("$T.combineIntermediate(state, groupId, " + intermediateStateRowAccess() + ")", declarationType);
|
||||||
|
@ -657,4 +660,24 @@ public class GroupingAggregatorImplementer {
|
||||||
private boolean hasPrimitiveState() {
|
private boolean hasPrimitiveState() {
|
||||||
return PRIMITIVE_STATE_PATTERN.matcher(stateType.toString()).matches();
|
return PRIMITIVE_STATE_PATTERN.matcher(stateType.toString()).matches();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TypeMirror valueTypeMirror() {
|
||||||
|
return combine.getParameters().get(combine.getParameters().size() - 1).asType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeName valueTypeName() {
|
||||||
|
return TypeName.get(valueTypeMirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeKind valueTypeKind() {
|
||||||
|
return valueTypeMirror().getKind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String valueTypeString() {
|
||||||
|
String valueTypeString = TypeName.get(valueTypeMirror()).toString();
|
||||||
|
if (valuesIsArray) {
|
||||||
|
valueTypeString = valueTypeString.substring(0, valueTypeString.length() - 2);
|
||||||
|
}
|
||||||
|
return valueTypeString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.lang.StringBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.AggregatorFunction;
|
||||||
|
import org.elasticsearch.compute.aggregation.IntermediateStateDesc;
|
||||||
|
import org.elasticsearch.compute.data.Block;
|
||||||
|
import org.elasticsearch.compute.data.BooleanVector;
|
||||||
|
import org.elasticsearch.compute.data.ElementType;
|
||||||
|
import org.elasticsearch.compute.data.IntBlock;
|
||||||
|
import org.elasticsearch.compute.data.IntVector;
|
||||||
|
import org.elasticsearch.compute.data.Page;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AggregatorFunction} implementation for {@link SpatialExtentCartesianShapeDocValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentCartesianShapeDocValuesAggregatorFunction implements AggregatorFunction {
|
||||||
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
|
new IntermediateStateDesc("minX", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("maxX", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("maxY", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("minY", ElementType.INT) );
|
||||||
|
|
||||||
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
private final SpatialExtentState state;
|
||||||
|
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
public SpatialExtentCartesianShapeDocValuesAggregatorFunction(DriverContext driverContext,
|
||||||
|
List<Integer> channels, SpatialExtentState state) {
|
||||||
|
this.driverContext = driverContext;
|
||||||
|
this.channels = channels;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentCartesianShapeDocValuesAggregatorFunction create(
|
||||||
|
DriverContext driverContext, List<Integer> channels) {
|
||||||
|
return new SpatialExtentCartesianShapeDocValuesAggregatorFunction(driverContext, channels, SpatialExtentCartesianShapeDocValuesAggregator.initSingle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
return INTERMEDIATE_STATE_DESC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intermediateBlockCount() {
|
||||||
|
return INTERMEDIATE_STATE_DESC.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRawInput(Page page, BooleanVector mask) {
|
||||||
|
if (mask.allFalse()) {
|
||||||
|
// Entire page masked away
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mask.allTrue()) {
|
||||||
|
// No masking
|
||||||
|
IntBlock block = page.getBlock(channels.get(0));
|
||||||
|
IntVector vector = block.asVector();
|
||||||
|
if (vector != null) {
|
||||||
|
addRawVector(vector);
|
||||||
|
} else {
|
||||||
|
addRawBlock(block);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Some positions masked away, others kept
|
||||||
|
IntBlock block = page.getBlock(channels.get(0));
|
||||||
|
IntVector vector = block.asVector();
|
||||||
|
if (vector != null) {
|
||||||
|
addRawVector(vector, mask);
|
||||||
|
} else {
|
||||||
|
addRawBlock(block, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawVector(IntVector vector) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawVector(IntVector vector, BooleanVector mask) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawBlock(IntBlock block) {
|
||||||
|
for (int p = 0; p < block.getPositionCount(); p++) {
|
||||||
|
if (block.isNull(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = block.getFirstValueIndex(p);
|
||||||
|
int end = start + block.getValueCount(p);
|
||||||
|
int[] valuesArray = new int[end - start];
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
valuesArray[i-start] = block.getInt(i);
|
||||||
|
}
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combine(state, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawBlock(IntBlock block, BooleanVector mask) {
|
||||||
|
for (int p = 0; p < block.getPositionCount(); p++) {
|
||||||
|
if (mask.getBoolean(p) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (block.isNull(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = block.getFirstValueIndex(p);
|
||||||
|
int end = start + block.getValueCount(p);
|
||||||
|
int[] valuesArray = new int[end - start];
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
valuesArray[i-start] = block.getInt(i);
|
||||||
|
}
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combine(state, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateInput(Page page) {
|
||||||
|
assert channels.size() == intermediateBlockCount();
|
||||||
|
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
||||||
|
Block minXUncast = page.getBlock(channels.get(0));
|
||||||
|
if (minXUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector minX = ((IntBlock) minXUncast).asVector();
|
||||||
|
assert minX.getPositionCount() == 1;
|
||||||
|
Block maxXUncast = page.getBlock(channels.get(1));
|
||||||
|
if (maxXUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector maxX = ((IntBlock) maxXUncast).asVector();
|
||||||
|
assert maxX.getPositionCount() == 1;
|
||||||
|
Block maxYUncast = page.getBlock(channels.get(2));
|
||||||
|
if (maxYUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
||||||
|
assert maxY.getPositionCount() == 1;
|
||||||
|
Block minYUncast = page.getBlock(channels.get(3));
|
||||||
|
if (minYUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector minY = ((IntBlock) minYUncast).asVector();
|
||||||
|
assert minY.getPositionCount() == 1;
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combineIntermediate(state, minX.getInt(0), maxX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
|
state.toIntermediate(blocks, offset, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
|
blocks[offset] = SpatialExtentCartesianShapeDocValuesAggregator.evaluateFinal(state, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName()).append("[");
|
||||||
|
sb.append("channels=").append(channels);
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
state.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentCartesianShapeDocValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentCartesianShapeDocValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
public SpatialExtentCartesianShapeDocValuesAggregatorFunctionSupplier(List<Integer> channels) {
|
||||||
|
this.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpatialExtentCartesianShapeDocValuesAggregatorFunction aggregator(
|
||||||
|
DriverContext driverContext) {
|
||||||
|
return SpatialExtentCartesianShapeDocValuesAggregatorFunction.create(driverContext, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction groupingAggregator(
|
||||||
|
DriverContext driverContext) {
|
||||||
|
return SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction.create(channels, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String describe() {
|
||||||
|
return "spatial_extent_cartesian_shape_doc of valuess";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.lang.StringBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction;
|
||||||
|
import org.elasticsearch.compute.aggregation.IntermediateStateDesc;
|
||||||
|
import org.elasticsearch.compute.aggregation.SeenGroupIds;
|
||||||
|
import org.elasticsearch.compute.data.Block;
|
||||||
|
import org.elasticsearch.compute.data.ElementType;
|
||||||
|
import org.elasticsearch.compute.data.IntBlock;
|
||||||
|
import org.elasticsearch.compute.data.IntVector;
|
||||||
|
import org.elasticsearch.compute.data.Page;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentCartesianShapeDocValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
|
new IntermediateStateDesc("minX", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("maxX", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("maxY", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("minY", ElementType.INT) );
|
||||||
|
|
||||||
|
private final SpatialExtentGroupingState state;
|
||||||
|
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
public SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction(List<Integer> channels,
|
||||||
|
SpatialExtentGroupingState state, DriverContext driverContext) {
|
||||||
|
this.channels = channels;
|
||||||
|
this.state = state;
|
||||||
|
this.driverContext = driverContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction create(
|
||||||
|
List<Integer> channels, DriverContext driverContext) {
|
||||||
|
return new SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction(channels, SpatialExtentCartesianShapeDocValuesAggregator.initGrouping(), driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
return INTERMEDIATE_STATE_DESC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intermediateBlockCount() {
|
||||||
|
return INTERMEDIATE_STATE_DESC.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds,
|
||||||
|
Page page) {
|
||||||
|
IntBlock valuesBlock = page.getBlock(channels.get(0));
|
||||||
|
IntVector valuesVector = valuesBlock.asVector();
|
||||||
|
if (valuesVector == null) {
|
||||||
|
if (valuesBlock.mayHaveNulls()) {
|
||||||
|
state.enableGroupIdTracking(seenGroupIds);
|
||||||
|
}
|
||||||
|
return new GroupingAggregatorFunction.AddInput() {
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntBlock groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntVector groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new GroupingAggregatorFunction.AddInput() {
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntBlock groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntVector groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntVector groups, IntBlock values) {
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
int groupId = groups.getInt(groupPosition);
|
||||||
|
if (values.isNull(groupPosition + positionOffset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
|
int[] valuesArray = new int[valuesEnd - valuesStart];
|
||||||
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
|
valuesArray[v-valuesStart] = values.getInt(v);
|
||||||
|
}
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combine(state, groupId, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntVector groups, IntVector values) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntBlock groups, IntBlock values) {
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
if (groups.isNull(groupPosition)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int groupStart = groups.getFirstValueIndex(groupPosition);
|
||||||
|
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
||||||
|
for (int g = groupStart; g < groupEnd; g++) {
|
||||||
|
int groupId = groups.getInt(g);
|
||||||
|
if (values.isNull(groupPosition + positionOffset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
|
int[] valuesArray = new int[valuesEnd - valuesStart];
|
||||||
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
|
valuesArray[v-valuesStart] = values.getInt(v);
|
||||||
|
}
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combine(state, groupId, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntBlock groups, IntVector values) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectedMayContainUnseenGroups(SeenGroupIds seenGroupIds) {
|
||||||
|
state.enableGroupIdTracking(seenGroupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
||||||
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
|
assert channels.size() == intermediateBlockCount();
|
||||||
|
Block minXUncast = page.getBlock(channels.get(0));
|
||||||
|
if (minXUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector minX = ((IntBlock) minXUncast).asVector();
|
||||||
|
Block maxXUncast = page.getBlock(channels.get(1));
|
||||||
|
if (maxXUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector maxX = ((IntBlock) maxXUncast).asVector();
|
||||||
|
Block maxYUncast = page.getBlock(channels.get(2));
|
||||||
|
if (maxYUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
||||||
|
Block minYUncast = page.getBlock(channels.get(3));
|
||||||
|
if (minYUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector minY = ((IntBlock) minYUncast).asVector();
|
||||||
|
assert minX.getPositionCount() == maxX.getPositionCount() && minX.getPositionCount() == maxY.getPositionCount() && minX.getPositionCount() == minY.getPositionCount();
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
int groupId = groups.getInt(groupPosition);
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combineIntermediate(state, groupId, minX.getInt(groupPosition + positionOffset), maxX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) {
|
||||||
|
if (input.getClass() != getClass()) {
|
||||||
|
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
||||||
|
}
|
||||||
|
SpatialExtentGroupingState inState = ((SpatialExtentCartesianShapeDocValuesGroupingAggregatorFunction) input).state;
|
||||||
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
|
SpatialExtentCartesianShapeDocValuesAggregator.combineStates(state, groupId, inState, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) {
|
||||||
|
state.toIntermediate(blocks, offset, selected, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
||||||
|
DriverContext driverContext) {
|
||||||
|
blocks[offset] = SpatialExtentCartesianShapeDocValuesAggregator.evaluateFinal(state, selected, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName()).append("[");
|
||||||
|
sb.append("channels=").append(channels);
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
state.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,10 @@ import org.elasticsearch.compute.data.Page;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AggregatorFunction} implementation for {@link SpatialExtentCartesianShapeAggregator}.
|
* {@link AggregatorFunction} implementation for {@link SpatialExtentCartesianShapeSourceValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentCartesianShapeAggregatorFunction implements AggregatorFunction {
|
public final class SpatialExtentCartesianShapeSourceValuesAggregatorFunction implements AggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minX", ElementType.INT),
|
new IntermediateStateDesc("minX", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxX", ElementType.INT),
|
new IntermediateStateDesc("maxX", ElementType.INT),
|
||||||
|
@ -39,16 +39,16 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
|
|
||||||
private final List<Integer> channels;
|
private final List<Integer> channels;
|
||||||
|
|
||||||
public SpatialExtentCartesianShapeAggregatorFunction(DriverContext driverContext,
|
public SpatialExtentCartesianShapeSourceValuesAggregatorFunction(DriverContext driverContext,
|
||||||
List<Integer> channels, SpatialExtentState state) {
|
List<Integer> channels, SpatialExtentState state) {
|
||||||
this.driverContext = driverContext;
|
this.driverContext = driverContext;
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpatialExtentCartesianShapeAggregatorFunction create(DriverContext driverContext,
|
public static SpatialExtentCartesianShapeSourceValuesAggregatorFunction create(
|
||||||
List<Integer> channels) {
|
DriverContext driverContext, List<Integer> channels) {
|
||||||
return new SpatialExtentCartesianShapeAggregatorFunction(driverContext, channels, SpatialExtentCartesianShapeAggregator.initSingle());
|
return new SpatialExtentCartesianShapeSourceValuesAggregatorFunction(driverContext, channels, SpatialExtentCartesianShapeSourceValuesAggregator.initSingle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
@ -90,7 +90,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
private void addRawVector(BytesRefVector vector) {
|
private void addRawVector(BytesRefVector vector) {
|
||||||
BytesRef scratch = new BytesRef();
|
BytesRef scratch = new BytesRef();
|
||||||
for (int i = 0; i < vector.getPositionCount(); i++) {
|
for (int i = 0; i < vector.getPositionCount(); i++) {
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, vector.getBytesRef(i, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
if (mask.getBoolean(i) == false) {
|
if (mask.getBoolean(i) == false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, vector.getBytesRef(i, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
int start = block.getFirstValueIndex(p);
|
int start = block.getFirstValueIndex(p);
|
||||||
int end = start + block.getValueCount(p);
|
int end = start + block.getValueCount(p);
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, block.getBytesRef(i, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
int start = block.getFirstValueIndex(p);
|
int start = block.getFirstValueIndex(p);
|
||||||
int end = start + block.getValueCount(p);
|
int end = start + block.getValueCount(p);
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, block.getBytesRef(i, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector minY = ((IntBlock) minYUncast).asVector();
|
||||||
assert minY.getPositionCount() == 1;
|
assert minY.getPositionCount() == 1;
|
||||||
SpatialExtentCartesianShapeAggregator.combineIntermediate(state, minX.getInt(0), maxX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combineIntermediate(state, minX.getInt(0), maxX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -173,7 +173,7 @@ public final class SpatialExtentCartesianShapeAggregatorFunction implements Aggr
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
blocks[offset] = SpatialExtentCartesianShapeAggregator.evaluateFinal(state, driverContext);
|
blocks[offset] = SpatialExtentCartesianShapeSourceValuesAggregator.evaluateFinal(state, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentCartesianShapeSourceValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentCartesianShapeSourceValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
public SpatialExtentCartesianShapeSourceValuesAggregatorFunctionSupplier(List<Integer> channels) {
|
||||||
|
this.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpatialExtentCartesianShapeSourceValuesAggregatorFunction aggregator(
|
||||||
|
DriverContext driverContext) {
|
||||||
|
return SpatialExtentCartesianShapeSourceValuesAggregatorFunction.create(driverContext, channels);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction groupingAggregator(
|
||||||
|
DriverContext driverContext) {
|
||||||
|
return SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction.create(channels, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String describe() {
|
||||||
|
return "spatial_extent_cartesian_shape_source of valuess";
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,10 @@ import org.elasticsearch.compute.data.Page;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentCartesianShapeAggregator}.
|
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentCartesianShapeSourceValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentCartesianShapeGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
public final class SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minX", ElementType.INT),
|
new IntermediateStateDesc("minX", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxX", ElementType.INT),
|
new IntermediateStateDesc("maxX", ElementType.INT),
|
||||||
|
@ -39,16 +39,16 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
|
|
||||||
private final DriverContext driverContext;
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
public SpatialExtentCartesianShapeGroupingAggregatorFunction(List<Integer> channels,
|
public SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction(List<Integer> channels,
|
||||||
SpatialExtentGroupingState state, DriverContext driverContext) {
|
SpatialExtentGroupingState state, DriverContext driverContext) {
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.driverContext = driverContext;
|
this.driverContext = driverContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpatialExtentCartesianShapeGroupingAggregatorFunction create(List<Integer> channels,
|
public static SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction create(
|
||||||
DriverContext driverContext) {
|
List<Integer> channels, DriverContext driverContext) {
|
||||||
return new SpatialExtentCartesianShapeGroupingAggregatorFunction(channels, SpatialExtentCartesianShapeAggregator.initGrouping(), driverContext);
|
return new SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction(channels, SpatialExtentCartesianShapeSourceValuesAggregator.initGrouping(), driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
@ -112,7 +112,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
for (int v = valuesStart; v < valuesEnd; v++) {
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
BytesRef scratch = new BytesRef();
|
BytesRef scratch = new BytesRef();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
for (int v = valuesStart; v < valuesEnd; v++) {
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
||||||
for (int g = groupStart; g < groupEnd; g++) {
|
for (int g = groupStart; g < groupEnd; g++) {
|
||||||
int groupId = groups.getInt(g);
|
int groupId = groups.getInt(g);
|
||||||
SpatialExtentCartesianShapeAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
assert minX.getPositionCount() == maxX.getPositionCount() && minX.getPositionCount() == maxY.getPositionCount() && minX.getPositionCount() == minY.getPositionCount();
|
assert minX.getPositionCount() == maxX.getPositionCount() && minX.getPositionCount() == maxY.getPositionCount() && minX.getPositionCount() == minY.getPositionCount();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentCartesianShapeAggregator.combineIntermediate(state, groupId, minX.getInt(groupPosition + positionOffset), maxX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
SpatialExtentCartesianShapeSourceValuesAggregator.combineIntermediate(state, groupId, minX.getInt(groupPosition + positionOffset), maxX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,9 +203,9 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
if (input.getClass() != getClass()) {
|
if (input.getClass() != getClass()) {
|
||||||
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
||||||
}
|
}
|
||||||
SpatialExtentGroupingState inState = ((SpatialExtentCartesianShapeGroupingAggregatorFunction) input).state;
|
SpatialExtentGroupingState inState = ((SpatialExtentCartesianShapeSourceValuesGroupingAggregatorFunction) input).state;
|
||||||
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
SpatialExtentCartesianShapeAggregator.combineStates(state, groupId, inState, position);
|
SpatialExtentCartesianShapeSourceValuesAggregator.combineStates(state, groupId, inState, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -216,7 +216,7 @@ public final class SpatialExtentCartesianShapeGroupingAggregatorFunction impleme
|
||||||
@Override
|
@Override
|
||||||
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
||||||
DriverContext driverContext) {
|
DriverContext driverContext) {
|
||||||
blocks[offset] = SpatialExtentCartesianShapeAggregator.evaluateFinal(state, selected, driverContext);
|
blocks[offset] = SpatialExtentCartesianShapeSourceValuesAggregator.evaluateFinal(state, selected, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -27,12 +27,12 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoPointDocValuesAggregatorFunction implements AggregatorFunction {
|
public final class SpatialExtentGeoPointDocValuesAggregatorFunction implements AggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final DriverContext driverContext;
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
@ -136,43 +136,43 @@ public final class SpatialExtentGeoPointDocValuesAggregatorFunction implements A
|
||||||
public void addIntermediateInput(Page page) {
|
public void addIntermediateInput(Page page) {
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == 1;
|
assert top.getPositionCount() == 1;
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
assert minPosX.getPositionCount() == 1;
|
assert bottom.getPositionCount() == 1;
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
assert maxNegX.getPositionCount() == 1;
|
assert negLeft.getPositionCount() == 1;
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
assert maxPosX.getPositionCount() == 1;
|
assert negRight.getPositionCount() == 1;
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
assert maxY.getPositionCount() == 1;
|
assert posLeft.getPositionCount() == 1;
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minY.getPositionCount() == 1;
|
assert posRight.getPositionCount() == 1;
|
||||||
SpatialExtentGeoPointDocValuesAggregator.combineIntermediate(state, minNegX.getInt(0), minPosX.getInt(0), maxNegX.getInt(0), maxPosX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
SpatialExtentGeoPointDocValuesAggregator.combineIntermediate(state, top.getInt(0), bottom.getInt(0), negLeft.getInt(0), negRight.getInt(0), posLeft.getInt(0), posRight.getInt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,12 +27,12 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoPointDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
public final class SpatialExtentGeoPointDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
||||||
|
|
||||||
|
@ -168,40 +168,40 @@ public final class SpatialExtentGeoPointDocValuesGroupingAggregatorFunction impl
|
||||||
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
||||||
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == minPosX.getPositionCount() && minNegX.getPositionCount() == maxNegX.getPositionCount() && minNegX.getPositionCount() == maxPosX.getPositionCount() && minNegX.getPositionCount() == maxY.getPositionCount() && minNegX.getPositionCount() == minY.getPositionCount();
|
assert top.getPositionCount() == bottom.getPositionCount() && top.getPositionCount() == negLeft.getPositionCount() && top.getPositionCount() == negRight.getPositionCount() && top.getPositionCount() == posLeft.getPositionCount() && top.getPositionCount() == posRight.getPositionCount();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentGeoPointDocValuesAggregator.combineIntermediate(state, groupId, minNegX.getInt(groupPosition + positionOffset), minPosX.getInt(groupPosition + positionOffset), maxNegX.getInt(groupPosition + positionOffset), maxPosX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
SpatialExtentGeoPointDocValuesAggregator.combineIntermediate(state, groupId, top.getInt(groupPosition + positionOffset), bottom.getInt(groupPosition + positionOffset), negLeft.getInt(groupPosition + positionOffset), negRight.getInt(groupPosition + positionOffset), posLeft.getInt(groupPosition + positionOffset), posRight.getInt(groupPosition + positionOffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,12 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoPointSourceValuesAggregatorFunction implements AggregatorFunction {
|
public final class SpatialExtentGeoPointSourceValuesAggregatorFunction implements AggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final DriverContext driverContext;
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
@ -141,43 +141,43 @@ public final class SpatialExtentGeoPointSourceValuesAggregatorFunction implement
|
||||||
public void addIntermediateInput(Page page) {
|
public void addIntermediateInput(Page page) {
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == 1;
|
assert top.getPositionCount() == 1;
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
assert minPosX.getPositionCount() == 1;
|
assert bottom.getPositionCount() == 1;
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
assert maxNegX.getPositionCount() == 1;
|
assert negLeft.getPositionCount() == 1;
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
assert maxPosX.getPositionCount() == 1;
|
assert negRight.getPositionCount() == 1;
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
assert maxY.getPositionCount() == 1;
|
assert posLeft.getPositionCount() == 1;
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minY.getPositionCount() == 1;
|
assert posRight.getPositionCount() == 1;
|
||||||
SpatialExtentGeoPointSourceValuesAggregator.combineIntermediate(state, minNegX.getInt(0), minPosX.getInt(0), maxNegX.getInt(0), maxPosX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
SpatialExtentGeoPointSourceValuesAggregator.combineIntermediate(state, top.getInt(0), bottom.getInt(0), negLeft.getInt(0), negRight.getInt(0), posLeft.getInt(0), posRight.getInt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -28,12 +28,12 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoPointSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
public final class SpatialExtentGeoPointSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
||||||
|
|
||||||
|
@ -173,40 +173,40 @@ public final class SpatialExtentGeoPointSourceValuesGroupingAggregatorFunction i
|
||||||
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
||||||
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == minPosX.getPositionCount() && minNegX.getPositionCount() == maxNegX.getPositionCount() && minNegX.getPositionCount() == maxPosX.getPositionCount() && minNegX.getPositionCount() == maxY.getPositionCount() && minNegX.getPositionCount() == minY.getPositionCount();
|
assert top.getPositionCount() == bottom.getPositionCount() && top.getPositionCount() == negLeft.getPositionCount() && top.getPositionCount() == negRight.getPositionCount() && top.getPositionCount() == posLeft.getPositionCount() && top.getPositionCount() == posRight.getPositionCount();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentGeoPointSourceValuesAggregator.combineIntermediate(state, groupId, minNegX.getInt(groupPosition + positionOffset), minPosX.getInt(groupPosition + positionOffset), maxNegX.getInt(groupPosition + positionOffset), maxPosX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
SpatialExtentGeoPointSourceValuesAggregator.combineIntermediate(state, groupId, top.getInt(groupPosition + positionOffset), bottom.getInt(groupPosition + positionOffset), negLeft.getInt(groupPosition + positionOffset), negRight.getInt(groupPosition + positionOffset), posLeft.getInt(groupPosition + positionOffset), posRight.getInt(groupPosition + positionOffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.lang.StringBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.AggregatorFunction;
|
||||||
|
import org.elasticsearch.compute.aggregation.IntermediateStateDesc;
|
||||||
|
import org.elasticsearch.compute.data.Block;
|
||||||
|
import org.elasticsearch.compute.data.BooleanVector;
|
||||||
|
import org.elasticsearch.compute.data.ElementType;
|
||||||
|
import org.elasticsearch.compute.data.IntBlock;
|
||||||
|
import org.elasticsearch.compute.data.IntVector;
|
||||||
|
import org.elasticsearch.compute.data.Page;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AggregatorFunction} implementation for {@link SpatialExtentGeoShapeDocValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentGeoShapeDocValuesAggregatorFunction implements AggregatorFunction {
|
||||||
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
private final SpatialExtentStateWrappedLongitudeState state;
|
||||||
|
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
public SpatialExtentGeoShapeDocValuesAggregatorFunction(DriverContext driverContext,
|
||||||
|
List<Integer> channels, SpatialExtentStateWrappedLongitudeState state) {
|
||||||
|
this.driverContext = driverContext;
|
||||||
|
this.channels = channels;
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentGeoShapeDocValuesAggregatorFunction create(DriverContext driverContext,
|
||||||
|
List<Integer> channels) {
|
||||||
|
return new SpatialExtentGeoShapeDocValuesAggregatorFunction(driverContext, channels, SpatialExtentGeoShapeDocValuesAggregator.initSingle());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
return INTERMEDIATE_STATE_DESC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intermediateBlockCount() {
|
||||||
|
return INTERMEDIATE_STATE_DESC.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRawInput(Page page, BooleanVector mask) {
|
||||||
|
if (mask.allFalse()) {
|
||||||
|
// Entire page masked away
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mask.allTrue()) {
|
||||||
|
// No masking
|
||||||
|
IntBlock block = page.getBlock(channels.get(0));
|
||||||
|
IntVector vector = block.asVector();
|
||||||
|
if (vector != null) {
|
||||||
|
addRawVector(vector);
|
||||||
|
} else {
|
||||||
|
addRawBlock(block);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Some positions masked away, others kept
|
||||||
|
IntBlock block = page.getBlock(channels.get(0));
|
||||||
|
IntVector vector = block.asVector();
|
||||||
|
if (vector != null) {
|
||||||
|
addRawVector(vector, mask);
|
||||||
|
} else {
|
||||||
|
addRawBlock(block, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawVector(IntVector vector) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawVector(IntVector vector, BooleanVector mask) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawBlock(IntBlock block) {
|
||||||
|
for (int p = 0; p < block.getPositionCount(); p++) {
|
||||||
|
if (block.isNull(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = block.getFirstValueIndex(p);
|
||||||
|
int end = start + block.getValueCount(p);
|
||||||
|
int[] valuesArray = new int[end - start];
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
valuesArray[i-start] = block.getInt(i);
|
||||||
|
}
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combine(state, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawBlock(IntBlock block, BooleanVector mask) {
|
||||||
|
for (int p = 0; p < block.getPositionCount(); p++) {
|
||||||
|
if (mask.getBoolean(p) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (block.isNull(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int start = block.getFirstValueIndex(p);
|
||||||
|
int end = start + block.getValueCount(p);
|
||||||
|
int[] valuesArray = new int[end - start];
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
valuesArray[i-start] = block.getInt(i);
|
||||||
|
}
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combine(state, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateInput(Page page) {
|
||||||
|
assert channels.size() == intermediateBlockCount();
|
||||||
|
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
||||||
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
|
if (topUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
|
assert top.getPositionCount() == 1;
|
||||||
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
|
assert bottom.getPositionCount() == 1;
|
||||||
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
|
assert negLeft.getPositionCount() == 1;
|
||||||
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
|
assert negRight.getPositionCount() == 1;
|
||||||
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
|
assert posLeft.getPositionCount() == 1;
|
||||||
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
|
assert posRight.getPositionCount() == 1;
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combineIntermediate(state, top.getInt(0), bottom.getInt(0), negLeft.getInt(0), negRight.getInt(0), posLeft.getInt(0), posRight.getInt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateIntermediate(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
|
state.toIntermediate(blocks, offset, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
|
blocks[offset] = SpatialExtentGeoShapeDocValuesAggregator.evaluateFinal(state, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName()).append("[");
|
||||||
|
sb.append("channels=").append(channels);
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
state.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,29 +12,29 @@ import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentGeoShapeAggregator}.
|
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentGeoShapeDocValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoShapeAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
public final class SpatialExtentGeoShapeDocValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
||||||
private final List<Integer> channels;
|
private final List<Integer> channels;
|
||||||
|
|
||||||
public SpatialExtentGeoShapeAggregatorFunctionSupplier(List<Integer> channels) {
|
public SpatialExtentGeoShapeDocValuesAggregatorFunctionSupplier(List<Integer> channels) {
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpatialExtentGeoShapeAggregatorFunction aggregator(DriverContext driverContext) {
|
public SpatialExtentGeoShapeDocValuesAggregatorFunction aggregator(DriverContext driverContext) {
|
||||||
return SpatialExtentGeoShapeAggregatorFunction.create(driverContext, channels);
|
return SpatialExtentGeoShapeDocValuesAggregatorFunction.create(driverContext, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpatialExtentGeoShapeGroupingAggregatorFunction groupingAggregator(
|
public SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction groupingAggregator(
|
||||||
DriverContext driverContext) {
|
DriverContext driverContext) {
|
||||||
return SpatialExtentGeoShapeGroupingAggregatorFunction.create(channels, driverContext);
|
return SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction.create(channels, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String describe() {
|
public String describe() {
|
||||||
return "spatial_extent_geo of shapes";
|
return "spatial_extent_geo_shape_doc of valuess";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
// 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
// 2.0.
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.Override;
|
||||||
|
import java.lang.String;
|
||||||
|
import java.lang.StringBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import org.elasticsearch.compute.aggregation.GroupingAggregatorFunction;
|
||||||
|
import org.elasticsearch.compute.aggregation.IntermediateStateDesc;
|
||||||
|
import org.elasticsearch.compute.aggregation.SeenGroupIds;
|
||||||
|
import org.elasticsearch.compute.data.Block;
|
||||||
|
import org.elasticsearch.compute.data.ElementType;
|
||||||
|
import org.elasticsearch.compute.data.IntBlock;
|
||||||
|
import org.elasticsearch.compute.data.IntVector;
|
||||||
|
import org.elasticsearch.compute.data.Page;
|
||||||
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentGeoShapeDocValuesAggregator}.
|
||||||
|
* This class is generated. Do not edit it.
|
||||||
|
*/
|
||||||
|
public final class SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
|
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
||||||
|
|
||||||
|
private final List<Integer> channels;
|
||||||
|
|
||||||
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
public SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction(List<Integer> channels,
|
||||||
|
SpatialExtentGroupingStateWrappedLongitudeState state, DriverContext driverContext) {
|
||||||
|
this.channels = channels;
|
||||||
|
this.state = state;
|
||||||
|
this.driverContext = driverContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction create(
|
||||||
|
List<Integer> channels, DriverContext driverContext) {
|
||||||
|
return new SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction(channels, SpatialExtentGeoShapeDocValuesAggregator.initGrouping(), driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
return INTERMEDIATE_STATE_DESC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intermediateBlockCount() {
|
||||||
|
return INTERMEDIATE_STATE_DESC.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GroupingAggregatorFunction.AddInput prepareProcessPage(SeenGroupIds seenGroupIds,
|
||||||
|
Page page) {
|
||||||
|
IntBlock valuesBlock = page.getBlock(channels.get(0));
|
||||||
|
IntVector valuesVector = valuesBlock.asVector();
|
||||||
|
if (valuesVector == null) {
|
||||||
|
if (valuesBlock.mayHaveNulls()) {
|
||||||
|
state.enableGroupIdTracking(seenGroupIds);
|
||||||
|
}
|
||||||
|
return new GroupingAggregatorFunction.AddInput() {
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntBlock groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntVector groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return new GroupingAggregatorFunction.AddInput() {
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntBlock groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int positionOffset, IntVector groupIds) {
|
||||||
|
addRawInput(positionOffset, groupIds, valuesVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntVector groups, IntBlock values) {
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
int groupId = groups.getInt(groupPosition);
|
||||||
|
if (values.isNull(groupPosition + positionOffset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
|
int[] valuesArray = new int[valuesEnd - valuesStart];
|
||||||
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
|
valuesArray[v-valuesStart] = values.getInt(v);
|
||||||
|
}
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combine(state, groupId, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntVector groups, IntVector values) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntBlock groups, IntBlock values) {
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
if (groups.isNull(groupPosition)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int groupStart = groups.getFirstValueIndex(groupPosition);
|
||||||
|
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
||||||
|
for (int g = groupStart; g < groupEnd; g++) {
|
||||||
|
int groupId = groups.getInt(g);
|
||||||
|
if (values.isNull(groupPosition + positionOffset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
|
int[] valuesArray = new int[valuesEnd - valuesStart];
|
||||||
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
|
valuesArray[v-valuesStart] = values.getInt(v);
|
||||||
|
}
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combine(state, groupId, valuesArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRawInput(int positionOffset, IntBlock groups, IntVector values) {
|
||||||
|
// This type does not support vectors because all values are multi-valued
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectedMayContainUnseenGroups(SeenGroupIds seenGroupIds) {
|
||||||
|
state.enableGroupIdTracking(seenGroupIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
||||||
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
|
assert channels.size() == intermediateBlockCount();
|
||||||
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
|
if (topUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
|
assert top.getPositionCount() == bottom.getPositionCount() && top.getPositionCount() == negLeft.getPositionCount() && top.getPositionCount() == negRight.getPositionCount() && top.getPositionCount() == posLeft.getPositionCount() && top.getPositionCount() == posRight.getPositionCount();
|
||||||
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
|
int groupId = groups.getInt(groupPosition);
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combineIntermediate(state, groupId, top.getInt(groupPosition + positionOffset), bottom.getInt(groupPosition + positionOffset), negLeft.getInt(groupPosition + positionOffset), negRight.getInt(groupPosition + positionOffset), posLeft.getInt(groupPosition + positionOffset), posRight.getInt(groupPosition + positionOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addIntermediateRowInput(int groupId, GroupingAggregatorFunction input, int position) {
|
||||||
|
if (input.getClass() != getClass()) {
|
||||||
|
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
||||||
|
}
|
||||||
|
SpatialExtentGroupingStateWrappedLongitudeState inState = ((SpatialExtentGeoShapeDocValuesGroupingAggregatorFunction) input).state;
|
||||||
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
|
SpatialExtentGeoShapeDocValuesAggregator.combineStates(state, groupId, inState, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateIntermediate(Block[] blocks, int offset, IntVector selected) {
|
||||||
|
state.toIntermediate(blocks, offset, selected, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
||||||
|
DriverContext driverContext) {
|
||||||
|
blocks[offset] = SpatialExtentGeoShapeDocValuesAggregator.evaluateFinal(state, selected, driverContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getClass().getSimpleName()).append("[");
|
||||||
|
sb.append("channels=").append(channels);
|
||||||
|
sb.append("]");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
state.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,17 +23,17 @@ import org.elasticsearch.compute.data.Page;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AggregatorFunction} implementation for {@link SpatialExtentGeoShapeAggregator}.
|
* {@link AggregatorFunction} implementation for {@link SpatialExtentGeoShapeSourceValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoShapeAggregatorFunction implements AggregatorFunction {
|
public final class SpatialExtentGeoShapeSourceValuesAggregatorFunction implements AggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final DriverContext driverContext;
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
|
@ -41,16 +41,16 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
|
|
||||||
private final List<Integer> channels;
|
private final List<Integer> channels;
|
||||||
|
|
||||||
public SpatialExtentGeoShapeAggregatorFunction(DriverContext driverContext,
|
public SpatialExtentGeoShapeSourceValuesAggregatorFunction(DriverContext driverContext,
|
||||||
List<Integer> channels, SpatialExtentStateWrappedLongitudeState state) {
|
List<Integer> channels, SpatialExtentStateWrappedLongitudeState state) {
|
||||||
this.driverContext = driverContext;
|
this.driverContext = driverContext;
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpatialExtentGeoShapeAggregatorFunction create(DriverContext driverContext,
|
public static SpatialExtentGeoShapeSourceValuesAggregatorFunction create(
|
||||||
List<Integer> channels) {
|
DriverContext driverContext, List<Integer> channels) {
|
||||||
return new SpatialExtentGeoShapeAggregatorFunction(driverContext, channels, SpatialExtentGeoShapeAggregator.initSingle());
|
return new SpatialExtentGeoShapeSourceValuesAggregatorFunction(driverContext, channels, SpatialExtentGeoShapeSourceValuesAggregator.initSingle());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
@ -92,7 +92,7 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
private void addRawVector(BytesRefVector vector) {
|
private void addRawVector(BytesRefVector vector) {
|
||||||
BytesRef scratch = new BytesRef();
|
BytesRef scratch = new BytesRef();
|
||||||
for (int i = 0; i < vector.getPositionCount(); i++) {
|
for (int i = 0; i < vector.getPositionCount(); i++) {
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, vector.getBytesRef(i, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
if (mask.getBoolean(i) == false) {
|
if (mask.getBoolean(i) == false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, vector.getBytesRef(i, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, vector.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
int start = block.getFirstValueIndex(p);
|
int start = block.getFirstValueIndex(p);
|
||||||
int end = start + block.getValueCount(p);
|
int end = start + block.getValueCount(p);
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, block.getBytesRef(i, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
int start = block.getFirstValueIndex(p);
|
int start = block.getFirstValueIndex(p);
|
||||||
int end = start + block.getValueCount(p);
|
int end = start + block.getValueCount(p);
|
||||||
for (int i = start; i < end; i++) {
|
for (int i = start; i < end; i++) {
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, block.getBytesRef(i, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, block.getBytesRef(i, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,43 +141,43 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
public void addIntermediateInput(Page page) {
|
public void addIntermediateInput(Page page) {
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
assert page.getBlockCount() >= channels.get(0) + intermediateStateDesc().size();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == 1;
|
assert top.getPositionCount() == 1;
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
assert minPosX.getPositionCount() == 1;
|
assert bottom.getPositionCount() == 1;
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
assert maxNegX.getPositionCount() == 1;
|
assert negLeft.getPositionCount() == 1;
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
assert maxPosX.getPositionCount() == 1;
|
assert negRight.getPositionCount() == 1;
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
assert maxY.getPositionCount() == 1;
|
assert posLeft.getPositionCount() == 1;
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minY.getPositionCount() == 1;
|
assert posRight.getPositionCount() == 1;
|
||||||
SpatialExtentGeoShapeAggregator.combineIntermediate(state, minNegX.getInt(0), minPosX.getInt(0), maxNegX.getInt(0), maxPosX.getInt(0), maxY.getInt(0), minY.getInt(0));
|
SpatialExtentGeoShapeSourceValuesAggregator.combineIntermediate(state, top.getInt(0), bottom.getInt(0), negLeft.getInt(0), negRight.getInt(0), posLeft.getInt(0), posRight.getInt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -187,7 +187,7 @@ public final class SpatialExtentGeoShapeAggregatorFunction implements Aggregator
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
public void evaluateFinal(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
blocks[offset] = SpatialExtentGeoShapeAggregator.evaluateFinal(state, driverContext);
|
blocks[offset] = SpatialExtentGeoShapeSourceValuesAggregator.evaluateFinal(state, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -12,29 +12,30 @@ import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentCartesianShapeAggregator}.
|
* {@link AggregatorFunctionSupplier} implementation for {@link SpatialExtentGeoShapeSourceValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentCartesianShapeAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
public final class SpatialExtentGeoShapeSourceValuesAggregatorFunctionSupplier implements AggregatorFunctionSupplier {
|
||||||
private final List<Integer> channels;
|
private final List<Integer> channels;
|
||||||
|
|
||||||
public SpatialExtentCartesianShapeAggregatorFunctionSupplier(List<Integer> channels) {
|
public SpatialExtentGeoShapeSourceValuesAggregatorFunctionSupplier(List<Integer> channels) {
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpatialExtentCartesianShapeAggregatorFunction aggregator(DriverContext driverContext) {
|
public SpatialExtentGeoShapeSourceValuesAggregatorFunction aggregator(
|
||||||
return SpatialExtentCartesianShapeAggregatorFunction.create(driverContext, channels);
|
DriverContext driverContext) {
|
||||||
|
return SpatialExtentGeoShapeSourceValuesAggregatorFunction.create(driverContext, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpatialExtentCartesianShapeGroupingAggregatorFunction groupingAggregator(
|
public SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction groupingAggregator(
|
||||||
DriverContext driverContext) {
|
DriverContext driverContext) {
|
||||||
return SpatialExtentCartesianShapeGroupingAggregatorFunction.create(channels, driverContext);
|
return SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction.create(channels, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String describe() {
|
public String describe() {
|
||||||
return "spatial_extent_cartesian of shapes";
|
return "spatial_extent_geo_shape_source of valuess";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,17 +23,17 @@ import org.elasticsearch.compute.data.Page;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentGeoShapeAggregator}.
|
* {@link GroupingAggregatorFunction} implementation for {@link SpatialExtentGeoShapeSourceValuesAggregator}.
|
||||||
* This class is generated. Do not edit it.
|
* This class is generated. Do not edit it.
|
||||||
*/
|
*/
|
||||||
public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
public final class SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction implements GroupingAggregatorFunction {
|
||||||
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
private static final List<IntermediateStateDesc> INTERMEDIATE_STATE_DESC = List.of(
|
||||||
new IntermediateStateDesc("minNegX", ElementType.INT),
|
new IntermediateStateDesc("top", ElementType.INT),
|
||||||
new IntermediateStateDesc("minPosX", ElementType.INT),
|
new IntermediateStateDesc("bottom", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxNegX", ElementType.INT),
|
new IntermediateStateDesc("negLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxPosX", ElementType.INT),
|
new IntermediateStateDesc("negRight", ElementType.INT),
|
||||||
new IntermediateStateDesc("maxY", ElementType.INT),
|
new IntermediateStateDesc("posLeft", ElementType.INT),
|
||||||
new IntermediateStateDesc("minY", ElementType.INT) );
|
new IntermediateStateDesc("posRight", ElementType.INT) );
|
||||||
|
|
||||||
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
private final SpatialExtentGroupingStateWrappedLongitudeState state;
|
||||||
|
|
||||||
|
@ -41,16 +41,16 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
|
|
||||||
private final DriverContext driverContext;
|
private final DriverContext driverContext;
|
||||||
|
|
||||||
public SpatialExtentGeoShapeGroupingAggregatorFunction(List<Integer> channels,
|
public SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction(List<Integer> channels,
|
||||||
SpatialExtentGroupingStateWrappedLongitudeState state, DriverContext driverContext) {
|
SpatialExtentGroupingStateWrappedLongitudeState state, DriverContext driverContext) {
|
||||||
this.channels = channels;
|
this.channels = channels;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.driverContext = driverContext;
|
this.driverContext = driverContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpatialExtentGeoShapeGroupingAggregatorFunction create(List<Integer> channels,
|
public static SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction create(
|
||||||
DriverContext driverContext) {
|
List<Integer> channels, DriverContext driverContext) {
|
||||||
return new SpatialExtentGeoShapeGroupingAggregatorFunction(channels, SpatialExtentGeoShapeAggregator.initGrouping(), driverContext);
|
return new SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction(channels, SpatialExtentGeoShapeSourceValuesAggregator.initGrouping(), driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
public static List<IntermediateStateDesc> intermediateStateDesc() {
|
||||||
|
@ -114,7 +114,7 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
for (int v = valuesStart; v < valuesEnd; v++) {
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
BytesRef scratch = new BytesRef();
|
BytesRef scratch = new BytesRef();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
int valuesStart = values.getFirstValueIndex(groupPosition + positionOffset);
|
||||||
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
int valuesEnd = valuesStart + values.getValueCount(groupPosition + positionOffset);
|
||||||
for (int v = valuesStart; v < valuesEnd; v++) {
|
for (int v = valuesStart; v < valuesEnd; v++) {
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(v, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
int groupEnd = groupStart + groups.getValueCount(groupPosition);
|
||||||
for (int g = groupStart; g < groupEnd; g++) {
|
for (int g = groupStart; g < groupEnd; g++) {
|
||||||
int groupId = groups.getInt(g);
|
int groupId = groups.getInt(g);
|
||||||
SpatialExtentGeoShapeAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
SpatialExtentGeoShapeSourceValuesAggregator.combine(state, groupId, values.getBytesRef(groupPosition + positionOffset, scratch));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,40 +173,40 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
public void addIntermediateInput(int positionOffset, IntVector groups, Page page) {
|
||||||
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
assert channels.size() == intermediateBlockCount();
|
assert channels.size() == intermediateBlockCount();
|
||||||
Block minNegXUncast = page.getBlock(channels.get(0));
|
Block topUncast = page.getBlock(channels.get(0));
|
||||||
if (minNegXUncast.areAllValuesNull()) {
|
if (topUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minNegX = ((IntBlock) minNegXUncast).asVector();
|
IntVector top = ((IntBlock) topUncast).asVector();
|
||||||
Block minPosXUncast = page.getBlock(channels.get(1));
|
Block bottomUncast = page.getBlock(channels.get(1));
|
||||||
if (minPosXUncast.areAllValuesNull()) {
|
if (bottomUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minPosX = ((IntBlock) minPosXUncast).asVector();
|
IntVector bottom = ((IntBlock) bottomUncast).asVector();
|
||||||
Block maxNegXUncast = page.getBlock(channels.get(2));
|
Block negLeftUncast = page.getBlock(channels.get(2));
|
||||||
if (maxNegXUncast.areAllValuesNull()) {
|
if (negLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxNegX = ((IntBlock) maxNegXUncast).asVector();
|
IntVector negLeft = ((IntBlock) negLeftUncast).asVector();
|
||||||
Block maxPosXUncast = page.getBlock(channels.get(3));
|
Block negRightUncast = page.getBlock(channels.get(3));
|
||||||
if (maxPosXUncast.areAllValuesNull()) {
|
if (negRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxPosX = ((IntBlock) maxPosXUncast).asVector();
|
IntVector negRight = ((IntBlock) negRightUncast).asVector();
|
||||||
Block maxYUncast = page.getBlock(channels.get(4));
|
Block posLeftUncast = page.getBlock(channels.get(4));
|
||||||
if (maxYUncast.areAllValuesNull()) {
|
if (posLeftUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector maxY = ((IntBlock) maxYUncast).asVector();
|
IntVector posLeft = ((IntBlock) posLeftUncast).asVector();
|
||||||
Block minYUncast = page.getBlock(channels.get(5));
|
Block posRightUncast = page.getBlock(channels.get(5));
|
||||||
if (minYUncast.areAllValuesNull()) {
|
if (posRightUncast.areAllValuesNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IntVector minY = ((IntBlock) minYUncast).asVector();
|
IntVector posRight = ((IntBlock) posRightUncast).asVector();
|
||||||
assert minNegX.getPositionCount() == minPosX.getPositionCount() && minNegX.getPositionCount() == maxNegX.getPositionCount() && minNegX.getPositionCount() == maxPosX.getPositionCount() && minNegX.getPositionCount() == maxY.getPositionCount() && minNegX.getPositionCount() == minY.getPositionCount();
|
assert top.getPositionCount() == bottom.getPositionCount() && top.getPositionCount() == negLeft.getPositionCount() && top.getPositionCount() == negRight.getPositionCount() && top.getPositionCount() == posLeft.getPositionCount() && top.getPositionCount() == posRight.getPositionCount();
|
||||||
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
for (int groupPosition = 0; groupPosition < groups.getPositionCount(); groupPosition++) {
|
||||||
int groupId = groups.getInt(groupPosition);
|
int groupId = groups.getInt(groupPosition);
|
||||||
SpatialExtentGeoShapeAggregator.combineIntermediate(state, groupId, minNegX.getInt(groupPosition + positionOffset), minPosX.getInt(groupPosition + positionOffset), maxNegX.getInt(groupPosition + positionOffset), maxPosX.getInt(groupPosition + positionOffset), maxY.getInt(groupPosition + positionOffset), minY.getInt(groupPosition + positionOffset));
|
SpatialExtentGeoShapeSourceValuesAggregator.combineIntermediate(state, groupId, top.getInt(groupPosition + positionOffset), bottom.getInt(groupPosition + positionOffset), negLeft.getInt(groupPosition + positionOffset), negRight.getInt(groupPosition + positionOffset), posLeft.getInt(groupPosition + positionOffset), posRight.getInt(groupPosition + positionOffset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,9 +215,9 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
if (input.getClass() != getClass()) {
|
if (input.getClass() != getClass()) {
|
||||||
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
throw new IllegalArgumentException("expected " + getClass() + "; got " + input.getClass());
|
||||||
}
|
}
|
||||||
SpatialExtentGroupingStateWrappedLongitudeState inState = ((SpatialExtentGeoShapeGroupingAggregatorFunction) input).state;
|
SpatialExtentGroupingStateWrappedLongitudeState inState = ((SpatialExtentGeoShapeSourceValuesGroupingAggregatorFunction) input).state;
|
||||||
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
state.enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
SpatialExtentGeoShapeAggregator.combineStates(state, groupId, inState, position);
|
SpatialExtentGeoShapeSourceValuesAggregator.combineStates(state, groupId, inState, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -228,7 +228,7 @@ public final class SpatialExtentGeoShapeGroupingAggregatorFunction implements Gr
|
||||||
@Override
|
@Override
|
||||||
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
public void evaluateFinal(Block[] blocks, int offset, IntVector selected,
|
||||||
DriverContext driverContext) {
|
DriverContext driverContext) {
|
||||||
blocks[offset] = SpatialExtentGeoShapeAggregator.evaluateFinal(state, selected, driverContext);
|
blocks[offset] = SpatialExtentGeoShapeSourceValuesAggregator.evaluateFinal(state, selected, driverContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
* 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; you may not use this file except in compliance with the Elastic License
|
|
||||||
* 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.compute.aggregation.spatial;
|
|
||||||
|
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor.WrapLongitude;
|
|
||||||
|
|
||||||
class GeoPointEnvelopeVisitor extends SpatialEnvelopeVisitor.GeoPointVisitor {
|
|
||||||
GeoPointEnvelopeVisitor() {
|
|
||||||
super(WrapLongitude.WRAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
minY = Double.POSITIVE_INFINITY;
|
|
||||||
maxY = Double.NEGATIVE_INFINITY;
|
|
||||||
minNegX = Double.POSITIVE_INFINITY;
|
|
||||||
maxNegX = Double.NEGATIVE_INFINITY;
|
|
||||||
minPosX = Double.POSITIVE_INFINITY;
|
|
||||||
maxPosX = Double.NEGATIVE_INFINITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMinNegX() {
|
|
||||||
return minNegX;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMinPosX() {
|
|
||||||
return minPosX;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMaxNegX() {
|
|
||||||
return maxNegX;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMaxPosX() {
|
|
||||||
return maxPosX;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMaxY() {
|
|
||||||
return maxY;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getMinY() {
|
|
||||||
return minY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Rectangle asRectangle(
|
|
||||||
double minNegX,
|
|
||||||
double minPosX,
|
|
||||||
double maxNegX,
|
|
||||||
double maxPosX,
|
|
||||||
double maxY,
|
|
||||||
double minY,
|
|
||||||
WrapLongitude wrapLongitude
|
|
||||||
) {
|
|
||||||
return SpatialEnvelopeVisitor.GeoPointVisitor.getResult(minNegX, minPosX, maxNegX, maxPosX, maxY, minY, wrapLongitude);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,12 +12,10 @@ import org.apache.lucene.geo.XYEncodingUtils;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.geometry.Point;
|
import org.elasticsearch.geometry.Point;
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
|
||||||
import org.elasticsearch.geometry.utils.GeometryValidator;
|
import org.elasticsearch.geometry.utils.GeometryValidator;
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor.WrapLongitude;
|
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
|
|
||||||
class SpatialAggregationUtils {
|
public class SpatialAggregationUtils {
|
||||||
private SpatialAggregationUtils() { /* Utility class */ }
|
private SpatialAggregationUtils() { /* Utility class */ }
|
||||||
|
|
||||||
public static Geometry decode(BytesRef wkb) {
|
public static Geometry decode(BytesRef wkb) {
|
||||||
|
@ -52,26 +50,12 @@ class SpatialAggregationUtils {
|
||||||
return GeoEncodingUtils.decodeLatitude((int) (encoded >>> 32));
|
return GeoEncodingUtils.decodeLatitude((int) (encoded >>> 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int encodeNegativeLongitude(double d) {
|
public static int encodeLongitude(double d) {
|
||||||
return Double.isFinite(d) ? GeoEncodingUtils.encodeLongitude(d) : DEFAULT_NEG;
|
return Double.isFinite(d) ? GeoEncodingUtils.encodeLongitude(d) : encodeInfinity(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int encodePositiveLongitude(double d) {
|
private static int encodeInfinity(double d) {
|
||||||
return Double.isFinite(d) ? GeoEncodingUtils.encodeLongitude(d) : DEFAULT_POS;
|
return d == Double.NEGATIVE_INFINITY ? Integer.MIN_VALUE : Integer.MAX_VALUE;
|
||||||
}
|
|
||||||
|
|
||||||
public static Rectangle asRectangle(int minNegX, int minPosX, int maxNegX, int maxPosX, int maxY, int minY) {
|
|
||||||
assert minNegX <= 0 == maxNegX <= 0;
|
|
||||||
assert minPosX >= 0 == maxPosX >= 0;
|
|
||||||
return GeoPointEnvelopeVisitor.asRectangle(
|
|
||||||
minNegX <= 0 ? decodeLongitude(minNegX) : Double.POSITIVE_INFINITY,
|
|
||||||
minPosX >= 0 ? decodeLongitude(minPosX) : Double.POSITIVE_INFINITY,
|
|
||||||
maxNegX <= 0 ? decodeLongitude(maxNegX) : Double.NEGATIVE_INFINITY,
|
|
||||||
maxPosX >= 0 ? decodeLongitude(maxPosX) : Double.NEGATIVE_INFINITY,
|
|
||||||
GeoEncodingUtils.decodeLatitude(maxY),
|
|
||||||
GeoEncodingUtils.decodeLatitude(minY),
|
|
||||||
WrapLongitude.WRAP
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int maxNeg(int a, int b) {
|
public static int maxNeg(int a, int b) {
|
||||||
|
@ -81,8 +65,4 @@ class SpatialAggregationUtils {
|
||||||
public static int minPos(int a, int b) {
|
public static int minPos(int a, int b) {
|
||||||
return a >= 0 && b >= 0 ? Math.min(a, b) : Math.max(a, b);
|
return a >= 0 && b >= 0 ? Math.min(a, b) : Math.max(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default values are intentionally non-negative/non-positive, so we can mark unassigned values.
|
|
||||||
public static final int DEFAULT_POS = -1;
|
|
||||||
public static final int DEFAULT_NEG = 1;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of cartesian points. It is assumed the points are encoded as longs.
|
* Computes the extent of a set of cartesian points. It is assumed the points are encoded as longs.
|
||||||
* This requires that the planner has planned that points are loaded from the index as doc-values.
|
* This requires that the planner has planned that points are loaded from the index as doc-values.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as four integers: minX, maxX, maxY, minY.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Rectangle class.
|
||||||
|
* Note that this is very different from the six values used for the intermediate state of geo_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,8 +15,9 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of cartesian points. It is assumed that the cartesian points are encoded as WKB BytesRef.
|
* Computes the extent of a set of cartesian points. It is assumed that the cartesian points are encoded as WKB BytesRef.
|
||||||
* This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead.
|
* This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead.
|
||||||
* This is also used for final aggregations and aggregations in the coordinator node,
|
* The intermediate state is the extent of the shapes, encoded as four integers: minX, maxX, maxY, minY.
|
||||||
* even if the local node partial aggregation is done with {@link SpatialExtentCartesianPointDocValuesAggregator}.
|
* The order of the integers is the same as defined in the constructor of the Rectangle class.
|
||||||
|
* Note that this is very different from the six values used for the intermediate state of geo_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import org.elasticsearch.compute.ann.Aggregator;
|
||||||
|
import org.elasticsearch.compute.ann.GroupingAggregator;
|
||||||
|
import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the extent of a set of cartesian shapes read from doc-values, which means they are encoded as an array of integers.
|
||||||
|
* This requires that the planner has planned that the shape extent is loaded from the index as doc-values.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as four integers: minX, maxX, maxY, minY.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Rectangle class.
|
||||||
|
* Note that this is very different from the six values used for the intermediate state of geo_shape geometries.
|
||||||
|
*/
|
||||||
|
@Aggregator(
|
||||||
|
{
|
||||||
|
@IntermediateState(name = "minX", type = "INT"),
|
||||||
|
@IntermediateState(name = "maxX", type = "INT"),
|
||||||
|
@IntermediateState(name = "maxY", type = "INT"),
|
||||||
|
@IntermediateState(name = "minY", type = "INT") }
|
||||||
|
)
|
||||||
|
@GroupingAggregator
|
||||||
|
class SpatialExtentCartesianShapeDocValuesAggregator extends SpatialExtentAggregator {
|
||||||
|
public static SpatialExtentState initSingle() {
|
||||||
|
return new SpatialExtentState(PointType.CARTESIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentGroupingState initGrouping() {
|
||||||
|
return new SpatialExtentGroupingState(PointType.CARTESIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void combine(SpatialExtentState current, int[] values) {
|
||||||
|
current.add(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void combine(SpatialExtentGroupingState current, int groupId, int[] values) {
|
||||||
|
current.add(groupId, values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,8 +13,11 @@ import org.elasticsearch.compute.ann.GroupingAggregator;
|
||||||
import org.elasticsearch.compute.ann.IntermediateState;
|
import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of cartesian shapes. It is assumed that the cartesian shapes are encoded as WKB BytesRef.
|
* Computes the extent of a set of cartesian shapes read from source, which means they are encoded as WKB BytesRef.
|
||||||
* We do not currently support reading shape values or extents from doc values.
|
* This requires that the planner has NOT planned that shapes are loaded from the index as doc-values, but from source instead.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as four integers: minX, maxX, maxY, minY.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Rectangle class.
|
||||||
|
* Note that this is very different from the six values used for the intermediate state of geo_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
|
@ -24,7 +27,7 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
@IntermediateState(name = "minY", type = "INT") }
|
@IntermediateState(name = "minY", type = "INT") }
|
||||||
)
|
)
|
||||||
@GroupingAggregator
|
@GroupingAggregator
|
||||||
class SpatialExtentCartesianShapeAggregator extends SpatialExtentAggregator {
|
class SpatialExtentCartesianShapeSourceValuesAggregator extends SpatialExtentAggregator {
|
||||||
public static SpatialExtentState initSingle() {
|
public static SpatialExtentState initSingle() {
|
||||||
return new SpatialExtentState(PointType.CARTESIAN);
|
return new SpatialExtentState(PointType.CARTESIAN);
|
||||||
}
|
}
|
|
@ -14,15 +14,19 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of geo points. It is assumed the points are encoded as longs.
|
* Computes the extent of a set of geo points. It is assumed the points are encoded as longs.
|
||||||
* This requires that the planner has planned that points are loaded from the index as doc-values.
|
* This requires that the planner has planned that points are loaded from the index as doc-values.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as six integers: top, bottom, negLeft, negRight, posLeft, posRight.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Extent class,
|
||||||
|
* as that is the order in which the values are stored in shape doc-values.
|
||||||
|
* Note that this is very different from the four values used for the intermediate state of cartesian_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
@IntermediateState(name = "minNegX", type = "INT"),
|
@IntermediateState(name = "top", type = "INT"),
|
||||||
@IntermediateState(name = "minPosX", type = "INT"),
|
@IntermediateState(name = "bottom", type = "INT"),
|
||||||
@IntermediateState(name = "maxNegX", type = "INT"),
|
@IntermediateState(name = "negLeft", type = "INT"),
|
||||||
@IntermediateState(name = "maxPosX", type = "INT"),
|
@IntermediateState(name = "negRight", type = "INT"),
|
||||||
@IntermediateState(name = "maxY", type = "INT"),
|
@IntermediateState(name = "posLeft", type = "INT"),
|
||||||
@IntermediateState(name = "minY", type = "INT") }
|
@IntermediateState(name = "posRight", type = "INT") }
|
||||||
)
|
)
|
||||||
@GroupingAggregator
|
@GroupingAggregator
|
||||||
class SpatialExtentGeoPointDocValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
class SpatialExtentGeoPointDocValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
||||||
|
|
|
@ -15,17 +15,19 @@ import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of geo points. It is assumed that the geo points are encoded as WKB BytesRef.
|
* Computes the extent of a set of geo points. It is assumed that the geo points are encoded as WKB BytesRef.
|
||||||
* This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead.
|
* This requires that the planner has NOT planned that points are loaded from the index as doc-values, but from source instead.
|
||||||
* This is also used for final aggregations and aggregations in the coordinator node,
|
* The intermediate state is the extent of the shapes, encoded as six integers: top, bottom, negLeft, negRight, posLeft, posRight.
|
||||||
* even if the local node partial aggregation is done with {@link SpatialExtentGeoPointDocValuesAggregator}.
|
* The order of the integers is the same as defined in the constructor of the Extent class,
|
||||||
|
* as that is the order in which the values are stored in shape doc-values.
|
||||||
|
* Note that this is very different from the four values used for the intermediate state of cartesian_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
@IntermediateState(name = "minNegX", type = "INT"),
|
@IntermediateState(name = "top", type = "INT"),
|
||||||
@IntermediateState(name = "minPosX", type = "INT"),
|
@IntermediateState(name = "bottom", type = "INT"),
|
||||||
@IntermediateState(name = "maxNegX", type = "INT"),
|
@IntermediateState(name = "negLeft", type = "INT"),
|
||||||
@IntermediateState(name = "maxPosX", type = "INT"),
|
@IntermediateState(name = "negRight", type = "INT"),
|
||||||
@IntermediateState(name = "maxY", type = "INT"),
|
@IntermediateState(name = "posLeft", type = "INT"),
|
||||||
@IntermediateState(name = "minY", type = "INT") }
|
@IntermediateState(name = "posRight", type = "INT") }
|
||||||
)
|
)
|
||||||
@GroupingAggregator
|
@GroupingAggregator
|
||||||
class SpatialExtentGeoPointSourceValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
class SpatialExtentGeoPointSourceValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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; you may not use this file except in compliance with the Elastic License
|
||||||
|
* 2.0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import org.elasticsearch.compute.ann.Aggregator;
|
||||||
|
import org.elasticsearch.compute.ann.GroupingAggregator;
|
||||||
|
import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the extent of a set of geo shapes read from doc-values, which means they are encoded as an array of integers.
|
||||||
|
* This requires that the planner has planned that the shape extent is loaded from the index as doc-values.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as six integers: top, bottom, negLeft, negRight, posLeft, posRight.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Extent class,
|
||||||
|
* as that is the order in which the values are stored in shape doc-values.
|
||||||
|
* Note that this is very different from the four values used for the intermediate state of cartesian_shape geometries.
|
||||||
|
*/
|
||||||
|
@Aggregator(
|
||||||
|
{
|
||||||
|
@IntermediateState(name = "top", type = "INT"),
|
||||||
|
@IntermediateState(name = "bottom", type = "INT"),
|
||||||
|
@IntermediateState(name = "negLeft", type = "INT"),
|
||||||
|
@IntermediateState(name = "negRight", type = "INT"),
|
||||||
|
@IntermediateState(name = "posLeft", type = "INT"),
|
||||||
|
@IntermediateState(name = "posRight", type = "INT") }
|
||||||
|
)
|
||||||
|
@GroupingAggregator
|
||||||
|
class SpatialExtentGeoShapeDocValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
||||||
|
public static SpatialExtentStateWrappedLongitudeState initSingle() {
|
||||||
|
return new SpatialExtentStateWrappedLongitudeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SpatialExtentGroupingStateWrappedLongitudeState initGrouping() {
|
||||||
|
return new SpatialExtentGroupingStateWrappedLongitudeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void combine(SpatialExtentStateWrappedLongitudeState current, int[] values) {
|
||||||
|
current.add(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void combine(SpatialExtentGroupingStateWrappedLongitudeState current, int groupId, int[] values) {
|
||||||
|
current.add(groupId, values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,21 +13,24 @@ import org.elasticsearch.compute.ann.GroupingAggregator;
|
||||||
import org.elasticsearch.compute.ann.IntermediateState;
|
import org.elasticsearch.compute.ann.IntermediateState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the extent of a set of geo shapes. It is assumed that the geo shapes are encoded as WKB BytesRef.
|
* Computes the extent of a set of geo shapes read from source, which means they are encoded as WKB BytesRef.
|
||||||
* We do not currently support reading shape values or extents from doc values.
|
* This requires that the planner has NOT planned that shapes are loaded from the index as doc-values, but from source instead.
|
||||||
|
* The intermediate state is the extent of the shapes, encoded as six integers: top, bottom, negLeft, negRight, posLeft, posRight.
|
||||||
|
* The order of the integers is the same as defined in the constructor of the Extent class,
|
||||||
|
* as that is the order in which the values are stored in shape doc-values.
|
||||||
|
* Note that this is very different from the four values used for the intermediate state of cartesian_shape geometries.
|
||||||
*/
|
*/
|
||||||
@Aggregator(
|
@Aggregator(
|
||||||
{
|
{
|
||||||
@IntermediateState(name = "minNegX", type = "INT"),
|
@IntermediateState(name = "top", type = "INT"),
|
||||||
@IntermediateState(name = "minPosX", type = "INT"),
|
@IntermediateState(name = "bottom", type = "INT"),
|
||||||
@IntermediateState(name = "maxNegX", type = "INT"),
|
@IntermediateState(name = "negLeft", type = "INT"),
|
||||||
@IntermediateState(name = "maxPosX", type = "INT"),
|
@IntermediateState(name = "negRight", type = "INT"),
|
||||||
@IntermediateState(name = "maxY", type = "INT"),
|
@IntermediateState(name = "posLeft", type = "INT"),
|
||||||
@IntermediateState(name = "minY", type = "INT") }
|
@IntermediateState(name = "posRight", type = "INT") }
|
||||||
)
|
)
|
||||||
@GroupingAggregator
|
@GroupingAggregator
|
||||||
class SpatialExtentGeoShapeAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
class SpatialExtentGeoShapeSourceValuesAggregator extends SpatialExtentLongitudeWrappingAggregator {
|
||||||
// TODO support non-longitude wrapped geo shapes.
|
|
||||||
public static SpatialExtentStateWrappedLongitudeState initSingle() {
|
public static SpatialExtentStateWrappedLongitudeState initSingle() {
|
||||||
return new SpatialExtentStateWrappedLongitudeState();
|
return new SpatialExtentStateWrappedLongitudeState();
|
||||||
}
|
}
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
import org.elasticsearch.geometry.Rectangle;
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
|
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
@ -53,11 +54,18 @@ final class SpatialExtentGroupingState extends AbstractArrayState {
|
||||||
) {
|
) {
|
||||||
for (int i = 0; i < selected.getPositionCount(); i++) {
|
for (int i = 0; i < selected.getPositionCount(); i++) {
|
||||||
int group = selected.getInt(i);
|
int group = selected.getInt(i);
|
||||||
assert hasValue(group);
|
if (hasValue(group)) {
|
||||||
minXsBuilder.appendInt(minXs.get(group));
|
minXsBuilder.appendInt(minXs.get(group));
|
||||||
maxXsBuilder.appendInt(maxXs.get(group));
|
maxXsBuilder.appendInt(maxXs.get(group));
|
||||||
maxYsBuilder.appendInt(maxYs.get(group));
|
maxYsBuilder.appendInt(maxYs.get(group));
|
||||||
minYsBuilder.appendInt(minYs.get(group));
|
minYsBuilder.appendInt(minYs.get(group));
|
||||||
|
} else {
|
||||||
|
// TODO: Should we add Nulls here instead?
|
||||||
|
minXsBuilder.appendInt(Integer.MAX_VALUE);
|
||||||
|
maxXsBuilder.appendInt(Integer.MIN_VALUE);
|
||||||
|
maxYsBuilder.appendInt(Integer.MIN_VALUE);
|
||||||
|
minYsBuilder.appendInt(Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
blocks[offset + 0] = minXsBuilder.build();
|
blocks[offset + 0] = minXsBuilder.build();
|
||||||
blocks[offset + 1] = maxXsBuilder.build();
|
blocks[offset + 1] = maxXsBuilder.build();
|
||||||
|
@ -66,6 +74,32 @@ final class SpatialExtentGroupingState extends AbstractArrayState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when extents are extracted from the doc-values field by the {@link GeometryDocValueReader}.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in the ST_EXTENT aggregation.
|
||||||
|
*/
|
||||||
|
public void add(int groupId, int[] values) {
|
||||||
|
if (values.length == 6) {
|
||||||
|
// Values are stored according to the order defined in the Extent class
|
||||||
|
int top = values[0];
|
||||||
|
int bottom = values[1];
|
||||||
|
int negLeft = values[2];
|
||||||
|
int negRight = values[3];
|
||||||
|
int posLeft = values[4];
|
||||||
|
int posRight = values[5];
|
||||||
|
add(groupId, Math.min(negLeft, posLeft), Math.max(negRight, posRight), top, bottom);
|
||||||
|
} else if (values.length == 4) {
|
||||||
|
// Values are stored according to the order defined in the Rectangle class
|
||||||
|
int minX = values[0];
|
||||||
|
int maxX = values[1];
|
||||||
|
int maxY = values[2];
|
||||||
|
int minY = values[3];
|
||||||
|
add(groupId, minX, maxX, maxY, minY);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Expected 4 or 6 values, got " + values.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void add(int groupId, Geometry geometry) {
|
public void add(int groupId, Geometry geometry) {
|
||||||
ensureCapacity(groupId);
|
ensureCapacity(groupId);
|
||||||
pointType.computeEnvelope(geometry)
|
pointType.computeEnvelope(geometry)
|
||||||
|
@ -80,6 +114,10 @@ final class SpatialExtentGroupingState extends AbstractArrayState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when the field is a geo_point or cartesian_point and is loaded from doc-values.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in a spatial aggregation.
|
||||||
|
*/
|
||||||
public void add(int groupId, long encoded) {
|
public void add(int groupId, long encoded) {
|
||||||
int x = pointType.extractX(encoded);
|
int x = pointType.extractX(encoded);
|
||||||
int y = pointType.extractY(encoded);
|
int y = pointType.extractY(encoded);
|
||||||
|
|
|
@ -19,20 +19,23 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
|
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import static org.elasticsearch.compute.aggregation.spatial.SpatialExtentStateWrappedLongitudeState.asRectangle;
|
||||||
|
|
||||||
final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArrayState implements GroupingAggregatorState {
|
final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArrayState implements GroupingAggregatorState {
|
||||||
// Only geo points support longitude wrapping.
|
// Only geo points support longitude wrapping.
|
||||||
private static final PointType POINT_TYPE = PointType.GEO;
|
private static final PointType POINT_TYPE = PointType.GEO;
|
||||||
private IntArray minNegXs;
|
private IntArray tops;
|
||||||
private IntArray minPosXs;
|
private IntArray bottoms;
|
||||||
private IntArray maxNegXs;
|
private IntArray negLefts;
|
||||||
private IntArray maxPosXs;
|
private IntArray negRights;
|
||||||
private IntArray maxYs;
|
private IntArray posLefts;
|
||||||
private IntArray minYs;
|
private IntArray posRights;
|
||||||
|
|
||||||
private GeoPointEnvelopeVisitor geoPointVisitor = new GeoPointEnvelopeVisitor();
|
private final SpatialEnvelopeVisitor.GeoPointVisitor geoPointVisitor;
|
||||||
|
|
||||||
SpatialExtentGroupingStateWrappedLongitudeState() {
|
SpatialExtentGroupingStateWrappedLongitudeState() {
|
||||||
this(BigArrays.NON_RECYCLING_INSTANCE);
|
this(BigArrays.NON_RECYCLING_INSTANCE);
|
||||||
|
@ -40,44 +43,52 @@ final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArra
|
||||||
|
|
||||||
SpatialExtentGroupingStateWrappedLongitudeState(BigArrays bigArrays) {
|
SpatialExtentGroupingStateWrappedLongitudeState(BigArrays bigArrays) {
|
||||||
super(bigArrays);
|
super(bigArrays);
|
||||||
this.minNegXs = bigArrays.newIntArray(0, false);
|
this.tops = bigArrays.newIntArray(0, false);
|
||||||
this.minPosXs = bigArrays.newIntArray(0, false);
|
this.bottoms = bigArrays.newIntArray(0, false);
|
||||||
this.maxNegXs = bigArrays.newIntArray(0, false);
|
this.negLefts = bigArrays.newIntArray(0, false);
|
||||||
this.maxPosXs = bigArrays.newIntArray(0, false);
|
this.negRights = bigArrays.newIntArray(0, false);
|
||||||
this.maxYs = bigArrays.newIntArray(0, false);
|
this.posLefts = bigArrays.newIntArray(0, false);
|
||||||
this.minYs = bigArrays.newIntArray(0, false);
|
this.posRights = bigArrays.newIntArray(0, false);
|
||||||
enableGroupIdTracking(new SeenGroupIds.Empty());
|
enableGroupIdTracking(new SeenGroupIds.Empty());
|
||||||
|
this.geoPointVisitor = new SpatialEnvelopeVisitor.GeoPointVisitor(SpatialEnvelopeVisitor.WrapLongitude.WRAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void toIntermediate(Block[] blocks, int offset, IntVector selected, DriverContext driverContext) {
|
public void toIntermediate(Block[] blocks, int offset, IntVector selected, DriverContext driverContext) {
|
||||||
assert blocks.length >= offset;
|
assert blocks.length >= offset;
|
||||||
try (
|
try (
|
||||||
var minNegXsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var topsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
var minPosXsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var bottomsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
var maxNegXsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var negLeftsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
var maxPosXsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var negRightsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
var maxYsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var posLeftsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
var minYsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
var posRightsBuilder = driverContext.blockFactory().newIntBlockBuilder(selected.getPositionCount());
|
||||||
) {
|
) {
|
||||||
for (int i = 0; i < selected.getPositionCount(); i++) {
|
for (int i = 0; i < selected.getPositionCount(); i++) {
|
||||||
int group = selected.getInt(i);
|
int group = selected.getInt(i);
|
||||||
assert hasValue(group);
|
if (hasValue(group)) {
|
||||||
assert minNegXs.get(group) <= 0 == maxNegXs.get(group) <= 0;
|
topsBuilder.appendInt(tops.get(group));
|
||||||
assert minPosXs.get(group) >= 0 == maxPosXs.get(group) >= 0;
|
bottomsBuilder.appendInt(bottoms.get(group));
|
||||||
minNegXsBuilder.appendInt(minNegXs.get(group));
|
negLeftsBuilder.appendInt(negLefts.get(group));
|
||||||
minPosXsBuilder.appendInt(minPosXs.get(group));
|
negRightsBuilder.appendInt(negRights.get(group));
|
||||||
maxNegXsBuilder.appendInt(maxNegXs.get(group));
|
posLeftsBuilder.appendInt(posLefts.get(group));
|
||||||
maxPosXsBuilder.appendInt(maxPosXs.get(group));
|
posRightsBuilder.appendInt(posRights.get(group));
|
||||||
maxYsBuilder.appendInt(maxYs.get(group));
|
} else {
|
||||||
minYsBuilder.appendInt(minYs.get(group));
|
// TODO: Should we add Nulls here instead?
|
||||||
|
topsBuilder.appendInt(Integer.MIN_VALUE);
|
||||||
|
bottomsBuilder.appendInt(Integer.MAX_VALUE);
|
||||||
|
negLeftsBuilder.appendInt(Integer.MAX_VALUE);
|
||||||
|
negRightsBuilder.appendInt(Integer.MIN_VALUE);
|
||||||
|
posLeftsBuilder.appendInt(Integer.MAX_VALUE);
|
||||||
|
posRightsBuilder.appendInt(Integer.MIN_VALUE);
|
||||||
}
|
}
|
||||||
blocks[offset + 0] = minNegXsBuilder.build();
|
}
|
||||||
blocks[offset + 1] = minPosXsBuilder.build();
|
blocks[offset + 0] = topsBuilder.build();
|
||||||
blocks[offset + 2] = maxNegXsBuilder.build();
|
blocks[offset + 1] = bottomsBuilder.build();
|
||||||
blocks[offset + 3] = maxPosXsBuilder.build();
|
blocks[offset + 2] = negLeftsBuilder.build();
|
||||||
blocks[offset + 4] = maxYsBuilder.build();
|
blocks[offset + 3] = negRightsBuilder.build();
|
||||||
blocks[offset + 5] = minYsBuilder.build();
|
blocks[offset + 4] = posLeftsBuilder.build();
|
||||||
|
blocks[offset + 5] = posRightsBuilder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +98,12 @@ final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArra
|
||||||
if (geo.visit(new SpatialEnvelopeVisitor(geoPointVisitor))) {
|
if (geo.visit(new SpatialEnvelopeVisitor(geoPointVisitor))) {
|
||||||
add(
|
add(
|
||||||
groupId,
|
groupId,
|
||||||
SpatialAggregationUtils.encodeNegativeLongitude(geoPointVisitor.getMinNegX()),
|
POINT_TYPE.encoder().encodeY(geoPointVisitor.getTop()),
|
||||||
SpatialAggregationUtils.encodePositiveLongitude(geoPointVisitor.getMinPosX()),
|
POINT_TYPE.encoder().encodeY(geoPointVisitor.getBottom()),
|
||||||
SpatialAggregationUtils.encodeNegativeLongitude(geoPointVisitor.getMaxNegX()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getNegLeft()),
|
||||||
SpatialAggregationUtils.encodePositiveLongitude(geoPointVisitor.getMaxPosX()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getNegRight()),
|
||||||
POINT_TYPE.encoder().encodeY(geoPointVisitor.getMaxY()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getPosLeft()),
|
||||||
POINT_TYPE.encoder().encodeY(geoPointVisitor.getMinY())
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getPosRight())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,53 +113,73 @@ final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArra
|
||||||
if (inState.hasValue(inPosition)) {
|
if (inState.hasValue(inPosition)) {
|
||||||
add(
|
add(
|
||||||
groupId,
|
groupId,
|
||||||
inState.minNegXs.get(inPosition),
|
inState.tops.get(inPosition),
|
||||||
inState.minPosXs.get(inPosition),
|
inState.bottoms.get(inPosition),
|
||||||
inState.maxNegXs.get(inPosition),
|
inState.negLefts.get(inPosition),
|
||||||
inState.maxPosXs.get(inPosition),
|
inState.negRights.get(inPosition),
|
||||||
inState.maxYs.get(inPosition),
|
inState.posLefts.get(inPosition),
|
||||||
inState.minYs.get(inPosition)
|
inState.posRights.get(inPosition)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when the field is a geo_point or cartesian_point and is loaded from doc-values.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in a spatial aggregation.
|
||||||
|
*/
|
||||||
public void add(int groupId, long encoded) {
|
public void add(int groupId, long encoded) {
|
||||||
int x = POINT_TYPE.extractX(encoded);
|
int x = POINT_TYPE.extractX(encoded);
|
||||||
int y = POINT_TYPE.extractY(encoded);
|
int y = POINT_TYPE.extractY(encoded);
|
||||||
add(groupId, x, x, x, x, y, y);
|
add(groupId, y, y, x, x, x, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(int groupId, int minNegX, int minPosX, int maxNegX, int maxPosX, int maxY, int minY) {
|
/**
|
||||||
|
* This method is used when extents are extracted from the doc-values field by the {@link GeometryDocValueReader}.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in the ST_EXTENT aggregation.
|
||||||
|
*/
|
||||||
|
public void add(int groupId, int[] values) {
|
||||||
|
if (values.length != 6) {
|
||||||
|
throw new IllegalArgumentException("Expected 6 values, got " + values.length);
|
||||||
|
}
|
||||||
|
// Values are stored according to the order defined in the Extent class
|
||||||
|
int top = values[0];
|
||||||
|
int bottom = values[1];
|
||||||
|
int negLeft = values[2];
|
||||||
|
int negRight = values[3];
|
||||||
|
int posLeft = values[4];
|
||||||
|
int posRight = values[5];
|
||||||
|
add(groupId, top, bottom, negLeft, negRight, posLeft, posRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int groupId, int top, int bottom, int negLeft, int negRight, int posLeft, int posRight) {
|
||||||
ensureCapacity(groupId);
|
ensureCapacity(groupId);
|
||||||
if (hasValue(groupId)) {
|
if (hasValue(groupId)) {
|
||||||
minNegXs.set(groupId, Math.min(minNegXs.get(groupId), minNegX));
|
tops.set(groupId, Math.max(tops.get(groupId), top));
|
||||||
minPosXs.set(groupId, SpatialAggregationUtils.minPos(minPosXs.get(groupId), minPosX));
|
bottoms.set(groupId, Math.min(bottoms.get(groupId), bottom));
|
||||||
maxNegXs.set(groupId, SpatialAggregationUtils.maxNeg(maxNegXs.get(groupId), maxNegX));
|
negLefts.set(groupId, Math.min(negLefts.get(groupId), negLeft));
|
||||||
maxPosXs.set(groupId, Math.max(maxPosXs.get(groupId), maxPosX));
|
negRights.set(groupId, SpatialAggregationUtils.maxNeg(negRights.get(groupId), negRight));
|
||||||
maxYs.set(groupId, Math.max(maxYs.get(groupId), maxY));
|
posLefts.set(groupId, SpatialAggregationUtils.minPos(posLefts.get(groupId), posLeft));
|
||||||
minYs.set(groupId, Math.min(minYs.get(groupId), minY));
|
posRights.set(groupId, Math.max(posRights.get(groupId), posRight));
|
||||||
} else {
|
} else {
|
||||||
minNegXs.set(groupId, minNegX);
|
tops.set(groupId, top);
|
||||||
minPosXs.set(groupId, minPosX);
|
bottoms.set(groupId, bottom);
|
||||||
maxNegXs.set(groupId, maxNegX);
|
negLefts.set(groupId, negLeft);
|
||||||
maxPosXs.set(groupId, maxPosX);
|
negRights.set(groupId, negRight);
|
||||||
maxYs.set(groupId, maxY);
|
posLefts.set(groupId, posLeft);
|
||||||
minYs.set(groupId, minY);
|
posRights.set(groupId, posRight);
|
||||||
}
|
}
|
||||||
assert minNegX <= 0 == maxNegX <= 0 : "minNegX=" + minNegX + " maxNegX=" + maxNegX;
|
|
||||||
assert minPosX >= 0 == maxPosX >= 0 : "minPosX=" + minPosX + " maxPosX=" + maxPosX;
|
|
||||||
trackGroupId(groupId);
|
trackGroupId(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureCapacity(int groupId) {
|
private void ensureCapacity(int groupId) {
|
||||||
long requiredSize = groupId + 1;
|
long requiredSize = groupId + 1;
|
||||||
if (minNegXs.size() < requiredSize) {
|
if (negLefts.size() < requiredSize) {
|
||||||
minNegXs = bigArrays.grow(minNegXs, requiredSize);
|
tops = bigArrays.grow(tops, requiredSize);
|
||||||
minPosXs = bigArrays.grow(minPosXs, requiredSize);
|
bottoms = bigArrays.grow(bottoms, requiredSize);
|
||||||
maxNegXs = bigArrays.grow(maxNegXs, requiredSize);
|
negLefts = bigArrays.grow(negLefts, requiredSize);
|
||||||
maxPosXs = bigArrays.grow(maxPosXs, requiredSize);
|
negRights = bigArrays.grow(negRights, requiredSize);
|
||||||
minYs = bigArrays.grow(minYs, requiredSize);
|
posLefts = bigArrays.grow(posLefts, requiredSize);
|
||||||
maxYs = bigArrays.grow(maxYs, requiredSize);
|
posRights = bigArrays.grow(posRights, requiredSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,13 +191,13 @@ final class SpatialExtentGroupingStateWrappedLongitudeState extends AbstractArra
|
||||||
builder.appendBytesRef(
|
builder.appendBytesRef(
|
||||||
new BytesRef(
|
new BytesRef(
|
||||||
WellKnownBinary.toWKB(
|
WellKnownBinary.toWKB(
|
||||||
SpatialAggregationUtils.asRectangle(
|
asRectangle(
|
||||||
minNegXs.get(si),
|
tops.get(si),
|
||||||
minPosXs.get(si),
|
bottoms.get(si),
|
||||||
maxNegXs.get(si),
|
negLefts.get(si),
|
||||||
maxPosXs.get(si),
|
negRights.get(si),
|
||||||
maxYs.get(si),
|
posLefts.get(si),
|
||||||
minYs.get(si)
|
posRights.get(si)
|
||||||
),
|
),
|
||||||
ByteOrder.LITTLE_ENDIAN
|
ByteOrder.LITTLE_ENDIAN
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,27 +16,27 @@ import org.elasticsearch.compute.operator.DriverContext;
|
||||||
abstract class SpatialExtentLongitudeWrappingAggregator {
|
abstract class SpatialExtentLongitudeWrappingAggregator {
|
||||||
public static void combineIntermediate(
|
public static void combineIntermediate(
|
||||||
SpatialExtentStateWrappedLongitudeState current,
|
SpatialExtentStateWrappedLongitudeState current,
|
||||||
int minNegX,
|
int top,
|
||||||
int minPosX,
|
int bottom,
|
||||||
int maxNegX,
|
int negLeft,
|
||||||
int maxPosX,
|
int negRight,
|
||||||
int maxY,
|
int posLeft,
|
||||||
int minY
|
int posRight
|
||||||
) {
|
) {
|
||||||
current.add(minNegX, minPosX, maxNegX, maxPosX, maxY, minY);
|
current.add(top, bottom, negLeft, negRight, posLeft, posRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void combineIntermediate(
|
public static void combineIntermediate(
|
||||||
SpatialExtentGroupingStateWrappedLongitudeState current,
|
SpatialExtentGroupingStateWrappedLongitudeState current,
|
||||||
int groupId,
|
int groupId,
|
||||||
int minNegX,
|
int top,
|
||||||
int minPosX,
|
int bottom,
|
||||||
int maxNegX,
|
int negLeft,
|
||||||
int maxPosX,
|
int negRight,
|
||||||
int maxY,
|
int posLeft,
|
||||||
int minY
|
int posRight
|
||||||
) {
|
) {
|
||||||
current.add(groupId, minNegX, minPosX, maxNegX, maxPosX, maxY, minY);
|
current.add(groupId, top, bottom, negLeft, negRight, posLeft, posRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Block evaluateFinal(SpatialExtentStateWrappedLongitudeState state, DriverContext driverContext) {
|
public static Block evaluateFinal(SpatialExtentStateWrappedLongitudeState state, DriverContext driverContext) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.geometry.Geometry;
|
||||||
import org.elasticsearch.geometry.Rectangle;
|
import org.elasticsearch.geometry.Rectangle;
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
||||||
|
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
@ -55,6 +56,32 @@ final class SpatialExtentState implements AggregatorState {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when extents are extracted from the doc-values field by the {@link GeometryDocValueReader}.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in the ST_EXTENT aggregation.
|
||||||
|
*/
|
||||||
|
public void add(int[] values) {
|
||||||
|
if (values.length == 6) {
|
||||||
|
// Values are stored according to the order defined in the Extent class
|
||||||
|
int top = values[0];
|
||||||
|
int bottom = values[1];
|
||||||
|
int negLeft = values[2];
|
||||||
|
int negRight = values[3];
|
||||||
|
int posLeft = values[4];
|
||||||
|
int posRight = values[5];
|
||||||
|
add(Math.min(negLeft, posLeft), Math.max(negRight, posRight), top, bottom);
|
||||||
|
} else if (values.length == 4) {
|
||||||
|
// Values are stored according to the order defined in the Rectangle class
|
||||||
|
int minX = values[0];
|
||||||
|
int maxX = values[1];
|
||||||
|
int maxY = values[2];
|
||||||
|
int minY = values[3];
|
||||||
|
add(minX, maxX, maxY, minY);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Expected 4 or 6 values, got " + values.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void add(int minX, int maxX, int maxY, int minY) {
|
public void add(int minX, int maxX, int maxY, int minY) {
|
||||||
seen = true;
|
seen = true;
|
||||||
this.minX = Math.min(this.minX, minX);
|
this.minX = Math.min(this.minX, minX);
|
||||||
|
@ -63,6 +90,10 @@ final class SpatialExtentState implements AggregatorState {
|
||||||
this.minY = Math.min(this.minY, minY);
|
this.minY = Math.min(this.minY, minY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when the field is a geo_point or cartesian_point and is loaded from doc-values.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in a spatial aggregation.
|
||||||
|
*/
|
||||||
public void add(long encoded) {
|
public void add(long encoded) {
|
||||||
int x = pointType.extractX(encoded);
|
int x = pointType.extractX(encoded);
|
||||||
int y = pointType.extractY(encoded);
|
int y = pointType.extractY(encoded);
|
||||||
|
|
|
@ -7,28 +7,35 @@
|
||||||
|
|
||||||
package org.elasticsearch.compute.aggregation.spatial;
|
package org.elasticsearch.compute.aggregation.spatial;
|
||||||
|
|
||||||
|
import org.apache.lucene.geo.GeoEncodingUtils;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.compute.aggregation.AggregatorState;
|
import org.elasticsearch.compute.aggregation.AggregatorState;
|
||||||
import org.elasticsearch.compute.data.Block;
|
import org.elasticsearch.compute.data.Block;
|
||||||
import org.elasticsearch.compute.operator.DriverContext;
|
import org.elasticsearch.compute.operator.DriverContext;
|
||||||
import org.elasticsearch.geometry.Geometry;
|
import org.elasticsearch.geometry.Geometry;
|
||||||
|
import org.elasticsearch.geometry.Rectangle;
|
||||||
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
||||||
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
|
import org.elasticsearch.lucene.spatial.GeometryDocValueReader;
|
||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import static org.elasticsearch.compute.aggregation.spatial.SpatialAggregationUtils.decodeLongitude;
|
||||||
|
|
||||||
final class SpatialExtentStateWrappedLongitudeState implements AggregatorState {
|
final class SpatialExtentStateWrappedLongitudeState implements AggregatorState {
|
||||||
// Only geo points support longitude wrapping.
|
// Only geo points support longitude wrapping.
|
||||||
private static final PointType POINT_TYPE = PointType.GEO;
|
private static final PointType POINT_TYPE = PointType.GEO;
|
||||||
private boolean seen = false;
|
private boolean seen = false;
|
||||||
private int minNegX = SpatialAggregationUtils.DEFAULT_NEG;
|
private int top = Integer.MIN_VALUE;
|
||||||
private int minPosX = SpatialAggregationUtils.DEFAULT_POS;
|
private int bottom = Integer.MAX_VALUE;
|
||||||
private int maxNegX = SpatialAggregationUtils.DEFAULT_NEG;
|
private int negLeft = Integer.MAX_VALUE;
|
||||||
private int maxPosX = SpatialAggregationUtils.DEFAULT_POS;
|
private int negRight = Integer.MIN_VALUE;
|
||||||
private int maxY = Integer.MIN_VALUE;
|
private int posLeft = Integer.MAX_VALUE;
|
||||||
private int minY = Integer.MAX_VALUE;
|
private int posRight = Integer.MIN_VALUE;
|
||||||
|
|
||||||
private GeoPointEnvelopeVisitor geoPointVisitor = new GeoPointEnvelopeVisitor();
|
private final SpatialEnvelopeVisitor.GeoPointVisitor geoPointVisitor = new SpatialEnvelopeVisitor.GeoPointVisitor(
|
||||||
|
SpatialEnvelopeVisitor.WrapLongitude.WRAP
|
||||||
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
|
@ -37,44 +44,64 @@ final class SpatialExtentStateWrappedLongitudeState implements AggregatorState {
|
||||||
public void toIntermediate(Block[] blocks, int offset, DriverContext driverContext) {
|
public void toIntermediate(Block[] blocks, int offset, DriverContext driverContext) {
|
||||||
assert blocks.length >= offset + 6;
|
assert blocks.length >= offset + 6;
|
||||||
var blockFactory = driverContext.blockFactory();
|
var blockFactory = driverContext.blockFactory();
|
||||||
blocks[offset + 0] = blockFactory.newConstantIntBlockWith(minNegX, 1);
|
blocks[offset + 0] = blockFactory.newConstantIntBlockWith(top, 1);
|
||||||
blocks[offset + 1] = blockFactory.newConstantIntBlockWith(minPosX, 1);
|
blocks[offset + 1] = blockFactory.newConstantIntBlockWith(bottom, 1);
|
||||||
blocks[offset + 2] = blockFactory.newConstantIntBlockWith(maxNegX, 1);
|
blocks[offset + 2] = blockFactory.newConstantIntBlockWith(negLeft, 1);
|
||||||
blocks[offset + 3] = blockFactory.newConstantIntBlockWith(maxPosX, 1);
|
blocks[offset + 3] = blockFactory.newConstantIntBlockWith(negRight, 1);
|
||||||
blocks[offset + 4] = blockFactory.newConstantIntBlockWith(maxY, 1);
|
blocks[offset + 4] = blockFactory.newConstantIntBlockWith(posLeft, 1);
|
||||||
blocks[offset + 5] = blockFactory.newConstantIntBlockWith(minY, 1);
|
blocks[offset + 5] = blockFactory.newConstantIntBlockWith(posRight, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Geometry geo) {
|
public void add(Geometry geo) {
|
||||||
geoPointVisitor.reset();
|
geoPointVisitor.reset();
|
||||||
if (geo.visit(new SpatialEnvelopeVisitor(geoPointVisitor))) {
|
if (geo.visit(new SpatialEnvelopeVisitor(geoPointVisitor))) {
|
||||||
add(
|
add(
|
||||||
SpatialAggregationUtils.encodeNegativeLongitude(geoPointVisitor.getMinNegX()),
|
POINT_TYPE.encoder().encodeY(geoPointVisitor.getTop()),
|
||||||
SpatialAggregationUtils.encodePositiveLongitude(geoPointVisitor.getMinPosX()),
|
POINT_TYPE.encoder().encodeY(geoPointVisitor.getBottom()),
|
||||||
SpatialAggregationUtils.encodeNegativeLongitude(geoPointVisitor.getMaxNegX()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getNegLeft()),
|
||||||
SpatialAggregationUtils.encodePositiveLongitude(geoPointVisitor.getMaxPosX()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getNegRight()),
|
||||||
POINT_TYPE.encoder().encodeY(geoPointVisitor.getMaxY()),
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getPosLeft()),
|
||||||
POINT_TYPE.encoder().encodeY(geoPointVisitor.getMinY())
|
SpatialAggregationUtils.encodeLongitude(geoPointVisitor.getPosRight())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(int minNegX, int minPosX, int maxNegX, int maxPosX, int maxY, int minY) {
|
/**
|
||||||
seen = true;
|
* This method is used when extents are extracted from the doc-values field by the {@link GeometryDocValueReader}.
|
||||||
this.minNegX = Math.min(this.minNegX, minNegX);
|
* This optimization is enabled when the field has doc-values and is only used in the ST_EXTENT aggregation.
|
||||||
this.minPosX = SpatialAggregationUtils.minPos(this.minPosX, minPosX);
|
*/
|
||||||
this.maxNegX = SpatialAggregationUtils.maxNeg(this.maxNegX, maxNegX);
|
public void add(int[] values) {
|
||||||
this.maxPosX = Math.max(this.maxPosX, maxPosX);
|
if (values.length != 6) {
|
||||||
this.maxY = Math.max(this.maxY, maxY);
|
throw new IllegalArgumentException("Expected 6 values, got " + values.length);
|
||||||
this.minY = Math.min(this.minY, minY);
|
}
|
||||||
assert this.minNegX <= 0 == this.maxNegX <= 0 : "minNegX=" + this.minNegX + " maxNegX=" + this.maxNegX;
|
// Values are stored according to the order defined in the Extent class
|
||||||
assert this.minPosX >= 0 == this.maxPosX >= 0 : "minPosX=" + this.minPosX + " maxPosX=" + this.maxPosX;
|
int top = values[0];
|
||||||
|
int bottom = values[1];
|
||||||
|
int negLeft = values[2];
|
||||||
|
int negRight = values[3];
|
||||||
|
int posLeft = values[4];
|
||||||
|
int posRight = values[5];
|
||||||
|
add(top, bottom, negLeft, negRight, posLeft, posRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void add(int top, int bottom, int negLeft, int negRight, int posLeft, int posRight) {
|
||||||
|
seen = true;
|
||||||
|
this.top = Math.max(this.top, top);
|
||||||
|
this.bottom = Math.min(this.bottom, bottom);
|
||||||
|
this.negLeft = Math.min(this.negLeft, negLeft);
|
||||||
|
this.negRight = SpatialAggregationUtils.maxNeg(this.negRight, negRight);
|
||||||
|
this.posLeft = SpatialAggregationUtils.minPos(this.posLeft, posLeft);
|
||||||
|
this.posRight = Math.max(this.posRight, posRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is used when the field is a geo_point or cartesian_point and is loaded from doc-values.
|
||||||
|
* This optimization is enabled when the field has doc-values and is only used in a spatial aggregation.
|
||||||
|
*/
|
||||||
public void add(long encoded) {
|
public void add(long encoded) {
|
||||||
int x = POINT_TYPE.extractX(encoded);
|
int x = POINT_TYPE.extractX(encoded);
|
||||||
int y = POINT_TYPE.extractY(encoded);
|
int y = POINT_TYPE.extractY(encoded);
|
||||||
add(x, x, x, x, y, y);
|
add(y, y, x, x, x, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block toBlock(DriverContext driverContext) {
|
public Block toBlock(DriverContext driverContext) {
|
||||||
|
@ -83,9 +110,18 @@ final class SpatialExtentStateWrappedLongitudeState implements AggregatorState {
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] toWKB() {
|
private byte[] toWKB() {
|
||||||
return WellKnownBinary.toWKB(
|
return WellKnownBinary.toWKB(asRectangle(top, bottom, negLeft, negRight, posLeft, posRight), ByteOrder.LITTLE_ENDIAN);
|
||||||
SpatialAggregationUtils.asRectangle(minNegX, minPosX, maxNegX, maxPosX, maxY, minY),
|
}
|
||||||
ByteOrder.LITTLE_ENDIAN
|
|
||||||
|
static Rectangle asRectangle(int top, int bottom, int negLeft, int negRight, int posLeft, int posRight) {
|
||||||
|
return SpatialEnvelopeVisitor.GeoPointVisitor.getResult(
|
||||||
|
GeoEncodingUtils.decodeLatitude(top),
|
||||||
|
GeoEncodingUtils.decodeLatitude(bottom),
|
||||||
|
negLeft <= 0 ? decodeLongitude(negLeft) : Double.POSITIVE_INFINITY,
|
||||||
|
negRight <= 0 ? decodeLongitude(negRight) : Double.NEGATIVE_INFINITY,
|
||||||
|
posLeft >= 0 ? decodeLongitude(posLeft) : Double.POSITIVE_INFINITY,
|
||||||
|
posRight >= 0 ? decodeLongitude(posRight) : Double.NEGATIVE_INFINITY,
|
||||||
|
SpatialEnvelopeVisitor.WrapLongitude.WRAP
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,6 +524,8 @@ POINT (42.97109629958868 14.7552534006536) | 1
|
||||||
|
|
||||||
stExtentSingleGeoPoint
|
stExtentSingleGeoPoint
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
ROW point = TO_GEOPOINT("POINT(42.97109629958868 14.7552534006536)")
|
ROW point = TO_GEOPOINT("POINT(42.97109629958868 14.7552534006536)")
|
||||||
| STATS extent = ST_EXTENT_AGG(point)
|
| STATS extent = ST_EXTENT_AGG(point)
|
||||||
;
|
;
|
||||||
|
@ -534,6 +536,8 @@ BBOX(42.97109629958868, 42.97109629958868, 14.7552534006536, 14.7552534006536)
|
||||||
|
|
||||||
stExtentMultipleGeoPoints
|
stExtentMultipleGeoPoints
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
// tag::st_extent_agg-airports[]
|
// tag::st_extent_agg-airports[]
|
||||||
FROM airports
|
FROM airports
|
||||||
| WHERE country == "India"
|
| WHERE country == "India"
|
||||||
|
@ -547,35 +551,257 @@ BBOX (70.77995480038226, 91.5882289968431, 33.9830909203738, 8.47650992218405)
|
||||||
// end::st_extent_agg-airports-result[]
|
// end::st_extent_agg-airports-result[]
|
||||||
;
|
;
|
||||||
|
|
||||||
stExtentMultipleGeoPointsNoDocValues
|
stExtentMultipleGeoPointsCount
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
FROM airports_no_doc_values | WHERE country == "India" | STATS extent = ST_EXTENT_AGG(location)
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airports
|
||||||
|
| WHERE country == "India"
|
||||||
|
| STATS extent = ST_EXTENT_AGG(location), count = COUNT()
|
||||||
;
|
;
|
||||||
|
|
||||||
extent:geo_shape
|
extent:geo_shape | count:long
|
||||||
BBOX (70.77995480038226, 91.5882289968431, 33.9830909203738, 8.47650992218405)
|
BBOX (70.77995480038226, 91.5882289968431, 33.9830909203738, 8.47650992218405) | 50
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentMultipleGeoPointsCountNoDocValues
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airports_no_doc_values
|
||||||
|
| WHERE country == "India"
|
||||||
|
| STATS extent = ST_EXTENT_AGG(location), count = COUNT()
|
||||||
|
;
|
||||||
|
|
||||||
|
extent:geo_shape | count:long
|
||||||
|
BBOX (70.77995480038226, 91.5882289968431, 33.9830909203738, 8.47650992218405) | 50
|
||||||
;
|
;
|
||||||
|
|
||||||
stExtentMultipleGeoPointGrouping
|
stExtentMultipleGeoPointGrouping
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
FROM airports | STATS extent = ST_EXTENT_AGG(location) BY country | SORT country | LIMIT 3
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airports
|
||||||
|
| STATS extent = ST_EXTENT_AGG(location), count = COUNT() BY country
|
||||||
|
| SORT count DESC, country ASC
|
||||||
|
| LIMIT 5
|
||||||
;
|
;
|
||||||
|
|
||||||
extent:geo_shape | country:keyword
|
extent:geo_shape | count:long | country:keyword
|
||||||
BBOX (69.2100736219436, 69.2100736219436, 34.56339786294848, 34.56339786294848) | Afghanistan
|
BBOX (-159.34908430092037, -71.01640669628978, 64.81809809803963, 19.71479767933488) | 129 | United States
|
||||||
BBOX (19.715032372623682, 19.715032372623682, 41.4208514476195, 41.4208514476195) | Albania
|
BBOX (70.77995480038226, 91.5882289968431, 33.9830909203738, 8.47650992218405) | 50 | India
|
||||||
BBOX (-0.6067969836294651, 6.621946580708027, 36.69972063973546, 35.62027471605688) | Algeria
|
BBOX (-117.19751106575131, -86.87441730871797, 32.833958650007844, 14.791128113865852) | 45 | Mexico
|
||||||
|
BBOX (76.01301474496722, 130.45620465651155, 46.84301500674337, 18.309095981530845) | 41 | China
|
||||||
|
BBOX (-135.07621010765433, -52.743333745747805, 63.751152316108346, 43.163360520266) | 37 | Canada
|
||||||
;
|
;
|
||||||
|
|
||||||
stExtentGeoShapes
|
stExtentGeoShapes
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
FROM airport_city_boundaries | WHERE region == "City of New York" | STATS extent = ST_EXTENT_AGG(city_boundary)
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_boundary)
|
||||||
;
|
;
|
||||||
|
|
||||||
extent:geo_shape
|
extent:geo_shape
|
||||||
BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321)
|
BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
stExtentGeoPoints
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_location)
|
||||||
|
;
|
||||||
|
|
||||||
|
extent:geo_shape
|
||||||
|
BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentGeoShapesAndPoints
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| STATS extent_shapes = ST_EXTENT_AGG(city_boundary), extent_points = ST_EXTENT_AGG(city_location)
|
||||||
|
;
|
||||||
|
|
||||||
|
extent_shapes:geo_shape | extent_points:geo_shape
|
||||||
|
BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321) | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentGeoShapesGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_boundary) BY prefix
|
||||||
|
| KEEP prefix, extent
|
||||||
|
| SORT prefix ASC
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent:geo_shape
|
||||||
|
E | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321)
|
||||||
|
J | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321)
|
||||||
|
L | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentGeoPointsGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_location) BY prefix
|
||||||
|
| KEEP prefix, extent
|
||||||
|
| SORT prefix ASC
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent:geo_shape
|
||||||
|
E | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
J | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
L | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentGeoShapesAndPointsGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| WHERE region == "City of New York"
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent_shapes = ST_EXTENT_AGG(city_boundary), extent_points = ST_EXTENT_AGG(city_location) BY prefix
|
||||||
|
| KEEP prefix, extent_shapes, extent_points
|
||||||
|
| SORT prefix ASC
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent_shapes:geo_shape | extent_points:geo_shape
|
||||||
|
E | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321) | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
J | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321) | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
L | BBOX (-74.25880000926554, -73.70020005851984, 40.91759996954352, 40.47659996431321) | BBOX (-73.92490002326667, -73.92490002326667, 40.69429999217391, 40.69429999217391)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoShapesGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_boundary) BY prefix
|
||||||
|
| KEEP prefix, extent
|
||||||
|
| SORT prefix
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent:geo_shape
|
||||||
|
A | BBOX (-171.91890003159642, 175.90319998562336, 64.61419996339828, -37.36450002528727)
|
||||||
|
B | BBOX (-116.51340007781982, 153.2021999359131, 60.631899973377585, -41.20620000176132)
|
||||||
|
C | BBOX (-107.51820000819862, 172.6055999379605, 55.732699991203845, -43.90400002710521)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoPointsGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_location) BY prefix
|
||||||
|
| KEEP prefix, extent
|
||||||
|
| SORT prefix
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent:geo_shape
|
||||||
|
A | BBOX (-171.75000007264316, 174.73999994806945, 64.54999999143183, -36.84060002211481)
|
||||||
|
B | BBOX (-116.23080002143979, 153.02809992805123, 60.46669999603182, -41.1500000115484)
|
||||||
|
C | BBOX (-107.39390007220209, 172.38329996354878, 55.676099974662066, -43.58330002985895)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoShapesAndPointsGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airport_city_boundaries
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent_shapes = ST_EXTENT_AGG(city_boundary), extent_points = ST_EXTENT_AGG(city_location) BY prefix
|
||||||
|
| KEEP prefix, extent_shapes, extent_points
|
||||||
|
| SORT prefix
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent_shapes:geo_shape | extent_points:geo_shape
|
||||||
|
A | BBOX (-171.91890003159642, 175.90319998562336, 64.61419996339828, -37.36450002528727) | BBOX (-171.75000007264316, 174.73999994806945, 64.54999999143183, -36.84060002211481)
|
||||||
|
B | BBOX (-116.51340007781982, 153.2021999359131, 60.631899973377585, -41.20620000176132) | BBOX (-116.23080002143979, 153.02809992805123, 60.46669999603182, -41.1500000115484)
|
||||||
|
C | BBOX (-107.51820000819862, 172.6055999379605, 55.732699991203845, -43.90400002710521) | BBOX (-107.39390007220209, 172.38329996354878, 55.676099974662066, -43.58330002985895)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoShapesGroupedEnrich
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
required_capability: enrich_load
|
||||||
|
|
||||||
|
FROM airports
|
||||||
|
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_boundary), count = COUNT() BY prefix
|
||||||
|
| KEEP prefix, count, extent
|
||||||
|
| SORT count DESC, prefix ASC
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | count:long | extent:geo_shape
|
||||||
|
S | 77 | BBOX (-136.45440001040697, 178.8686999771744, 61.38089996762574, -33.92440003808588)
|
||||||
|
C | 75 | BBOX (-107.51820000819862, 172.6055999379605, 55.732699991203845, -43.90400002710521)
|
||||||
|
B | 69 | BBOX (-116.51340007781982, 153.2021999359131, 60.631899973377585, -41.20620000176132)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoPointsGroupedEnrich
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
required_capability: enrich_load
|
||||||
|
|
||||||
|
FROM airports
|
||||||
|
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(city_location), count = COUNT() BY prefix
|
||||||
|
| KEEP prefix, count, extent
|
||||||
|
| SORT count DESC, prefix ASC
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | count:long | extent:geo_shape
|
||||||
|
S | 77 | BBOX (-135.3152000438422, 178.54539999738336, 69.21669997740537, -33.8678000215441)
|
||||||
|
C | 75 | BBOX (-107.39390007220209, 172.38329996354878, 55.676099974662066, -43.58330002985895)
|
||||||
|
B | 69 | BBOX (-116.23080002143979, 153.02809992805123, 60.46669999603182, -41.1500000115484)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyGeoShapesAndPointsGroupedEnrich
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
required_capability: enrich_load
|
||||||
|
|
||||||
|
FROM airports
|
||||||
|
| ENRICH city_boundaries ON city_location WITH airport, region, city_boundary
|
||||||
|
| EVAL prefix = SUBSTRING(abbrev, 1, 1)
|
||||||
|
| STATS extent_shapes = ST_EXTENT_AGG(city_boundary), extent_points = ST_EXTENT_AGG(city_location), count = COUNT() BY prefix
|
||||||
|
| KEEP prefix, count, extent_shapes, extent_points
|
||||||
|
| SORT count DESC, prefix ASC
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | count:long | extent_shapes:geo_shape | extent_points:geo_shape
|
||||||
|
S | 77 | BBOX (-136.45440001040697, 178.8686999771744, 61.38089996762574, -33.92440003808588) | BBOX (-135.3152000438422, 178.54539999738336, 69.21669997740537, -33.8678000215441)
|
||||||
|
C | 75 | BBOX (-107.51820000819862, 172.6055999379605, 55.732699991203845, -43.90400002710521) | BBOX (-107.39390007220209, 172.38329996354878, 55.676099974662066, -43.58330002985895)
|
||||||
|
B | 69 | BBOX (-116.51340007781982, 153.2021999359131, 60.631899973377585, -41.20620000176132) | BBOX (-116.23080002143979, 153.02809992805123, 60.46669999603182, -41.1500000115484)
|
||||||
|
;
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Tests for ST_INTERSECTS on GEO_POINT type
|
# Tests for ST_INTERSECTS on GEO_POINT type
|
||||||
|
|
||||||
|
@ -1777,6 +2003,18 @@ extent:cartesian_shape
|
||||||
BBOX (4783520.5, 1.6168486E7, 8704352.0, -584415.9375)
|
BBOX (4783520.5, 1.6168486E7, 8704352.0, -584415.9375)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
stExtentMultipleCartesianPointsCount
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM airports_web
|
||||||
|
| STATS extent = ST_EXTENT_AGG(location), count = COUNT()
|
||||||
|
;
|
||||||
|
|
||||||
|
extent:cartesian_shape | count:long
|
||||||
|
BBOX (-1.949601E7, 1.9947946E7, 1.4502138E7, -7128878.5) | 849
|
||||||
|
;
|
||||||
|
|
||||||
stExtentMultipleCartesianPointGrouping
|
stExtentMultipleCartesianPointGrouping
|
||||||
required_capability: st_extent_agg
|
required_capability: st_extent_agg
|
||||||
FROM airports_web | STATS extent = ST_EXTENT_AGG(location) BY scalerank | SORT scalerank DESC | LIMIT 3
|
FROM airports_web | STATS extent = ST_EXTENT_AGG(location) BY scalerank | SORT scalerank DESC | LIMIT 3
|
||||||
|
@ -1838,6 +2076,42 @@ count:long | key:keyword | extent:cartesian_shape
|
||||||
4 | Fou | BBOX (0.0, 3.0, 3.0, 0.0)
|
4 | Fou | BBOX (0.0, 3.0, 3.0, 0.0)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
stExtentManyCartesianShapesGrouped
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM countries_bbox_web
|
||||||
|
| EVAL prefix = SUBSTRING(id, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(shape) BY prefix
|
||||||
|
| KEEP prefix, extent
|
||||||
|
| SORT prefix
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | extent:cartesian_shape
|
||||||
|
A | BBOX (-2.0037508E7, 2.0037508E7, 6278042.5, -4.748140544E9)
|
||||||
|
B | BBOX (-9931524.0, 1.2841846E7, 7591831.0, -3994093.25)
|
||||||
|
C | BBOX (-1.8462154E7, 1.5002357E7, 1.7926778E7, -7538976.5)
|
||||||
|
;
|
||||||
|
|
||||||
|
stExtentManyCartesianShapesGroupedCount
|
||||||
|
required_capability: st_extent_agg
|
||||||
|
required_capability: st_extent_agg_docvalues
|
||||||
|
|
||||||
|
FROM countries_bbox_web
|
||||||
|
| EVAL prefix = SUBSTRING(id, 1, 1)
|
||||||
|
| STATS extent = ST_EXTENT_AGG(shape), count = COUNT() BY prefix
|
||||||
|
| KEEP prefix, count, extent
|
||||||
|
| SORT prefix
|
||||||
|
| LIMIT 3
|
||||||
|
;
|
||||||
|
|
||||||
|
prefix:keyword | count:long | extent:cartesian_shape
|
||||||
|
A | 17 | BBOX (-2.0037508E7, 2.0037508E7, 6278042.5, -4.748140544E9)
|
||||||
|
B | 18 | BBOX (-9931524.0, 1.2841846E7, 7591831.0, -3994093.25)
|
||||||
|
C | 19 | BBOX (-1.8462154E7, 1.5002357E7, 1.7926778E7, -7538976.5)
|
||||||
|
;
|
||||||
|
|
||||||
###############################################
|
###############################################
|
||||||
# Tests for ST_INTERSECTS on CARTESIAN_POINT type
|
# Tests for ST_INTERSECTS on CARTESIAN_POINT type
|
||||||
|
|
||||||
|
|
|
@ -296,9 +296,12 @@ public class EsqlCapabilities {
|
||||||
*/
|
*/
|
||||||
ST_DISTANCE,
|
ST_DISTANCE,
|
||||||
|
|
||||||
/** Support for function {@code ST_EXTENT}. */
|
/** Support for function {@code ST_EXTENT_AGG}. */
|
||||||
ST_EXTENT_AGG,
|
ST_EXTENT_AGG,
|
||||||
|
|
||||||
|
/** Optimization of ST_EXTENT_AGG with doc-values as IntBlock. */
|
||||||
|
ST_EXTENT_AGG_DOCVALUES,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix determination of CRS types in spatial functions when folding.
|
* Fix determination of CRS types in spatial functions when folding.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -39,7 +39,7 @@ public abstract class SpatialAggregateFunction extends AggregateFunction impleme
|
||||||
this.fieldExtractPreference = fieldExtractPreference;
|
this.fieldExtractPreference = fieldExtractPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract SpatialAggregateFunction withDocValues();
|
public abstract SpatialAggregateFunction withFieldExtractPreference(FieldExtractPreference preference);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean licenseCheck(XPackLicenseState state) {
|
public boolean licenseCheck(XPackLicenseState state) {
|
||||||
|
|
|
@ -71,8 +71,8 @@ public class SpatialCentroid extends SpatialAggregateFunction implements ToAggre
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpatialCentroid withDocValues() {
|
public SpatialCentroid withFieldExtractPreference(FieldExtractPreference preference) {
|
||||||
return new SpatialCentroid(source(), field(), filter(), FieldExtractPreference.DOC_VALUES);
|
return new SpatialCentroid(source(), field(), filter(), preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,10 +11,12 @@ import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.AggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianPointDocValuesAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianPointDocValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianPointSourceValuesAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianPointSourceValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianShapeAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianShapeDocValuesAggregatorFunctionSupplier;
|
||||||
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentCartesianShapeSourceValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoPointDocValuesAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoPointDocValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoPointSourceValuesAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoPointSourceValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoShapeAggregatorFunctionSupplier;
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoShapeDocValuesAggregatorFunctionSupplier;
|
||||||
|
import org.elasticsearch.compute.aggregation.spatial.SpatialExtentGeoShapeSourceValuesAggregatorFunctionSupplier;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference;
|
import org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference;
|
||||||
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
|
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
|
@ -75,8 +77,8 @@ public final class SpatialExtent extends SpatialAggregateFunction implements ToA
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialExtent withDocValues() {
|
public SpatialExtent withFieldExtractPreference(FieldExtractPreference preference) {
|
||||||
return new SpatialExtent(source(), field(), filter(), FieldExtractPreference.DOC_VALUES);
|
return new SpatialExtent(source(), field(), filter(), preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -101,7 +103,8 @@ public final class SpatialExtent extends SpatialAggregateFunction implements ToA
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AggregatorFunctionSupplier supplier(List<Integer> inputChannels) {
|
public AggregatorFunctionSupplier supplier(List<Integer> inputChannels) {
|
||||||
return switch (field().dataType()) {
|
DataType type = field().dataType();
|
||||||
|
return switch (type) {
|
||||||
case DataType.GEO_POINT -> switch (fieldExtractPreference) {
|
case DataType.GEO_POINT -> switch (fieldExtractPreference) {
|
||||||
case DOC_VALUES -> new SpatialExtentGeoPointDocValuesAggregatorFunctionSupplier(inputChannels);
|
case DOC_VALUES -> new SpatialExtentGeoPointDocValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
case NONE, EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentGeoPointSourceValuesAggregatorFunctionSupplier(inputChannels);
|
case NONE, EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentGeoPointSourceValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
|
@ -110,10 +113,17 @@ public final class SpatialExtent extends SpatialAggregateFunction implements ToA
|
||||||
case DOC_VALUES -> new SpatialExtentCartesianPointDocValuesAggregatorFunctionSupplier(inputChannels);
|
case DOC_VALUES -> new SpatialExtentCartesianPointDocValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
case NONE, EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentCartesianPointSourceValuesAggregatorFunctionSupplier(inputChannels);
|
case NONE, EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentCartesianPointSourceValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
};
|
};
|
||||||
// Shapes don't differentiate between source and doc values.
|
case DataType.GEO_SHAPE -> switch (fieldExtractPreference) {
|
||||||
case DataType.GEO_SHAPE -> new SpatialExtentGeoShapeAggregatorFunctionSupplier(inputChannels);
|
case EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentGeoShapeDocValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
case DataType.CARTESIAN_SHAPE -> new SpatialExtentCartesianShapeAggregatorFunctionSupplier(inputChannels);
|
case NONE -> new SpatialExtentGeoShapeSourceValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
default -> throw EsqlIllegalArgumentException.illegalDataType(field().dataType());
|
case DOC_VALUES -> throw new EsqlIllegalArgumentException("Illegal field extract preference: " + fieldExtractPreference);
|
||||||
|
};
|
||||||
|
case DataType.CARTESIAN_SHAPE -> switch (fieldExtractPreference) {
|
||||||
|
case EXTRACT_SPATIAL_BOUNDS -> new SpatialExtentCartesianShapeDocValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
|
case NONE -> new SpatialExtentCartesianShapeSourceValuesAggregatorFunctionSupplier(inputChannels);
|
||||||
|
case DOC_VALUES -> throw new EsqlIllegalArgumentException("Illegal field extract preference: " + fieldExtractPreference);
|
||||||
|
};
|
||||||
|
default -> throw EsqlIllegalArgumentException.illegalDataType(type);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.optimizer.rules.physical.local;
|
package org.elasticsearch.xpack.esql.optimizer.rules.physical.local;
|
||||||
|
|
||||||
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Alias;
|
import org.elasticsearch.xpack.esql.core.expression.Alias;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Attribute;
|
import org.elasticsearch.xpack.esql.core.expression.Attribute;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
import org.elasticsearch.xpack.esql.core.expression.Expression;
|
||||||
|
@ -84,7 +85,9 @@ public class SpatialDocValuesExtraction extends PhysicalOptimizerRules.Parameter
|
||||||
// We need to both mark the field to load differently, and change the spatial function to know to use it
|
// We need to both mark the field to load differently, and change the spatial function to know to use it
|
||||||
foundAttributes.add(fieldAttribute);
|
foundAttributes.add(fieldAttribute);
|
||||||
changedAggregates = true;
|
changedAggregates = true;
|
||||||
orderedAggregates.add(as.replaceChild(af.withDocValues()));
|
orderedAggregates.add(
|
||||||
|
as.replaceChild(af.withFieldExtractPreference(MappedFieldType.FieldExtractPreference.DOC_VALUES))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
orderedAggregates.add(aggExpr);
|
orderedAggregates.add(aggExpr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.esql.optimizer.rules.physical.local;
|
package org.elasticsearch.xpack.esql.optimizer.rules.physical.local;
|
||||||
|
|
||||||
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
import org.elasticsearch.lucene.spatial.GeometryDocValueWriter;
|
import org.elasticsearch.lucene.spatial.GeometryDocValueWriter;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Alias;
|
import org.elasticsearch.xpack.esql.core.expression.Alias;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.Attribute;
|
import org.elasticsearch.xpack.esql.core.expression.Attribute;
|
||||||
|
@ -49,9 +50,20 @@ import java.util.stream.Collectors;
|
||||||
public class SpatialShapeBoundsExtraction extends ParameterizedOptimizerRule<AggregateExec, LocalPhysicalOptimizerContext> {
|
public class SpatialShapeBoundsExtraction extends ParameterizedOptimizerRule<AggregateExec, LocalPhysicalOptimizerContext> {
|
||||||
@Override
|
@Override
|
||||||
protected PhysicalPlan rule(AggregateExec aggregate, LocalPhysicalOptimizerContext ctx) {
|
protected PhysicalPlan rule(AggregateExec aggregate, LocalPhysicalOptimizerContext ctx) {
|
||||||
var foundAttributes = new HashSet<Attribute>();
|
Set<Attribute> foundAttributes = findSpatialShapeBoundsAttributes(aggregate, ctx);
|
||||||
|
if (foundAttributes.isEmpty()) {
|
||||||
|
return aggregate;
|
||||||
|
}
|
||||||
|
return aggregate.transformDown(PhysicalPlan.class, exec -> switch (exec) {
|
||||||
|
case AggregateExec agg -> transformAggregateExec(agg, foundAttributes);
|
||||||
|
case FieldExtractExec fieldExtractExec -> transformFieldExtractExec(fieldExtractExec, foundAttributes);
|
||||||
|
default -> exec;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return aggregate.transformDown(UnaryExec.class, exec -> {
|
private static Set<Attribute> findSpatialShapeBoundsAttributes(AggregateExec aggregate, LocalPhysicalOptimizerContext ctx) {
|
||||||
|
var foundAttributes = new HashSet<Attribute>();
|
||||||
|
aggregate.transformDown(UnaryExec.class, exec -> {
|
||||||
switch (exec) {
|
switch (exec) {
|
||||||
case AggregateExec agg -> {
|
case AggregateExec agg -> {
|
||||||
List<AggregateFunction> aggregateFunctions = agg.aggregates()
|
List<AggregateFunction> aggregateFunctions = agg.aggregates()
|
||||||
|
@ -84,18 +96,27 @@ public class SpatialShapeBoundsExtraction extends ParameterizedOptimizerRule<Agg
|
||||||
}
|
}
|
||||||
case EvalExec evalExec -> foundAttributes.removeAll(evalExec.references());
|
case EvalExec evalExec -> foundAttributes.removeAll(evalExec.references());
|
||||||
case FilterExec filterExec -> foundAttributes.removeAll(filterExec.condition().references());
|
case FilterExec filterExec -> foundAttributes.removeAll(filterExec.condition().references());
|
||||||
case FieldExtractExec fieldExtractExec -> {
|
|
||||||
var boundsAttributes = new HashSet<>(foundAttributes);
|
|
||||||
boundsAttributes.retainAll(fieldExtractExec.attributesToExtract());
|
|
||||||
if (boundsAttributes.isEmpty() == false) {
|
|
||||||
exec = fieldExtractExec.withBoundsAttributes(boundsAttributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default -> { // Do nothing
|
default -> { // Do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return exec;
|
return exec;
|
||||||
});
|
});
|
||||||
|
return foundAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PhysicalPlan transformFieldExtractExec(FieldExtractExec fieldExtractExec, Set<Attribute> foundAttributes) {
|
||||||
|
var boundsAttributes = new HashSet<>(foundAttributes);
|
||||||
|
boundsAttributes.retainAll(fieldExtractExec.attributesToExtract());
|
||||||
|
return fieldExtractExec.withBoundsAttributes(boundsAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PhysicalPlan transformAggregateExec(AggregateExec agg, Set<Attribute> foundAttributes) {
|
||||||
|
return agg.transformExpressionsDown(
|
||||||
|
SpatialExtent.class,
|
||||||
|
spatialExtent -> foundAttributes.contains(spatialExtent.field())
|
||||||
|
? spatialExtent.withFieldExtractPreference(MappedFieldType.FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS)
|
||||||
|
: spatialExtent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isShape(DataType dataType) {
|
private static boolean isShape(DataType dataType) {
|
||||||
|
|
|
@ -180,10 +180,8 @@ final class AggregateMapper {
|
||||||
types = List.of("GeoPoint", "CartesianPoint");
|
types = List.of("GeoPoint", "CartesianPoint");
|
||||||
extraConfigs = SPATIAL_EXTRA_CONFIGS;
|
extraConfigs = SPATIAL_EXTRA_CONFIGS;
|
||||||
} else if (clazz == SpatialExtent.class) {
|
} else if (clazz == SpatialExtent.class) {
|
||||||
return Stream.concat(
|
types = List.of("GeoPoint", "CartesianPoint", "GeoShape", "CartesianShape");
|
||||||
combine(clazz, List.of("GeoPoint", "CartesianPoint"), SPATIAL_EXTRA_CONFIGS),
|
extraConfigs = SPATIAL_EXTRA_CONFIGS;
|
||||||
combine(clazz, List.of("GeoShape", "CartesianShape"), List.of(""))
|
|
||||||
);
|
|
||||||
} else if (Values.class.isAssignableFrom(clazz)) {
|
} else if (Values.class.isAssignableFrom(clazz)) {
|
||||||
// TODO can't we figure this out from the function itself?
|
// TODO can't we figure this out from the function itself?
|
||||||
types = List.of("Int", "Long", "Double", "Boolean", "BytesRef");
|
types = List.of("Int", "Long", "Double", "Boolean", "BytesRef");
|
||||||
|
|
|
@ -59,6 +59,7 @@ import java.util.function.Function;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES;
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES;
|
||||||
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS;
|
||||||
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE;
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.NONE;
|
||||||
import static org.elasticsearch.xpack.esql.core.util.Queries.Clause.FILTER;
|
import static org.elasticsearch.xpack.esql.core.util.Queries.Clause.FILTER;
|
||||||
import static org.elasticsearch.xpack.esql.optimizer.rules.physical.local.PushFiltersToSource.canPushToSource;
|
import static org.elasticsearch.xpack.esql.optimizer.rules.physical.local.PushFiltersToSource.canPushToSource;
|
||||||
|
@ -284,7 +285,7 @@ public class PlannerUtils {
|
||||||
case DOC_DATA_TYPE -> ElementType.DOC;
|
case DOC_DATA_TYPE -> ElementType.DOC;
|
||||||
case TSID_DATA_TYPE -> ElementType.BYTES_REF;
|
case TSID_DATA_TYPE -> ElementType.BYTES_REF;
|
||||||
case GEO_POINT, CARTESIAN_POINT -> fieldExtractPreference == DOC_VALUES ? ElementType.LONG : ElementType.BYTES_REF;
|
case GEO_POINT, CARTESIAN_POINT -> fieldExtractPreference == DOC_VALUES ? ElementType.LONG : ElementType.BYTES_REF;
|
||||||
case GEO_SHAPE, CARTESIAN_SHAPE -> ElementType.BYTES_REF;
|
case GEO_SHAPE, CARTESIAN_SHAPE -> fieldExtractPreference == EXTRACT_SPATIAL_BOUNDS ? ElementType.INT : ElementType.BYTES_REF;
|
||||||
case PARTIAL_AGG -> ElementType.COMPOSITE;
|
case PARTIAL_AGG -> ElementType.COMPOSITE;
|
||||||
case SHORT, BYTE, DATE_PERIOD, TIME_DURATION, OBJECT, FLOAT, HALF_FLOAT, SCALED_FLOAT -> throw EsqlIllegalArgumentException
|
case SHORT, BYTE, DATE_PERIOD, TIME_DURATION, OBJECT, FLOAT, HALF_FLOAT, SCALED_FLOAT -> throw EsqlIllegalArgumentException
|
||||||
.illegalDataType(dataType);
|
.illegalDataType(dataType);
|
||||||
|
@ -300,11 +301,4 @@ public class PlannerUtils {
|
||||||
new NoopCircuitBreaker("noop-esql-breaker"),
|
new NoopCircuitBreaker("noop-esql-breaker"),
|
||||||
BigArrays.NON_RECYCLING_INSTANCE
|
BigArrays.NON_RECYCLING_INSTANCE
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns DOC_VALUES if the given boolean is set.
|
|
||||||
*/
|
|
||||||
public static MappedFieldType.FieldExtractPreference extractPreference(boolean hasPreference) {
|
|
||||||
return hasPreference ? DOC_VALUES : NONE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ import org.elasticsearch.xpack.esql.core.expression.predicate.logical.And;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Not;
|
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Not;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or;
|
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or;
|
||||||
import org.elasticsearch.xpack.esql.core.expression.predicate.operator.comparison.BinaryComparison;
|
import org.elasticsearch.xpack.esql.core.expression.predicate.operator.comparison.BinaryComparison;
|
||||||
|
import org.elasticsearch.xpack.esql.core.tree.Node;
|
||||||
import org.elasticsearch.xpack.esql.core.tree.Source;
|
import org.elasticsearch.xpack.esql.core.tree.Source;
|
||||||
import org.elasticsearch.xpack.esql.core.type.DataType;
|
import org.elasticsearch.xpack.esql.core.type.DataType;
|
||||||
import org.elasticsearch.xpack.esql.core.type.EsField;
|
import org.elasticsearch.xpack.esql.core.type.EsField;
|
||||||
|
@ -218,7 +219,10 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
private TestDataSource airportsNotIndexed; // Test when spatial field has doc values but is not indexed
|
private TestDataSource airportsNotIndexed; // Test when spatial field has doc values but is not indexed
|
||||||
private TestDataSource airportsNotIndexedNorDocValues; // Test when spatial field is neither indexed nor has doc-values
|
private TestDataSource airportsNotIndexedNorDocValues; // Test when spatial field is neither indexed nor has doc-values
|
||||||
private TestDataSource airportsWeb; // Cartesian point field tests
|
private TestDataSource airportsWeb; // Cartesian point field tests
|
||||||
private TestDataSource airportsCityBoundaries;
|
private TestDataSource airportsCityBoundaries; // geo_shape field tests
|
||||||
|
private TestDataSource airportsCityBoundariesNoPointDocValues; // Disable doc-values on geo_point fields, but not geo_shape fields
|
||||||
|
private TestDataSource airportsCityBoundariesNoShapeDocValues; // Disable doc-values on geo_shape fields, but not geo_point fields
|
||||||
|
private TestDataSource airportsCityBoundariesNoDocValues; // Dsiable doc-values on both geo_point and geo_shape fields
|
||||||
private TestDataSource cartesianMultipolygons; // cartesian_shape field tests
|
private TestDataSource cartesianMultipolygons; // cartesian_shape field tests
|
||||||
private TestDataSource cartesianMultipolygonsNoDocValues; // cartesian_shape field tests but has no doc values
|
private TestDataSource cartesianMultipolygonsNoDocValues; // cartesian_shape field tests but has no doc values
|
||||||
private TestDataSource countriesBbox; // geo_shape field tests
|
private TestDataSource countriesBbox; // geo_shape field tests
|
||||||
|
@ -296,6 +300,27 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
functionRegistry,
|
functionRegistry,
|
||||||
enrichResolution
|
enrichResolution
|
||||||
);
|
);
|
||||||
|
this.airportsCityBoundariesNoPointDocValues = makeTestDataSource(
|
||||||
|
"airports_city_boundaries",
|
||||||
|
"mapping-airport_city_boundaries.json",
|
||||||
|
functionRegistry,
|
||||||
|
enrichResolution,
|
||||||
|
new TestConfigurableSearchStats().exclude(Config.DOC_VALUES, "location", "city_location")
|
||||||
|
);
|
||||||
|
this.airportsCityBoundariesNoShapeDocValues = makeTestDataSource(
|
||||||
|
"airports_city_boundaries",
|
||||||
|
"mapping-airport_city_boundaries.json",
|
||||||
|
functionRegistry,
|
||||||
|
enrichResolution,
|
||||||
|
new TestConfigurableSearchStats().exclude(Config.DOC_VALUES, "city_boundary")
|
||||||
|
);
|
||||||
|
this.airportsCityBoundariesNoDocValues = makeTestDataSource(
|
||||||
|
"airports_city_boundaries",
|
||||||
|
"mapping-airport_city_boundaries.json",
|
||||||
|
functionRegistry,
|
||||||
|
enrichResolution,
|
||||||
|
new TestConfigurableSearchStats().exclude(Config.DOC_VALUES, "city_boundary", "location", "city_location")
|
||||||
|
);
|
||||||
this.cartesianMultipolygons = makeTestDataSource(
|
this.cartesianMultipolygons = makeTestDataSource(
|
||||||
"cartesian_multipolygons",
|
"cartesian_multipolygons",
|
||||||
"mapping-cartesian_multipolygons.json",
|
"mapping-cartesian_multipolygons.json",
|
||||||
|
@ -3274,10 +3299,10 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
* ][_doc{f}#36], limit[], sort[] estimatedRowSize[204]
|
* ][_doc{f}#36], limit[], sort[] estimatedRowSize[204]
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public void testSpatialTypesAndStatsExtentOfGeoShapeDoesNotUseBinaryExtraction() {
|
public void testSpatialTypesAndStatsExtentOfGeoShapeUsesBinaryExtraction() {
|
||||||
// TODO: When we get geo_shape working with bounds extraction from doc-values, change the name of this test
|
|
||||||
var query = "FROM airports_city_boundaries | STATS extent = ST_EXTENT_AGG(city_boundary)";
|
var query = "FROM airports_city_boundaries | STATS extent = ST_EXTENT_AGG(city_boundary)";
|
||||||
var testData = airportsCityBoundaries;
|
for (boolean useDocValues : new Boolean[] { true, false }) {
|
||||||
|
var testData = useDocValues ? airportsCityBoundaries : airportsCityBoundariesNoDocValues;
|
||||||
var plan = physicalPlan(query, testData);
|
var plan = physicalPlan(query, testData);
|
||||||
|
|
||||||
var limit = as(plan, LimitExec.class);
|
var limit = as(plan, LimitExec.class);
|
||||||
|
@ -3298,15 +3323,15 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
||||||
exchange = as(agg.child(), ExchangeExec.class);
|
exchange = as(agg.child(), ExchangeExec.class);
|
||||||
agg = as(exchange.child(), AggregateExec.class);
|
agg = as(exchange.child(), AggregateExec.class);
|
||||||
// below the exchange (in data node) the aggregation is using a specific
|
// below the exchange (in data node) the aggregation is using a specific int[] which the aggregation needs to know about.
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
var fieldExtractPreference = useDocValues ? FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS : FieldExtractPreference.NONE;
|
||||||
assertChildIsExtractedAs(agg, FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS, GEO_SHAPE);
|
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, fieldExtractPreference);
|
||||||
|
assertChildIsExtractedAs(agg, fieldExtractPreference, GEO_SHAPE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This test verifies that the aggregation does not use spatial bounds extraction when the shape appears in an eval or filter.
|
* This test verifies that the aggregation does not use spatial bounds extraction when the shape appears in an eval or filter.
|
||||||
* TODO: Currently this tests nothing, because geo_shape is not supported anyway for bounds extraction,
|
|
||||||
* but it should be updated when it is supported.
|
|
||||||
*/
|
*/
|
||||||
public void testSpatialTypesAndStatsExtentOfShapesNegativeCases() {
|
public void testSpatialTypesAndStatsExtentOfShapesNegativeCases() {
|
||||||
for (String query : new String[] { """
|
for (String query : new String[] { """
|
||||||
|
@ -3329,6 +3354,7 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
||||||
var exchange = as(agg.child(), ExchangeExec.class);
|
var exchange = as(agg.child(), ExchangeExec.class);
|
||||||
agg = as(exchange.child(), AggregateExec.class);
|
agg = as(exchange.child(), AggregateExec.class);
|
||||||
|
// Because the shape was used in EVAL/WHERE we cannot use doc-values bounds extraction optimization
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
||||||
var exec = agg.child() instanceof FieldExtractExec ? agg : as(agg.child(), UnaryExec.class);
|
var exec = agg.child() instanceof FieldExtractExec ? agg : as(agg.child(), UnaryExec.class);
|
||||||
assertChildIsExtractedAs(exec, FieldExtractPreference.NONE, GEO_SHAPE);
|
assertChildIsExtractedAs(exec, FieldExtractPreference.NONE, GEO_SHAPE);
|
||||||
|
@ -3354,19 +3380,11 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
var optimized = optimizedPlan(plan, testData.stats);
|
var optimized = optimizedPlan(plan, testData.stats);
|
||||||
limit = as(optimized, LimitExec.class);
|
limit = as(optimized, LimitExec.class);
|
||||||
agg = as(limit.child(), AggregateExec.class);
|
agg = as(limit.child(), AggregateExec.class);
|
||||||
// For cartesian_shape extraction, we extract bounds from doc-values directly into a BBOX encoded as BytesRef,
|
|
||||||
// so the aggregation does not need to know about it.
|
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, CARTESIAN_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", SpatialExtent.class, CARTESIAN_SHAPE, FieldExtractPreference.NONE);
|
||||||
var exchange = as(agg.child(), ExchangeExec.class);
|
var exchange = as(agg.child(), ExchangeExec.class);
|
||||||
agg = as(exchange.child(), AggregateExec.class);
|
agg = as(exchange.child(), AggregateExec.class);
|
||||||
assertAggregation(
|
// We extract bounds from doc-values into a special int[] which the aggregation needs to know about.
|
||||||
agg,
|
assertAggregation(agg, "extent", "hasDocValues:" + hasDocValues, SpatialExtent.class, CARTESIAN_SHAPE, fieldExtractPreference);
|
||||||
"extent",
|
|
||||||
"hasDocValues:" + hasDocValues,
|
|
||||||
SpatialExtent.class,
|
|
||||||
CARTESIAN_SHAPE,
|
|
||||||
FieldExtractPreference.NONE
|
|
||||||
);
|
|
||||||
var exec = agg.child() instanceof FieldExtractExec ? agg : as(agg.child(), UnaryExec.class);
|
var exec = agg.child() instanceof FieldExtractExec ? agg : as(agg.child(), UnaryExec.class);
|
||||||
// For cartesian_shape, the bounds extraction is done in the FieldExtractExec, so it does need to know about this
|
// For cartesian_shape, the bounds extraction is done in the FieldExtractExec, so it does need to know about this
|
||||||
assertChildIsExtractedAs(exec, fieldExtractPreference, CARTESIAN_SHAPE);
|
assertChildIsExtractedAs(exec, fieldExtractPreference, CARTESIAN_SHAPE);
|
||||||
|
@ -3374,40 +3392,47 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Before local optimizations:
|
* This tests all four combinations of geo_point and geo_shape with and without doc-values.
|
||||||
|
* Since each will be extracted differently (points as encoded longs, and shapes as int[5] bounds representing Extents),
|
||||||
|
* we want to verify that the combinations do not clash and work together.
|
||||||
|
* The optimized query plan in the case when both points and shapes have doc-values will look like:
|
||||||
* <code>
|
* <code>
|
||||||
* LimitExec[1000[INTEGER]]
|
* LimitExec[1000[INTEGER]]
|
||||||
* \_AggregateExec[[],[SPATIALEXTENT(city_boundary{f}#13,true[BOOLEAN]) AS extent, SPATIALCENTROID(city_location{f}#12,true[BOOLEA
|
* \_AggregateExec[[],[
|
||||||
* N]) AS centroid],...]
|
* SPATIALEXTENT(city_boundary{f}#13,true[BOOLEAN]) AS extent,
|
||||||
* \_ExchangeExec[[..]]
|
* SPATIALCENTROID(city_location{f}#12,true[BOOLEAN]) AS centroid
|
||||||
* \_FragmentExec[filter=null, estimatedRowSize=0, reducer=[], fragment=[...]]
|
* ],FINAL,[...bounds attributes..., ...centroid attributes...],221]
|
||||||
* \_EsRelation[airports_city_boundaries][abbrev{f}#8, airport{f}#9, city{f}#11, city_boundar..]
|
* \_ExchangeExec[[...bounds attributes..., ...centroid attributes...],true]
|
||||||
* </code>
|
* \_AggregateExec[[],[
|
||||||
* After local optimizations:
|
* SPATIALEXTENT(city_boundary{f}#13,true[BOOLEAN]) AS extent,
|
||||||
* <code>
|
* SPATIALCENTROID(city_location{f}#12,true[BOOLEAN]) AS centroid
|
||||||
* LimitExec[1000[INTEGER]]
|
* ],INITIAL,[...bounds attributes..., ...centroid attributes...],221]
|
||||||
* \_AggregateExec[[],[SPATIALSTEXTENT(location{f}#48,true[BOOLEAN]) AS extent],FINAL,[minNegX{r}#52, minPosX{r}#53, maxNegX{r}#54,
|
* \_FieldExtractExec[city_boundary{f}#13, city_location{f}#12][city_location{f}#12],[city_boundary{f}#13]
|
||||||
* maxPosX{r}#55, maxY{r}#56, minY{r}#57],21]
|
* \_EsQueryExec[airports_city_boundaries], indexMode[standard], query[
|
||||||
* \_ExchangeExec[[minNegX{r}#52, minPosX{r}#53, maxNegX{r}#54, maxPosX{r}#55, maxY{r}#56, minY{r}#57],true]
|
* {"bool":{"should":[
|
||||||
* \_AggregateExec[[],[SPATIALSTEXTENT(location{f}#48,true[BOOLEAN]) AS extent],INITIAL,[
|
* {"exists":{"field":"city_boundary","boost":1.0}},
|
||||||
* minNegX{r}#73, minPosX{r}#74, maxNegX{rb#75, maxPosX{r}#76, maxY{r}#77, minY{r}#78],21]
|
* {"exists":{"field":"city_location","boost":1.0}}
|
||||||
* \_FieldExtractExec[location{f}#48][location{f}#48]
|
* ],"boost":1.0}}
|
||||||
* \_EsQueryExec[airports], indexMode[standard], query[{"exists":{"field":"location","boost":1.0}}][
|
* ][_doc{f}#55], limit[], sort[] estimatedRowSize[225]
|
||||||
* _doc{f}#79], limit[], sort[] estimatedRowSize[25]
|
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public void testMixedSpatialBoundsAndPointsExtracted() {
|
public void testMixedSpatialBoundsAndPointsExtracted() {
|
||||||
var query = """
|
var query = """
|
||||||
FROM airports_city_boundaries \
|
FROM airports_city_boundaries \
|
||||||
| STATS extent = ST_EXTENT_AGG(city_boundary), centroid = ST_CENTROID_AGG(city_location)""";
|
| STATS extent = ST_EXTENT_AGG(city_boundary), centroid = ST_CENTROID_AGG(city_location)""";
|
||||||
var testData = airportsCityBoundaries;
|
for (boolean pointDocValues : new Boolean[] { true, false }) {
|
||||||
|
for (boolean shapeDocValues : new Boolean[] { true, false }) {
|
||||||
|
var testData = pointDocValues
|
||||||
|
? (shapeDocValues ? airportsCityBoundaries : airportsCityBoundariesNoShapeDocValues)
|
||||||
|
: (shapeDocValues ? airportsCityBoundariesNoPointDocValues : airportsCityBoundariesNoDocValues);
|
||||||
|
var msg = "DocValues[point:" + pointDocValues + ", shape:" + shapeDocValues + "]";
|
||||||
var plan = physicalPlan(query, testData);
|
var plan = physicalPlan(query, testData);
|
||||||
|
|
||||||
var limit = as(plan, LimitExec.class);
|
var limit = as(plan, LimitExec.class);
|
||||||
var agg = as(limit.child(), AggregateExec.class);
|
var agg = as(limit.child(), AggregateExec.class);
|
||||||
// Before optimization the aggregation does not use doc-values
|
// Before optimization the aggregation does not use doc-values
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", msg, SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
||||||
assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, FieldExtractPreference.NONE);
|
assertAggregation(agg, "centroid", msg, SpatialCentroid.class, GEO_POINT, FieldExtractPreference.NONE);
|
||||||
|
|
||||||
var exchange = as(agg.child(), ExchangeExec.class);
|
var exchange = as(agg.child(), ExchangeExec.class);
|
||||||
var fragment = as(exchange.child(), FragmentExec.class);
|
var fragment = as(exchange.child(), FragmentExec.class);
|
||||||
|
@ -3419,15 +3444,20 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
limit = as(optimized, LimitExec.class);
|
limit = as(optimized, LimitExec.class);
|
||||||
agg = as(limit.child(), AggregateExec.class);
|
agg = as(limit.child(), AggregateExec.class);
|
||||||
// Above the exchange (in coordinator) the aggregation is not field-optimized.
|
// Above the exchange (in coordinator) the aggregation is not field-optimized.
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
assertAggregation(agg, "extent", msg, SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
||||||
assertAggregation(agg, "centroid", SpatialCentroid.class, GEO_POINT, FieldExtractPreference.NONE);
|
assertAggregation(agg, "centroid", msg, SpatialCentroid.class, GEO_POINT, FieldExtractPreference.NONE);
|
||||||
exchange = as(agg.child(), ExchangeExec.class);
|
exchange = as(agg.child(), ExchangeExec.class);
|
||||||
agg = as(exchange.child(), AggregateExec.class);
|
agg = as(exchange.child(), AggregateExec.class);
|
||||||
// below the exchange (in data node) the aggregation is field optimized.
|
|
||||||
assertAggregation(agg, "extent", SpatialExtent.class, GEO_SHAPE, FieldExtractPreference.NONE);
|
|
||||||
var fieldExtractExec = as(agg.child(), FieldExtractExec.class);
|
var fieldExtractExec = as(agg.child(), FieldExtractExec.class);
|
||||||
assertThat(fieldExtractExec.boundsAttributes().stream().map(a -> a.sourceText()).toList(), equalTo(List.of("city_boundary")));
|
// below the exchange (in data node) the aggregation is field optimized.
|
||||||
assertThat(fieldExtractExec.docValuesAttributes().stream().map(a -> a.sourceText()).toList(), equalTo(List.of("city_location")));
|
var shapeExtractPreference = shapeDocValues ? FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS : FieldExtractPreference.NONE;
|
||||||
|
assertAggregation(agg, "extent", msg, SpatialExtent.class, GEO_SHAPE, shapeExtractPreference);
|
||||||
|
List<String> boundsAttributes = shapeDocValues ? List.of("city_boundary") : List.of();
|
||||||
|
List<String> docValuesAttributes = pointDocValues ? List.of("city_location") : List.of();
|
||||||
|
assertThat(fieldExtractExec.boundsAttributes().stream().map(Node::sourceText).toList(), equalTo(boundsAttributes));
|
||||||
|
assertThat(fieldExtractExec.docValuesAttributes().stream().map(Node::sourceText).toList(), equalTo(docValuesAttributes));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7746,7 +7776,7 @@ public class PhysicalPlanOptimizerTests extends ESTestCase {
|
||||||
var aggFunc = assertAggregation(plan, aliasName, aggClass);
|
var aggFunc = assertAggregation(plan, aliasName, aggClass);
|
||||||
var aggField = as(aggFunc.field(), Attribute.class);
|
var aggField = as(aggFunc.field(), Attribute.class);
|
||||||
var spatialAgg = as(aggFunc, SpatialAggregateFunction.class);
|
var spatialAgg = as(aggFunc, SpatialAggregateFunction.class);
|
||||||
assertThat(spatialAgg.fieldExtractPreference(), equalTo(fieldExtractPreference));
|
assertThat(reason, spatialAgg.fieldExtractPreference(), equalTo(fieldExtractPreference));
|
||||||
assertThat(reason, aggField.dataType(), equalTo(fieldType));
|
assertThat(reason, aggField.dataType(), equalTo(fieldType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.elasticsearch.compute.data.BytesRefBlock;
|
||||||
import org.elasticsearch.compute.data.DocBlock;
|
import org.elasticsearch.compute.data.DocBlock;
|
||||||
import org.elasticsearch.compute.data.DocVector;
|
import org.elasticsearch.compute.data.DocVector;
|
||||||
import org.elasticsearch.compute.data.ElementType;
|
import org.elasticsearch.compute.data.ElementType;
|
||||||
|
import org.elasticsearch.compute.data.IntBlock;
|
||||||
import org.elasticsearch.compute.data.IntVector;
|
import org.elasticsearch.compute.data.IntVector;
|
||||||
import org.elasticsearch.compute.data.LongBlock;
|
import org.elasticsearch.compute.data.LongBlock;
|
||||||
import org.elasticsearch.compute.data.Page;
|
import org.elasticsearch.compute.data.Page;
|
||||||
|
@ -33,9 +34,14 @@ import org.elasticsearch.compute.operator.SourceOperator.SourceOperatorFactory;
|
||||||
import org.elasticsearch.core.Nullable;
|
import org.elasticsearch.core.Nullable;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.env.TestEnvironment;
|
import org.elasticsearch.env.TestEnvironment;
|
||||||
|
import org.elasticsearch.geometry.Geometry;
|
||||||
|
import org.elasticsearch.geometry.utils.GeometryValidator;
|
||||||
|
import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
|
||||||
|
import org.elasticsearch.geometry.utils.WellKnownBinary;
|
||||||
import org.elasticsearch.index.analysis.AnalysisRegistry;
|
import org.elasticsearch.index.analysis.AnalysisRegistry;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference;
|
import org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference;
|
||||||
import org.elasticsearch.indices.analysis.AnalysisModule;
|
import org.elasticsearch.indices.analysis.AnalysisModule;
|
||||||
|
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
||||||
import org.elasticsearch.plugins.scanners.StablePluginsRegistry;
|
import org.elasticsearch.plugins.scanners.StablePluginsRegistry;
|
||||||
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
|
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
|
||||||
import org.elasticsearch.xpack.esql.TestBlockFactory;
|
import org.elasticsearch.xpack.esql.TestBlockFactory;
|
||||||
|
@ -68,6 +74,9 @@ import java.util.stream.IntStream;
|
||||||
import static com.carrotsearch.randomizedtesting.generators.RandomNumbers.randomIntBetween;
|
import static com.carrotsearch.randomizedtesting.generators.RandomNumbers.randomIntBetween;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
import static org.apache.lucene.tests.util.LuceneTestCase.createTempDir;
|
import static org.apache.lucene.tests.util.LuceneTestCase.createTempDir;
|
||||||
|
import static org.elasticsearch.compute.aggregation.spatial.SpatialAggregationUtils.encodeLongitude;
|
||||||
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.DOC_VALUES;
|
||||||
|
import static org.elasticsearch.index.mapper.MappedFieldType.FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS;
|
||||||
|
|
||||||
public class TestPhysicalOperationProviders extends AbstractPhysicalOperationProviders {
|
public class TestPhysicalOperationProviders extends AbstractPhysicalOperationProviders {
|
||||||
private final List<IndexPage> indexPages;
|
private final List<IndexPage> indexPages;
|
||||||
|
@ -103,13 +112,7 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
PhysicalOperation op = source;
|
PhysicalOperation op = source;
|
||||||
for (Attribute attr : fieldExtractExec.attributesToExtract()) {
|
for (Attribute attr : fieldExtractExec.attributesToExtract()) {
|
||||||
layout.append(attr);
|
layout.append(attr);
|
||||||
op = op.with(
|
op = op.with(new TestFieldExtractOperatorFactory(attr, fieldExtractExec.fieldExtractPreference(attr)), layout.build());
|
||||||
new TestFieldExtractOperatorFactory(
|
|
||||||
attr,
|
|
||||||
PlannerUtils.extractPreference(fieldExtractExec.docValuesAttributes().contains(attr))
|
|
||||||
),
|
|
||||||
layout.build()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
@ -397,17 +400,16 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
FieldExtractPreference extractPreference,
|
FieldExtractPreference extractPreference,
|
||||||
BiFunction<DocBlock, TestBlockCopier, Block> extractBlock
|
BiFunction<DocBlock, TestBlockCopier, Block> extractBlock
|
||||||
) {
|
) {
|
||||||
BlockFactory blockFactory = docBlock.blockFactory();
|
|
||||||
boolean mapToDocValues = shouldMapToDocValues(dataType, extractPreference);
|
|
||||||
try (
|
try (
|
||||||
Block.Builder blockBuilder = mapToDocValues
|
Block.Builder blockBuilder = blockBuilder(
|
||||||
? blockFactory.newLongBlockBuilder(docBlock.getPositionCount())
|
dataType,
|
||||||
: blockBuilder(dataType, docBlock.getPositionCount(), TestBlockFactory.getNonBreakingInstance())
|
extractPreference,
|
||||||
|
docBlock.getPositionCount(),
|
||||||
|
TestBlockFactory.getNonBreakingInstance()
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
foreachIndexDoc(docBlock, indexDoc -> {
|
foreachIndexDoc(docBlock, indexDoc -> {
|
||||||
TestBlockCopier blockCopier = mapToDocValues
|
TestBlockCopier blockCopier = blockCopier(dataType, extractPreference, indexDoc.asVector().docs());
|
||||||
? TestSpatialPointStatsBlockCopier.create(indexDoc.asVector().docs(), dataType)
|
|
||||||
: new TestBlockCopier(indexDoc.asVector().docs());
|
|
||||||
Block blockForIndex = extractBlock.apply(indexDoc, blockCopier);
|
Block blockForIndex = extractBlock.apply(indexDoc, blockCopier);
|
||||||
blockBuilder.copyFrom(blockForIndex, 0, blockForIndex.getPositionCount());
|
blockBuilder.copyFrom(blockForIndex, 0, blockForIndex.getPositionCount());
|
||||||
});
|
});
|
||||||
|
@ -418,10 +420,6 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldMapToDocValues(DataType dataType, FieldExtractPreference extractPreference) {
|
|
||||||
return extractPreference == FieldExtractPreference.DOC_VALUES && DataType.isSpatialPoint(dataType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class TestBlockCopier {
|
private static class TestBlockCopier {
|
||||||
|
|
||||||
protected final IntVector docIndices;
|
protected final IntVector docIndices;
|
||||||
|
@ -447,7 +445,6 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
/**
|
/**
|
||||||
* geo_point and cartesian_point are normally loaded as WKT from source, but for aggregations we can load them as doc-values
|
* geo_point and cartesian_point are normally loaded as WKT from source, but for aggregations we can load them as doc-values
|
||||||
* which are encoded Long values. This class is used to convert the test loaded WKB into encoded longs for the aggregators.
|
* which are encoded Long values. This class is used to convert the test loaded WKB into encoded longs for the aggregators.
|
||||||
* TODO: We need a different solution to support geo_shape and cartesian_shape
|
|
||||||
*/
|
*/
|
||||||
private abstract static class TestSpatialPointStatsBlockCopier extends TestBlockCopier {
|
private abstract static class TestSpatialPointStatsBlockCopier extends TestBlockCopier {
|
||||||
|
|
||||||
|
@ -465,15 +462,15 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
for (int c = 0; c < docIndices.getPositionCount(); c++) {
|
for (int c = 0; c < docIndices.getPositionCount(); c++) {
|
||||||
int doc = docIndices.getInt(c);
|
int doc = docIndices.getInt(c);
|
||||||
int count = bytesRefBlock.getValueCount(doc);
|
int count = bytesRefBlock.getValueCount(doc);
|
||||||
int i = bytesRefBlock.getFirstValueIndex(doc);
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
builder.appendNull();
|
builder.appendNull();
|
||||||
} else {
|
} else {
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
builder.beginPositionEntry();
|
builder.beginPositionEntry();
|
||||||
}
|
}
|
||||||
for (int v = 0; v < count; v++) {
|
int firstValueIndex = bytesRefBlock.getFirstValueIndex(doc);
|
||||||
builder.appendLong(encode(bytesRefBlock.getBytesRef(i + v, scratch)));
|
for (int i = firstValueIndex; i < firstValueIndex + count; i++) {
|
||||||
|
builder.appendLong(encode(bytesRefBlock.getBytesRef(i, scratch)));
|
||||||
}
|
}
|
||||||
if (count > 1) {
|
if (count > 1) {
|
||||||
builder.endPositionEntry();
|
builder.endPositionEntry();
|
||||||
|
@ -499,12 +496,123 @@ public class TestPhysicalOperationProviders extends AbstractPhysicalOperationPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Block.Builder blockBuilder(DataType dataType, int estimatedSize, BlockFactory blockFactory) {
|
/**
|
||||||
|
* geo_shape and cartesian_shape are normally loaded as WKT from source, but for ST_EXTENT_AGG we can load them from doc-values
|
||||||
|
* extracting the spatial Extent information. This class is used to convert the test loaded WKB into the int[6] used in the aggregators.
|
||||||
|
*/
|
||||||
|
private abstract static class TestSpatialShapeExtentBlockCopier extends TestBlockCopier {
|
||||||
|
protected final SpatialEnvelopeVisitor.PointVisitor pointVisitor;
|
||||||
|
private final SpatialEnvelopeVisitor visitor;
|
||||||
|
|
||||||
|
private TestSpatialShapeExtentBlockCopier(IntVector docIndices, SpatialEnvelopeVisitor.PointVisitor pointVisitor) {
|
||||||
|
super(docIndices);
|
||||||
|
this.pointVisitor = pointVisitor;
|
||||||
|
this.visitor = new SpatialEnvelopeVisitor(pointVisitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Block copyBlock(Block originalData) {
|
||||||
|
BytesRef scratch = new BytesRef(100);
|
||||||
|
BytesRefBlock bytesRefBlock = (BytesRefBlock) originalData;
|
||||||
|
try (IntBlock.Builder builder = bytesRefBlock.blockFactory().newIntBlockBuilder(docIndices.getPositionCount())) {
|
||||||
|
for (int c = 0; c < docIndices.getPositionCount(); c++) {
|
||||||
|
int doc = docIndices.getInt(c);
|
||||||
|
int count = bytesRefBlock.getValueCount(doc);
|
||||||
|
if (count == 0) {
|
||||||
|
builder.appendNull();
|
||||||
|
} else {
|
||||||
|
pointVisitor.reset();
|
||||||
|
int firstValueIndex = bytesRefBlock.getFirstValueIndex(doc);
|
||||||
|
for (int i = firstValueIndex; i < firstValueIndex + count; i++) {
|
||||||
|
BytesRef wkb = bytesRefBlock.getBytesRef(i, scratch);
|
||||||
|
Geometry geometry = WellKnownBinary.fromWKB(GeometryValidator.NOOP, false, wkb.bytes, wkb.offset, wkb.length);
|
||||||
|
geometry.visit(visitor);
|
||||||
|
}
|
||||||
|
encodeExtent(builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void encodeExtent(IntBlock.Builder builder);
|
||||||
|
|
||||||
|
private static TestSpatialShapeExtentBlockCopier create(IntVector docIndices, DataType dataType) {
|
||||||
|
return switch (dataType) {
|
||||||
|
case GEO_SHAPE -> new TestGeoCopier(docIndices);
|
||||||
|
case CARTESIAN_SHAPE -> new TestCartesianCopier(docIndices);
|
||||||
|
default -> throw new IllegalArgumentException("Unsupported spatial data type: " + dataType);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestGeoCopier extends TestSpatialShapeExtentBlockCopier {
|
||||||
|
private TestGeoCopier(IntVector docIndices) {
|
||||||
|
super(docIndices, new SpatialEnvelopeVisitor.GeoPointVisitor(SpatialEnvelopeVisitor.WrapLongitude.WRAP));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeExtent(IntBlock.Builder builder) {
|
||||||
|
// We store the 6 values as a single multi-valued field, in the same order as the fields in the Extent class
|
||||||
|
// This requires that consumers also know the meaning of the values, which they can learn from the Extent class
|
||||||
|
SpatialEnvelopeVisitor.GeoPointVisitor visitor = (SpatialEnvelopeVisitor.GeoPointVisitor) pointVisitor;
|
||||||
|
builder.beginPositionEntry();
|
||||||
|
builder.appendInt(CoordinateEncoder.GEO.encodeY(visitor.getTop()));
|
||||||
|
builder.appendInt(CoordinateEncoder.GEO.encodeY(visitor.getBottom()));
|
||||||
|
builder.appendInt(encodeLongitude(visitor.getNegLeft()));
|
||||||
|
builder.appendInt(encodeLongitude(visitor.getNegRight()));
|
||||||
|
builder.appendInt(encodeLongitude(visitor.getPosLeft()));
|
||||||
|
builder.appendInt(encodeLongitude(visitor.getPosRight()));
|
||||||
|
builder.endPositionEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestCartesianCopier extends TestSpatialShapeExtentBlockCopier {
|
||||||
|
private TestCartesianCopier(IntVector docIndices) {
|
||||||
|
super(docIndices, new SpatialEnvelopeVisitor.CartesianPointVisitor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void encodeExtent(IntBlock.Builder builder) {
|
||||||
|
// We store the 4 values as a single multi-valued field, in the same order as the fields in the Rectangle class
|
||||||
|
// This requires that consumers also know the meaning of the values, which they can learn from the Rectangle class
|
||||||
|
SpatialEnvelopeVisitor.CartesianPointVisitor visitor = (SpatialEnvelopeVisitor.CartesianPointVisitor) pointVisitor;
|
||||||
|
builder.beginPositionEntry();
|
||||||
|
builder.appendInt(CoordinateEncoder.CARTESIAN.encodeX(visitor.getMinX()));
|
||||||
|
builder.appendInt(CoordinateEncoder.CARTESIAN.encodeX(visitor.getMaxX()));
|
||||||
|
builder.appendInt(CoordinateEncoder.CARTESIAN.encodeY(visitor.getMaxY()));
|
||||||
|
builder.appendInt(CoordinateEncoder.CARTESIAN.encodeY(visitor.getMinY()));
|
||||||
|
builder.endPositionEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Block.Builder blockBuilder(
|
||||||
|
DataType dataType,
|
||||||
|
FieldExtractPreference extractPreference,
|
||||||
|
int estimatedSize,
|
||||||
|
BlockFactory blockFactory
|
||||||
|
) {
|
||||||
ElementType elementType = switch (dataType) {
|
ElementType elementType = switch (dataType) {
|
||||||
case SHORT -> ElementType.INT;
|
case SHORT -> ElementType.INT;
|
||||||
case FLOAT, HALF_FLOAT, SCALED_FLOAT -> ElementType.DOUBLE;
|
case FLOAT, HALF_FLOAT, SCALED_FLOAT -> ElementType.DOUBLE;
|
||||||
default -> PlannerUtils.toElementType(dataType);
|
default -> PlannerUtils.toElementType(dataType);
|
||||||
};
|
};
|
||||||
|
if (extractPreference == DOC_VALUES && DataType.isSpatialPoint(dataType)) {
|
||||||
|
return blockFactory.newLongBlockBuilder(estimatedSize);
|
||||||
|
} else if (extractPreference == EXTRACT_SPATIAL_BOUNDS && DataType.isSpatial(dataType)) {
|
||||||
|
return blockFactory.newIntBlockBuilder(estimatedSize);
|
||||||
|
} else {
|
||||||
return elementType.newBlockBuilder(estimatedSize, blockFactory);
|
return elementType.newBlockBuilder(estimatedSize, blockFactory);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TestBlockCopier blockCopier(DataType dataType, FieldExtractPreference extractPreference, IntVector docIndices) {
|
||||||
|
if (extractPreference == DOC_VALUES && DataType.isSpatialPoint(dataType)) {
|
||||||
|
return TestSpatialPointStatsBlockCopier.create(docIndices, dataType);
|
||||||
|
} else if (extractPreference == EXTRACT_SPATIAL_BOUNDS && DataType.isSpatial(dataType)) {
|
||||||
|
return TestSpatialShapeExtentBlockCopier.create(docIndices, dataType);
|
||||||
|
} else {
|
||||||
|
return new TestBlockCopier(docIndices);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.elasticsearch.index.fielddata.FieldDataContext;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
|
import org.elasticsearch.index.mapper.BlockLoader;
|
||||||
import org.elasticsearch.index.mapper.DocumentParserContext;
|
import org.elasticsearch.index.mapper.DocumentParserContext;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
import org.elasticsearch.index.mapper.GeoShapeIndexer;
|
import org.elasticsearch.index.mapper.GeoShapeIndexer;
|
||||||
|
@ -300,14 +301,17 @@ public class GeoShapeWithDocValuesFieldMapper extends AbstractShapeGeometryField
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isBoundsExtractionSupported() {
|
public BlockLoader blockLoader(BlockLoaderContext blContext) {
|
||||||
// Extracting bounds for geo shapes is not implemented yet.
|
return blContext.fieldExtractPreference() == FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS
|
||||||
return false;
|
? new GeoBoundsBlockLoader(name())
|
||||||
|
: blockLoaderFromSource(blContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
static class GeoBoundsBlockLoader extends AbstractShapeGeometryFieldMapper.AbstractShapeGeometryFieldType.BoundsBlockLoader {
|
||||||
protected CoordinateEncoder coordinateEncoder() {
|
|
||||||
return CoordinateEncoder.GEO;
|
GeoBoundsBlockLoader(String fieldName) {
|
||||||
|
super(fieldName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.elasticsearch.index.fielddata.FieldDataContext;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||||
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
|
||||||
|
import org.elasticsearch.index.mapper.BlockLoader;
|
||||||
import org.elasticsearch.index.mapper.DocumentParserContext;
|
import org.elasticsearch.index.mapper.DocumentParserContext;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
|
@ -31,6 +32,7 @@ import org.elasticsearch.index.query.SearchExecutionContext;
|
||||||
import org.elasticsearch.lucene.spatial.BinaryShapeDocValuesField;
|
import org.elasticsearch.lucene.spatial.BinaryShapeDocValuesField;
|
||||||
import org.elasticsearch.lucene.spatial.CartesianShapeIndexer;
|
import org.elasticsearch.lucene.spatial.CartesianShapeIndexer;
|
||||||
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
import org.elasticsearch.lucene.spatial.CoordinateEncoder;
|
||||||
|
import org.elasticsearch.lucene.spatial.Extent;
|
||||||
import org.elasticsearch.lucene.spatial.XYQueriesUtils;
|
import org.elasticsearch.lucene.spatial.XYQueriesUtils;
|
||||||
import org.elasticsearch.script.field.AbstractScriptFieldFactory;
|
import org.elasticsearch.script.field.AbstractScriptFieldFactory;
|
||||||
import org.elasticsearch.script.field.DocValuesScriptFieldFactory;
|
import org.elasticsearch.script.field.DocValuesScriptFieldFactory;
|
||||||
|
@ -186,13 +188,26 @@ public class ShapeFieldMapper extends AbstractShapeGeometryFieldMapper<Geometry>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isBoundsExtractionSupported() {
|
public BlockLoader blockLoader(BlockLoaderContext blContext) {
|
||||||
return true;
|
return blContext.fieldExtractPreference() == FieldExtractPreference.EXTRACT_SPATIAL_BOUNDS
|
||||||
|
? new CartesianBoundsBlockLoader(name())
|
||||||
|
: blockLoaderFromSource(blContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
static class CartesianBoundsBlockLoader extends BoundsBlockLoader {
|
||||||
protected CoordinateEncoder coordinateEncoder() {
|
protected CartesianBoundsBlockLoader(String fieldName) {
|
||||||
return CoordinateEncoder.CARTESIAN;
|
super(fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeExtent(BlockLoader.IntBuilder builder, Extent extent) {
|
||||||
|
// For cartesian_shape we store 4 values as a multi-valued field, in the same order as the fields in the Rectangle class
|
||||||
|
builder.beginPositionEntry();
|
||||||
|
builder.appendInt(Math.min(extent.negLeft, extent.posLeft));
|
||||||
|
builder.appendInt(Math.max(extent.negRight, extent.posRight));
|
||||||
|
builder.appendInt(extent.top);
|
||||||
|
builder.appendInt(extent.bottom);
|
||||||
|
builder.endPositionEntry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue