From c0d9c77d161be2debc8422ac2367c1a423bd51d5 Mon Sep 17 00:00:00 2001 From: Andriy Redko Date: Mon, 15 Dec 2025 17:14:56 -0500 Subject: [PATCH] Fix the regression introduced in the OpenSearch OpenAPI spec (#1794) Signed-off-by: Andriy Redko --- CHANGELOG.md | 1 + .../_types/aggregations/Aggregation.java | 126 +++++++++++++ .../aggregations/BucketAggregationBase.java | 171 +----------------- .../integTest/AbstractRequestIT.java | 34 +++- java-codegen/opensearch-openapi.yaml | 25 +-- 5 files changed, 172 insertions(+), 185 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83cae53933..2b91c03a97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Fixed - Fixed an issue where `AwsSdk2Transport` would create a default `JacksonJsonpMapper` on every instantiation even if a mapper was already provided, which could cause issues with Jackson module resolution ([#1788](https://github.com/opensearch-project/opensearch-java/pull/1788)) +- Fixed the regression introduced in the OpenSearch OpenAPI spec ([#1794](https://github.com/opensearch-project/opensearch-java/pull/1794)) ### Security diff --git a/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/Aggregation.java b/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/Aggregation.java index 57994ec30d..661b984988 100644 --- a/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/Aggregation.java +++ b/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/Aggregation.java @@ -159,16 +159,21 @@ public final AggregationVariant _get() { return _value; } + @Nonnull + private final Map aggregations; + @Nonnull private final Map meta; public Aggregation(AggregationVariant value) { this._kind = ApiTypeHelper.requireNonNull(value._aggregationKind(), this, ""); this._value = ApiTypeHelper.requireNonNull(value, this, ""); + this.aggregations = null; this.meta = null; } private Aggregation(Builder builder) { + this.aggregations = ApiTypeHelper.unmodifiable(builder.aggregations); this.meta = ApiTypeHelper.unmodifiable(builder.meta); this._kind = ApiTypeHelper.requireNonNull(builder._kind, builder, ""); this._value = ApiTypeHelper.requireNonNull(builder._value, builder, ""); @@ -178,6 +183,17 @@ public static Aggregation of(Function + * API name: {@code aggregations} + *

+ */ + @Nonnull + public final Map aggregations() { + return this.aggregations; + } + /** * API name: {@code meta} */ @@ -1229,6 +1245,16 @@ public WeightedAverageAggregation weightedAvg() { @Override public void serialize(JsonGenerator generator, JsonpMapper mapper) { generator.writeStartObject(); + if (ApiTypeHelper.isDefined(this.aggregations)) { + generator.writeKey("aggregations"); + generator.writeStartObject(); + for (Map.Entry item0 : this.aggregations.entrySet()) { + generator.writeKey(item0.getKey()); + item0.getValue().serialize(generator, mapper); + } + generator.writeEnd(); + } + if (ApiTypeHelper.isDefined(this.meta)) { generator.writeKey("meta"); generator.writeStartObject(); @@ -1259,16 +1285,66 @@ public static class Builder extends ObjectBuilderBase { private Kind _kind; private AggregationVariant _value; @Nullable + private Map aggregations; + @Nullable private Map meta; public Builder() {} private Builder(Aggregation o) { + this.aggregations = _mapCopy(o.aggregations); this.meta = _mapCopy(o.meta); this._kind = o._kind; this._value = o._value; } + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds all elements of map to aggregations. + *

+ */ + @Nonnull + public final Builder aggregations(Map map) { + this.aggregations = _mapPutAll(this.aggregations, map); + return this; + } + + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds an entry to aggregations. + *

+ */ + @Nonnull + public final Builder aggregations(String key, Aggregation value) { + this.aggregations = _mapPut(this.aggregations, key, value); + return this; + } + + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds a value to aggregations using a builder lambda. + *

+ */ + @Nonnull + public final Builder aggregations(String key, Function> fn) { + return aggregations(key, fn.apply(new Aggregation.Builder()).build()); + } + /** * API name: {@code meta} * @@ -1979,6 +2055,53 @@ protected Aggregation build() { public class ContainerBuilder implements ObjectBuilder { private ContainerBuilder() {} + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds all elements of map to aggregations. + *

+ */ + @Nonnull + public final ContainerBuilder aggregations(Map map) { + Builder.this.aggregations = _mapPutAll(Builder.this.aggregations, map); + return this; + } + + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds an entry to aggregations. + *

+ */ + @Nonnull + public final ContainerBuilder aggregations(String key, Aggregation value) { + Builder.this.aggregations = _mapPut(Builder.this.aggregations, key, value); + return this; + } + + /** + * Sub-aggregations for this aggregation. Only applies to bucket aggregations. + *

+ * API name: {@code aggregations} + *

+ * + *

+ * Adds a value to aggregations using a builder lambda. + *

+ */ + @Nonnull + public final ContainerBuilder aggregations(String key, Function> fn) { + return aggregations(key, fn.apply(new Aggregation.Builder()).build()); + } + /** * API name: {@code meta} * @@ -2013,6 +2136,7 @@ public Aggregation build() { } protected static void setupAggregationDeserializer(ObjectDeserializer op) { + op.add(Builder::aggregations, JsonpDeserializer.stringMapDeserializer(Aggregation._DESERIALIZER), "aggregations", "aggs"); op.add(Builder::meta, JsonpDeserializer.stringMapDeserializer(JsonData._DESERIALIZER), "meta"); op.add(Builder::adjacencyMatrix, AdjacencyMatrixAggregation._DESERIALIZER, "adjacency_matrix"); op.add(Builder::autoDateHistogram, AutoDateHistogramAggregation._DESERIALIZER, "auto_date_histogram"); @@ -2092,6 +2216,7 @@ public int hashCode() { int result = 17; result = 31 * result + Objects.hashCode(this._kind); result = 31 * result + Objects.hashCode(this._value); + result = 31 * result + Objects.hashCode(this.aggregations); result = 31 * result + Objects.hashCode(this.meta); return result; } @@ -2103,6 +2228,7 @@ public boolean equals(Object o) { Aggregation other = (Aggregation) o; return Objects.equals(this._kind, other._kind) && Objects.equals(this._value, other._value) + && Objects.equals(this.aggregations, other.aggregations) && Objects.equals(this.meta, other.meta); } } diff --git a/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/BucketAggregationBase.java b/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/BucketAggregationBase.java index c803a45540..4ab6fd521f 100644 --- a/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/BucketAggregationBase.java +++ b/java-client/src/generated/java/org/opensearch/client/opensearch/_types/aggregations/BucketAggregationBase.java @@ -36,199 +36,35 @@ package org.opensearch.client.opensearch._types.aggregations; -import jakarta.json.stream.JsonGenerator; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; import javax.annotation.Generated; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opensearch.client.json.JsonpDeserializer; -import org.opensearch.client.json.JsonpMapper; import org.opensearch.client.json.ObjectDeserializer; -import org.opensearch.client.util.ApiTypeHelper; -import org.opensearch.client.util.ObjectBuilder; // typedef: _types.aggregations.BucketAggregationBase @Generated("org.opensearch.client.codegen.CodeGenerator") public abstract class BucketAggregationBase extends AggregationBase { - @Nonnull - private final Map aggregations; - - @Nonnull - private final Map aggs; - // --------------------------------------------------------------------------------------------- protected BucketAggregationBase(AbstractBuilder builder) { super(builder); - this.aggregations = ApiTypeHelper.unmodifiable(builder.aggregations); - this.aggs = ApiTypeHelper.unmodifiable(builder.aggs); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggregations} - *

- */ - @Nonnull - public final Map aggregations() { - return this.aggregations; - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggs} - *

- */ - @Nonnull - public final Map aggs() { - return this.aggs; - } - - protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { - super.serializeInternal(generator, mapper); - if (ApiTypeHelper.isDefined(this.aggregations)) { - generator.writeKey("aggregations"); - generator.writeStartObject(); - for (Map.Entry item0 : this.aggregations.entrySet()) { - generator.writeKey(item0.getKey()); - item0.getValue().serialize(generator, mapper); - } - generator.writeEnd(); - } - - if (ApiTypeHelper.isDefined(this.aggs)) { - generator.writeKey("aggs"); - generator.writeStartObject(); - for (Map.Entry item0 : this.aggs.entrySet()) { - generator.writeKey(item0.getKey()); - item0.getValue().serialize(generator, mapper); - } - generator.writeEnd(); - } } // --------------------------------------------------------------------------------------------- public abstract static class AbstractBuilder> extends AggregationBase.AbstractBuilder< BuilderT> { - @Nullable - private Map aggregations; - @Nullable - private Map aggs; protected AbstractBuilder() {} protected AbstractBuilder(BucketAggregationBase o) { super(o); - this.aggregations = _mapCopy(o.aggregations); - this.aggs = _mapCopy(o.aggs); } protected AbstractBuilder(AbstractBuilder o) { super(o); - this.aggregations = _mapCopy(o.aggregations); - this.aggs = _mapCopy(o.aggs); } - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggregations} - *

- * - *

- * Adds all elements of map to aggregations. - *

- */ - @Nonnull - public final BuilderT aggregations(Map map) { - this.aggregations = _mapPutAll(this.aggregations, map); - return self(); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggregations} - *

- * - *

- * Adds an entry to aggregations. - *

- */ - @Nonnull - public final BuilderT aggregations(String key, Aggregation value) { - this.aggregations = _mapPut(this.aggregations, key, value); - return self(); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggregations} - *

- * - *

- * Adds a value to aggregations using a builder lambda. - *

- */ - @Nonnull - public final BuilderT aggregations(String key, Function> fn) { - return aggregations(key, fn.apply(new Aggregation.Builder()).build()); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggs} - *

- * - *

- * Adds all elements of map to aggs. - *

- */ - @Nonnull - public final BuilderT aggs(Map map) { - this.aggs = _mapPutAll(this.aggs, map); - return self(); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggs} - *

- * - *

- * Adds an entry to aggs. - *

- */ - @Nonnull - public final BuilderT aggs(String key, Aggregation value) { - this.aggs = _mapPut(this.aggs, key, value); - return self(); - } - - /** - * Sub-aggregations for this bucket aggregation - *

- * API name: {@code aggs} - *

- * - *

- * Adds a value to aggs using a builder lambda. - *

- */ - @Nonnull - public final BuilderT aggs(String key, Function> fn) { - return aggs(key, fn.apply(new Aggregation.Builder()).build()); - } } // --------------------------------------------------------------------------------------------- @@ -237,15 +73,11 @@ protected static > void setupBucketAg ObjectDeserializer op ) { setupAggregationBaseDeserializer(op); - op.add(AbstractBuilder::aggregations, JsonpDeserializer.stringMapDeserializer(Aggregation._DESERIALIZER), "aggregations"); - op.add(AbstractBuilder::aggs, JsonpDeserializer.stringMapDeserializer(Aggregation._DESERIALIZER), "aggs"); } @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + Objects.hashCode(this.aggregations); - result = 31 * result + Objects.hashCode(this.aggs); return result; } @@ -256,7 +88,6 @@ public boolean equals(Object o) { } if (this == o) return true; if (o == null || this.getClass() != o.getClass()) return false; - BucketAggregationBase other = (BucketAggregationBase) o; - return Objects.equals(this.aggregations, other.aggregations) && Objects.equals(this.aggs, other.aggs); + return true; } } diff --git a/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java b/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java index 6efce2e340..62971322f8 100644 --- a/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java +++ b/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java @@ -44,11 +44,13 @@ import org.opensearch.Version; import org.opensearch.client.json.JsonData; import org.opensearch.client.opensearch.OpenSearchAsyncClient; +import org.opensearch.client.opensearch._types.FieldValue; import org.opensearch.client.opensearch._types.OpenSearchException; import org.opensearch.client.opensearch._types.Refresh; import org.opensearch.client.opensearch._types.Time; import org.opensearch.client.opensearch._types.aggregations.Aggregate; import org.opensearch.client.opensearch._types.aggregations.HistogramAggregate; +import org.opensearch.client.opensearch._types.aggregations.TermsAggregation; import org.opensearch.client.opensearch._types.analysis.Analyzer; import org.opensearch.client.opensearch._types.analysis.CustomAnalyzer; import org.opensearch.client.opensearch._types.analysis.ShingleTokenFilter; @@ -57,6 +59,8 @@ import org.opensearch.client.opensearch._types.mapping.Property; import org.opensearch.client.opensearch._types.mapping.TextProperty; import org.opensearch.client.opensearch._types.mapping.TypeMapping; +import org.opensearch.client.opensearch._types.query_dsl.BoolQuery; +import org.opensearch.client.opensearch._types.query_dsl.TermsQuery; import org.opensearch.client.opensearch.cat.NodesResponse; import org.opensearch.client.opensearch.core.BulkResponse; import org.opensearch.client.opensearch.core.ClearScrollResponse; @@ -451,13 +455,20 @@ public void testSearchAggregation() throws IOException { javaClient().create(_1 -> _1.index("products").id("C").document(new Product(25)).refresh(Refresh.True)); SearchResponse searchResponse = javaClient().search( - _1 -> _1.index("products").size(0).aggregations("prices", _3 -> _3.histogram(_4 -> _4.field("price").interval(10.0))), + _1 -> _1.index("products") + .size(0) + .aggregations( + "prices", + _3 -> _3.histogram(_4 -> _4.field("price").interval(10.0)) + .aggregations("average", _5 -> _5.avg(_6 -> _6.field("price"))) + ), Product.class ); HistogramAggregate prices = searchResponse.aggregations().get("prices").histogram(); assertEquals(3, prices.buckets().array().size()); assertEquals(1, prices.buckets().array().get(0).docCount()); + assertEquals(5.0, prices.buckets().array().get(0).aggregations().get("average").avg().value(), 0.01); // We've set "size" to zero assertEquals(0, searchResponse.hits().hits().size()); @@ -471,14 +482,29 @@ public void testSubAggregation() throws IOException { javaClient().create(_1 -> _1.index("products").id("B").document(new Product(10, "Blue")).refresh(Refresh.True)); javaClient().create(_1 -> _1.index("products").id("C").document(new Product(15, "Black")).refresh(Refresh.True)); + List fieldValues = List.of(FieldValue.of("Blue")); + SearchRequest searchRequest = SearchRequest.of( - _1 -> _1.index("products").size(0).aggregations("price", _3 -> _3.terms(_4 -> _4.field("price"))) + _1 -> _1.index("products") + .size(0) + .aggregations( + "price", + _3 -> _3.aggregations(Map.of("price", TermsAggregation.of(_4 -> _4.field("price")).toAggregation())) + .filter( + BoolQuery.of( + _5 -> _5.filter( + List.of(TermsQuery.of(_6 -> _6.field("color.keyword").terms(_7 -> _7.value(fieldValues))).toQuery()) + ) + ).toQuery() + ) + ) ); SearchResponse searchResponse = javaClient().search(searchRequest, Product.class); Aggregate prices = searchResponse.aggregations().get("price")._get().toAggregate(); - assertEquals(1, prices.dterms().buckets().array().get(0).docCount()); - assertEquals(1, prices.dterms().buckets().array().get(1).docCount()); + assertEquals(2, searchResponse.aggregations().get("price").filter().docCount()); + assertEquals(1, prices.filter().aggregations().get("price").dterms().buckets().array().get(0).docCount()); + assertEquals(1, prices.filter().aggregations().get("price").dterms().buckets().array().get(1).docCount()); // We've set "size" to zero assertEquals(0, searchResponse.hits().hits().size()); diff --git a/java-codegen/opensearch-openapi.yaml b/java-codegen/opensearch-openapi.yaml index 15326856eb..f2a3bf335a 100644 --- a/java-codegen/opensearch-openapi.yaml +++ b/java-codegen/opensearch-openapi.yaml @@ -40811,6 +40811,20 @@ components: allOf: - type: object properties: + aggregations: + description: |- + Sub-aggregations for this aggregation. + Only applies to bucket aggregations. + type: object + additionalProperties: + $ref: '#/components/schemas/_common.aggregations___AggregationContainer' + aggs: + description: |- + Sub-aggregations for this aggregation. + Only applies to bucket aggregations. + type: object + additionalProperties: + $ref: '#/components/schemas/_common.aggregations___AggregationContainer' meta: $ref: '#/components/schemas/_common___Metadata' - type: object @@ -41352,17 +41366,6 @@ components: allOf: - $ref: '#/components/schemas/_common.aggregations___Aggregation' - type: object - properties: - aggregations: - description: Sub-aggregations for this bucket aggregation - type: object - additionalProperties: - $ref: '#/components/schemas/_common.aggregations___AggregationContainer' - aggs: - description: Sub-aggregations for this bucket aggregation - type: object - additionalProperties: - $ref: '#/components/schemas/_common.aggregations___AggregationContainer' _common.aggregations___BucketMetricValueAggregate: allOf: - $ref: '#/components/schemas/_common.aggregations___SingleMetricAggregateBase'