info() {
- return NodeInfo.create(this, Div::new, left(), right());
+ return NodeInfo.create(this, Div::new, left(), right(), dataType);
}
@Override
protected Div replaceChildren(Expression newLeft, Expression newRight) {
- return new Div(source(), newLeft, newRight);
+ return new Div(source(), newLeft, newRight, dataType);
}
@Override
public DataType dataType() {
- return DataTypeConverter.commonType(left().dataType(), right().dataType());
+ if (dataType == null) {
+ dataType = DataTypeConverter.commonType(left().dataType(), right().dataType());
+ }
+ return dataType;
}
@Override
diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java
index b25593a57e00d..b9fa092868592 100644
--- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java
+++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/EsRelation.java
@@ -27,9 +27,17 @@ public class EsRelation extends LeafPlan {
private final boolean frozen;
public EsRelation(Source source, EsIndex index, boolean frozen) {
+ this(source, index, flatten(source, index.mapping()), frozen);
+ }
+
+ public EsRelation(Source source, EsIndex index, List
attributes) {
+ this(source, index, attributes, false);
+ }
+
+ private EsRelation(Source source, EsIndex index, List attributes, boolean frozen) {
super(source);
this.index = index;
- this.attrs = flatten(source, index.mapping());
+ this.attrs = attributes;
this.frozen = frozen;
}
diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/Project.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/Project.java
index dc63705b05685..fedf468009779 100644
--- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/Project.java
+++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/plan/logical/Project.java
@@ -43,6 +43,10 @@ public List extends NamedExpression> projections() {
return projections;
}
+ public Project withProjections(List extends NamedExpression> projections) {
+ return new Project(source(), child(), projections);
+ }
+
@Override
public boolean resolved() {
return super.resolved() && Expressions.anyMatch(projections, Functions::isAggregate) == false;
diff --git a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/EsField.java b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/EsField.java
index eaf8a5c894db4..163667749de2d 100644
--- a/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/EsField.java
+++ b/x-pack/plugin/ql/src/main/java/org/elasticsearch/xpack/ql/type/EsField.java
@@ -48,13 +48,6 @@ public DataType getDataType() {
return esDataType;
}
- /**
- * Create a new {@link EsField} replacing the type.
- */
- public EsField withType(DataType esDataType) {
- return new EsField(name, esDataType, properties, aggregatable, isAlias);
- }
-
/**
* This field can be aggregated
*/
diff --git a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/store/input/FrozenIndexInput.java b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/store/input/FrozenIndexInput.java
index c297c77ada1ee..0154882e598a8 100644
--- a/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/store/input/FrozenIndexInput.java
+++ b/x-pack/plugin/searchable-snapshots/src/main/java/org/elasticsearch/xpack/searchablesnapshots/store/input/FrozenIndexInput.java
@@ -91,7 +91,7 @@ private FrozenIndexInput(
headerBlobCacheByteRange,
footerBlobCacheByteRange
);
- this.cacheFile = cacheFile;
+ this.cacheFile = cacheFile.copy();
}
@Override
diff --git a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java
index 0e86469ad0eea..3112544905396 100644
--- a/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java
+++ b/x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Analyzer.java
@@ -8,6 +8,7 @@
import org.elasticsearch.common.logging.LoggerMessageFormat;
import org.elasticsearch.core.Tuple;
+import org.elasticsearch.xpack.ql.analyzer.AnalyzerRules;
import org.elasticsearch.xpack.ql.analyzer.AnalyzerRules.AddMissingEqualsToBoolField;
import org.elasticsearch.xpack.ql.analyzer.AnalyzerRules.ParameterizedAnalyzerRule;
import org.elasticsearch.xpack.ql.capabilities.Resolvables;
@@ -166,7 +167,13 @@ private static Attribute resolveAgainstList(UnresolvedAttribute u, Collection attrList, boolean allowCompound) {
- var matches = maybeResolveAgainstList(u, attrList, allowCompound, false);
+ var matches = maybeResolveAgainstList(
+ u,
+ attrList,
+ allowCompound,
+ false,
+ (ua, na) -> AnalyzerRules.handleSpecialFields(ua, na, allowCompound)
+ );
return matches.isEmpty() ? null : matches.get(0);
}
diff --git a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java
index 044828f9ab01b..77c7272c3b60d 100644
--- a/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java
+++ b/x-pack/plugin/transform/qa/single-node-tests/src/javaRestTest/java/org/elasticsearch/xpack/transform/integration/TransformPivotRestIT.java
@@ -1846,6 +1846,96 @@ public void testPivotWithTopMetrics() throws Exception {
assertEquals("business_3", actual);
}
+ public void testPivotWithBoxplot() throws Exception {
+ String transformId = "boxplot_transform";
+ String transformIndex = "boxplot_pivot_reviews";
+ setupDataAccessRole(DATA_ACCESS_ROLE, REVIEWS_INDEX_NAME, transformIndex);
+
+ final Request createTransformRequest = createRequestWithAuth(
+ "PUT",
+ getTransformEndpoint() + transformId,
+ BASIC_AUTH_VALUE_TRANSFORM_ADMIN_WITH_SOME_DATA_ACCESS
+ );
+
+ String config = Strings.format("""
+ {
+ "source": {
+ "index": "%s"
+ },
+ "dest": {
+ "index": "%s"
+ },
+ "pivot": {
+ "group_by": {
+ "reviewer": {
+ "terms": {
+ "field": "user_id"
+ }
+ }
+ },
+ "aggregations": {
+ "stars_boxplot": {
+ "boxplot": {
+ "field": "stars"
+ }
+ }
+ }
+ }
+ }""", REVIEWS_INDEX_NAME, transformIndex);
+
+ createTransformRequest.setJsonEntity(config);
+ Map createTransformResponse = entityAsMap(client().performRequest(createTransformRequest));
+ assertThat(createTransformResponse.get("acknowledged"), equalTo(Boolean.TRUE));
+
+ startAndWaitForTransform(transformId, transformIndex, BASIC_AUTH_VALUE_TRANSFORM_ADMIN_WITH_SOME_DATA_ACCESS);
+ assertTrue(indexExists(transformIndex));
+
+ Map searchResult = getAsMap(transformIndex + "/_search?q=reviewer:user_4");
+ assertEquals(1, XContentMapValues.extractValue("hits.total.value", searchResult));
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.min", searchResult)).get(0),
+ is(equalTo(1.0))
+ );
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.max", searchResult)).get(0),
+ is(equalTo(5.0))
+ );
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q1", searchResult)).get(0), is(equalTo(3.0)));
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q2", searchResult)).get(0), is(equalTo(5.0)));
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q3", searchResult)).get(0), is(equalTo(5.0)));
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.lower", searchResult)).get(0),
+ is(equalTo(1.0))
+ );
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.upper", searchResult)).get(0),
+ is(equalTo(5.0))
+ );
+
+ searchResult = getAsMap(transformIndex + "/_search?q=reviewer:user_1");
+ assertEquals(1, XContentMapValues.extractValue("hits.total.value", searchResult));
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.min", searchResult)).get(0),
+ is(equalTo(1.0))
+ );
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.max", searchResult)).get(0),
+ is(equalTo(5.0))
+ );
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q1", searchResult)).get(0), is(equalTo(3.0)));
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q2", searchResult)).get(0), is(equalTo(5.0)));
+ assertThat(((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.q3", searchResult)).get(0), is(equalTo(5.0)));
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.lower", searchResult)).get(0),
+ is(equalTo(1.0))
+ );
+ assertThat(
+ ((List>) XContentMapValues.extractValue("hits.hits._source.stars_boxplot.upper", searchResult)).get(0),
+ is(equalTo(5.0))
+ );
+
+ }
+
public void testPivotWithAggregateMetricDouble() throws Exception {
String transformId = "aggregate_metric_double_transform";
String transformIndex = "aggregate_metric_double_pivot_reviews";
diff --git a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregations.java b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregations.java
index 0f7574a58bb5e..95e05d93ff03a 100644
--- a/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregations.java
+++ b/x-pack/plugin/transform/src/main/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregations.java
@@ -56,7 +56,6 @@ public final class TransformAggregations {
private static final List UNSUPPORTED_AGGS = Arrays.asList(
"adjacency_matrix",
"auto_date_histogram",
- "boxplot", // https://github.com/elastic/elasticsearch/issues/52189
"composite", // DONT because it makes no sense
"date_histogram",
"date_range",
@@ -120,7 +119,8 @@ enum AggregationType {
RARE_TERMS("rare_terms", FLATTENED),
MISSING("missing", LONG),
TOP_METRICS("top_metrics", SOURCE),
- STATS("stats", DOUBLE);
+ STATS("stats", DOUBLE),
+ BOXPLOT("boxplot", DOUBLE);
private final String aggregationType;
private final String targetMapping;
diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/PivotTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/PivotTests.java
index 9d127d89575b4..fbb3b2fb9a866 100644
--- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/PivotTests.java
+++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/PivotTests.java
@@ -85,7 +85,7 @@ public class PivotTests extends ESTestCase {
private Client client;
// exclude aggregations from the analytics module as we don't have parser for it here
- private final Set externalAggregations = Collections.singleton("top_metrics");
+ private final Set externalAggregations = Set.of("top_metrics", "boxplot");
private final Set supportedAggregations = Stream.of(AggregationType.values())
.map(AggregationType::getName)
diff --git a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregationsTests.java b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregationsTests.java
index 1666fde56df11..4564ec5cc67ea 100644
--- a/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregationsTests.java
+++ b/x-pack/plugin/transform/src/test/java/org/elasticsearch/xpack/transform/transforms/pivot/TransformAggregationsTests.java
@@ -24,6 +24,7 @@
import org.elasticsearch.search.aggregations.metrics.StatsAggregationBuilder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.analytics.AnalyticsPlugin;
+import org.elasticsearch.xpack.analytics.boxplot.BoxplotAggregationBuilder;
import java.util.Arrays;
import java.util.List;
@@ -136,6 +137,9 @@ public void testResolveTargetMapping() {
assertEquals("double", TransformAggregations.resolveTargetMapping("stats", null));
assertEquals("double", TransformAggregations.resolveTargetMapping("stats", "int"));
+ // boxplot
+ assertEquals("double", TransformAggregations.resolveTargetMapping("boxplot", "double"));
+
// corner case: source type null
assertEquals(null, TransformAggregations.resolveTargetMapping("min", null));
}
@@ -365,6 +369,23 @@ public void testGetAggregationOutputTypesSubAggregations() {
assertEquals("percentiles", outputTypes.get("filter_1.filter_2.percentiles.99_5"));
}
+ public void testGetAggregationOutputTypesBoxplot() {
+ AggregationBuilder boxplotAggregationBuilder = new BoxplotAggregationBuilder("boxplot");
+
+ Tuple