diff --git a/CHANGELOG.md b/CHANGELOG.md index 262966246f..7d79fd6885 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ This section is for maintaining a changelog for all breaking changes for the cli ### Fixed - Fix version and build ([#254](https://github.com/opensearch-project/opensearch-java/pull/254)) - Fix integer overflow for variables in indices stats response ([#960](https://github.com/opensearch-project/opensearch-java/pull/960)) +- Fix composite aggregations for search requests ([#967](https://github.com/opensearch-project/opensearch-java/pull/967)) ### Security diff --git a/guides/search.md b/guides/search.md index 7fd3e06cc9..cf08f7a872 100644 --- a/guides/search.md +++ b/guides/search.md @@ -4,11 +4,13 @@ - [Basic Search](#basic-search) - [Get raw JSON results](#get-raw-json-results) - [Search documents using a match query](#search-documents-using-a-match-query) + - [Search documents using a hybrid query](#search-documents-using-a-hybrid-query) - [Search documents using suggesters](#search-documents-using-suggesters) - [Using completion suggester](#using-completion-suggester) - [Using term suggester](#using-term-suggester) - [Using phrase suggester](#using-phrase-suggester) - [Aggregations](#aggregations) + - [Composite Aggregations](#composite-aggregations) # Search @@ -299,4 +301,24 @@ for (Map.Entry entry : searchResponse.aggregations().entrySet } ``` +#### Composite Aggregations + +```java +final Map comAggrSrcMap = new HashMap<>(); +CompositeAggregationSource compositeAggregationSource1 = new CompositeAggregationSource.Builder().terms( + termsAggrBuilder -> termsAggrBuilder.field("title.keyword").missingBucket(false).order(SortOrder.Asc) +).build(); +comAggrSrcMap.put("titles", compositeAggregationSource1); + +CompositeAggregation compAgg = new CompositeAggregation.Builder().sources(comAggrSrcMap).build(); +Aggregation aggregation = new Aggregation.Builder().composite(compAgg).build(); + +SearchRequest request = SearchRequest.of(r -> r.index(indexName).query(q -> q.match(m -> m.field("title").query(FieldValue.of("Document 1")))).aggregations("my_buckets", aggregation)); +SearchResponse response = client.search(request, IndexData.class); +for (Map.Entry entry : response.aggregations().entrySet()) { + LOGGER.info("Agg - {}", entry.getKey()); + entry.getValue().composite().buckets().array().forEach(b -> LOGGER.info("{} : {}", b.key(), b.docCount())); +} +``` + You can find a working sample of the above code in [Search.java](../samples/src/main/java/org/opensearch/client/samples/Search.java). \ No newline at end of file diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeAggregationSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeAggregationSource.java index 00ab5deca8..5d11e2b4f9 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeAggregationSource.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeAggregationSource.java @@ -49,18 +49,16 @@ @JsonpDeserializable public class CompositeAggregationSource implements JsonpSerializable { @Nullable - private final TermsAggregation terms; + private final CompositeTermsAggregationSource terms; @Nullable - private final HistogramAggregation histogram; + private final CompositeHistogramAggregationSource histogram; @Nullable - private final DateHistogramAggregation dateHistogram; + private final CompositeDateHistogramAggregationSource dateHistogram; @Nullable - private final GeoTileGridAggregation geotileGrid; - - // --------------------------------------------------------------------------------------------- + private final CompositeGeoTileGridAggregationSource geotileGrid; private CompositeAggregationSource(Builder builder) { @@ -79,7 +77,7 @@ public static CompositeAggregationSource of(Function { @Nullable - private TermsAggregation terms; + private CompositeTermsAggregationSource terms; @Nullable - private HistogramAggregation histogram; + private CompositeHistogramAggregationSource histogram; @Nullable - private DateHistogramAggregation dateHistogram; + private CompositeDateHistogramAggregationSource dateHistogram; @Nullable - private GeoTileGridAggregation geotileGrid; + private CompositeGeoTileGridAggregationSource geotileGrid; /** * API name: {@code terms} */ - public final Builder terms(@Nullable TermsAggregation value) { + public final Builder terms(@Nullable CompositeTermsAggregationSource value) { this.terms = value; return this; } @@ -171,14 +167,14 @@ public final Builder terms(@Nullable TermsAggregation value) { /** * API name: {@code terms} */ - public final Builder terms(Function> fn) { - return this.terms(fn.apply(new TermsAggregation.Builder()).build()); + public final Builder terms(Function> fn) { + return this.terms(fn.apply(new CompositeTermsAggregationSource.Builder()).build()); } /** * API name: {@code histogram} */ - public final Builder histogram(@Nullable HistogramAggregation value) { + public final Builder histogram(@Nullable CompositeHistogramAggregationSource value) { this.histogram = value; return this; } @@ -186,14 +182,16 @@ public final Builder histogram(@Nullable HistogramAggregation value) { /** * API name: {@code histogram} */ - public final Builder histogram(Function> fn) { - return this.histogram(fn.apply(new HistogramAggregation.Builder()).build()); + public final Builder histogram( + Function> fn + ) { + return this.histogram(fn.apply(new CompositeHistogramAggregationSource.Builder()).build()); } /** * API name: {@code date_histogram} */ - public final Builder dateHistogram(@Nullable DateHistogramAggregation value) { + public final Builder dateHistogram(@Nullable CompositeDateHistogramAggregationSource value) { this.dateHistogram = value; return this; } @@ -201,14 +199,16 @@ public final Builder dateHistogram(@Nullable DateHistogramAggregation value) { /** * API name: {@code date_histogram} */ - public final Builder dateHistogram(Function> fn) { - return this.dateHistogram(fn.apply(new DateHistogramAggregation.Builder()).build()); + public final Builder dateHistogram( + Function> fn + ) { + return this.dateHistogram(fn.apply(new CompositeDateHistogramAggregationSource.Builder()).build()); } /** * API name: {@code geotile_grid} */ - public final Builder geotileGrid(@Nullable GeoTileGridAggregation value) { + public final Builder geotileGrid(@Nullable CompositeGeoTileGridAggregationSource value) { this.geotileGrid = value; return this; } @@ -216,8 +216,10 @@ public final Builder geotileGrid(@Nullable GeoTileGridAggregation value) { /** * API name: {@code geotile_grid} */ - public final Builder geotileGrid(Function> fn) { - return this.geotileGrid(fn.apply(new GeoTileGridAggregation.Builder()).build()); + public final Builder geotileGrid( + Function> fn + ) { + return this.geotileGrid(fn.apply(new CompositeGeoTileGridAggregationSource.Builder()).build()); } /** @@ -245,11 +247,10 @@ public CompositeAggregationSource build() { protected static void setupCompositeAggregationSourceDeserializer(ObjectDeserializer op) { - op.add(Builder::terms, TermsAggregation._DESERIALIZER, "terms"); - op.add(Builder::histogram, HistogramAggregation._DESERIALIZER, "histogram"); - op.add(Builder::dateHistogram, DateHistogramAggregation._DESERIALIZER, "date_histogram"); - op.add(Builder::geotileGrid, GeoTileGridAggregation._DESERIALIZER, "geotile_grid"); - + op.add(Builder::terms, CompositeTermsAggregationSource._DESERIALIZER, "terms"); + op.add(Builder::histogram, CompositeHistogramAggregationSource._DESERIALIZER, "histogram"); + op.add(Builder::dateHistogram, CompositeDateHistogramAggregationSource._DESERIALIZER, "date_histogram"); + op.add(Builder::geotileGrid, CompositeGeoTileGridAggregationSource._DESERIALIZER, "geotile_grid"); } } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeDateHistogramAggregationSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeDateHistogramAggregationSource.java new file mode 100644 index 0000000000..34fe20c093 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeDateHistogramAggregationSource.java @@ -0,0 +1,198 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch._types.aggregations; + +import jakarta.json.stream.JsonGenerator; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch._types.Time; +import org.opensearch.client.util.ObjectBuilder; + +public class CompositeDateHistogramAggregationSource extends CompositeValuesSource { + + @Nullable + private final Time calendarInterval; + + @Nullable + private final Time fixedInterval; + + @Nullable + private final Long offset; + + private final String zoneId; + + private CompositeDateHistogramAggregationSource(Builder builder) { + super(builder); + this.calendarInterval = builder.calendarInterval; + this.fixedInterval = builder.fixedInterval; + this.offset = builder.offset; + this.zoneId = builder.zoneId; + } + + public static CompositeDateHistogramAggregationSource of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + /** + * API name: {@code calendar_interval} + */ + @Nullable + public final Time calendarInterval() { + return this.calendarInterval; + } + + /** + * API name: {@code fixed_interval} + */ + @Nullable + public final Time fixedInterval() { + return this.fixedInterval; + } + + /** + * API name: {@code offset} + */ + @Nullable + public final Long offset() { + return this.offset; + + } + + /** + * Required - API name: {@code zone_id} + */ + public final String zoneId() { + return this.zoneId; + } + + /** + * Serialize this object to JSON. + */ + @Override + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + super.serializeInternal(generator, mapper); + if (this.calendarInterval != null) { + generator.writeKey("calendar_interval"); + this.calendarInterval.serialize(generator, mapper); + + } + if (this.fixedInterval != null) { + generator.writeKey("fixed_interval"); + this.fixedInterval.serialize(generator, mapper); + + } + if (this.offset != null) { + generator.writeKey("offset"); + generator.write(this.offset); + + } + + generator.writeKey("zone_id"); + generator.write(this.zoneId); + } + + /** + * Builder for {@link CompositeDateHistogramAggregationSource}. + */ + + public static class Builder extends CompositeValuesSource.AbstractBuilder + implements + ObjectBuilder { + + @Nullable + private Time calendarInterval; + + @Nullable + private Time fixedInterval; + + @Nullable + private Long offset; + + private String zoneId; + + /** + * API name: {@code calendar_interval} + */ + public final Builder calendarInterval(@Nullable Time value) { + this.calendarInterval = value; + return this; + } + + /** + * API name: {@code fixed_interval} + */ + public final Builder fixedInterval(@Nullable Time value) { + this.fixedInterval = value; + return this; + } + + /** + * API name: {@code offset} + */ + public final Builder offset(Long value) { + this.offset = value; + return this; + } + + /** + * Required - API name: {@code zone_id} + */ + + public final Builder zoneId(String value) { + this.zoneId = value; + return this; + } + + /** + * Builds a {@link CompositeDateHistogramAggregationSource}. + * + * @throws NullPointerException + * if some of the required fields are null. + */ + public CompositeDateHistogramAggregationSource build() { + _checkSingleUse(); + + return new CompositeDateHistogramAggregationSource(this); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Json deserializer for {@link CompositeDateHistogramAggregationSource} + */ + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy( + Builder::new, + CompositeDateHistogramAggregationSource::setupCompositeDateHistogramAggregationSourceDeserializer + ); + + protected static void setupCompositeDateHistogramAggregationSourceDeserializer( + ObjectDeserializer op + ) { + CompositeValuesSource.setupCompositeValuesSourceDeserializer(op); + op.add(Builder::calendarInterval, Time._DESERIALIZER, "calendar_interval"); + op.add(Builder::fixedInterval, Time._DESERIALIZER, "fixed_interval"); + op.add(Builder::offset, JsonpDeserializer.longDeserializer(), "offset"); + op.add(Builder::zoneId, JsonpDeserializer.stringDeserializer(), "time_zone"); + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeGeoTileGridAggregationSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeGeoTileGridAggregationSource.java new file mode 100644 index 0000000000..fa035e9d80 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeGeoTileGridAggregationSource.java @@ -0,0 +1,150 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch._types.aggregations; + +import jakarta.json.stream.JsonGenerator; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch._types.GeoBounds; +import org.opensearch.client.util.ObjectBuilder; + +@JsonpDeserializable +public class CompositeGeoTileGridAggregationSource extends CompositeValuesSource { + + @Nullable + private final Integer precision; + + @Nullable + private final GeoBounds geoBounds; + + private CompositeGeoTileGridAggregationSource(Builder builder) { + super(builder); + this.precision = builder.precision; + this.geoBounds = builder.geoBounds; + } + + public static CompositeGeoTileGridAggregationSource of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + /** + * API name: {@code precision} + */ + @Nullable + public final Integer precision() { + return this.precision; + } + + /** + * API name: {@code geoBounds} + */ + @Nullable + public final GeoBounds geoBounds() { + return this.geoBounds; + } + + /** + * Serialize this object to JSON. + */ + @Override + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + super.serializeInternal(generator, mapper); + if (this.precision != null) { + generator.writeKey("precision"); + generator.write(this.precision); + } + if (this.geoBounds != null) { + generator.writeKey("bounds"); + this.geoBounds.serialize(generator, mapper); + + } + } + + /** + * Builder for {@link CompositeGeoTileGridAggregationSource}. + */ + + public static class Builder extends CompositeValuesSource.AbstractBuilder + implements + ObjectBuilder { + @Nullable + private Integer precision; + + @Nullable + private GeoBounds geoBounds; + + /** + * API name: {@code precision} + */ + public final Builder precision(@Nullable Integer precision) { + this.precision = precision; + return this; + } + + /** + * API name: {@code geoBounds} + */ + public final Builder geoBounds(@Nullable GeoBounds geoBounds) { + this.geoBounds = geoBounds; + return this; + } + + /** + * API name: {@code geoBounds} + */ + public final Builder geoBounds(Function> fn) { + return this.geoBounds(fn.apply(new GeoBounds.Builder()).build()); + } + + /** + * Builds a {@link CompositeGeoTileGridAggregationSource}. + * + * @throws NullPointerException + * if some of the required fields are null. + */ + public CompositeGeoTileGridAggregationSource build() { + _checkSingleUse(); + + return new CompositeGeoTileGridAggregationSource(this); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Json deserializer for {@link CompositeGeoTileGridAggregationSource} + */ + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy( + Builder::new, + CompositeGeoTileGridAggregationSource::setupCompositeGeoTileGridAggregationSourceDeserializer + ); + + protected static void setupCompositeGeoTileGridAggregationSourceDeserializer( + ObjectDeserializer op + ) { + CompositeValuesSource.setupCompositeValuesSourceDeserializer(op); + op.add(Builder::precision, JsonpDeserializer.integerDeserializer(), "precision"); + op.add(Builder::geoBounds, GeoBounds._DESERIALIZER, "bounds"); + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeHistogramAggregationSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeHistogramAggregationSource.java new file mode 100644 index 0000000000..acd2cb80e1 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeHistogramAggregationSource.java @@ -0,0 +1,107 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch._types.aggregations; + +import jakarta.json.stream.JsonGenerator; +import java.util.function.Function; +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.util.ObjectBuilder; + +@JsonpDeserializable +public class CompositeHistogramAggregationSource extends CompositeValuesSource { + + private final double interval; + + private CompositeHistogramAggregationSource(Builder builder) { + super(builder); + this.interval = builder.interval; + } + + public static CompositeHistogramAggregationSource of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + /** + * Required - API name: {@code interval} + */ + public final double interval() { + return this.interval; + } + + /** + * Serialize this object to JSON. + */ + @Override + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + super.serializeInternal(generator, mapper); + generator.writeKey("interval"); + generator.write(this.interval); + } + + /** + * Builder for {@link CompositeHistogramAggregationSource}. + */ + + public static class Builder extends CompositeValuesSource.AbstractBuilder + implements + ObjectBuilder { + private double interval; + + /** + * Required - API name: {@code interval} + */ + public final Builder interval(double interval) { + this.interval = interval; + return this; + } + + /** + * Builds a {@link CompositeHistogramAggregationSource}. + * + * @throws NullPointerException + * if some of the required fields are null. + */ + public CompositeHistogramAggregationSource build() { + _checkSingleUse(); + + return new CompositeHistogramAggregationSource(this); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Json deserializer for {@link CompositeHistogramAggregationSource} + */ + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy( + Builder::new, + CompositeHistogramAggregationSource::setupCompositeHistogramAggregationSourceDeserializer + ); + + protected static void setupCompositeHistogramAggregationSourceDeserializer( + ObjectDeserializer op + ) { + CompositeValuesSource.setupCompositeValuesSourceDeserializer(op); + op.add(Builder::interval, JsonpDeserializer.doubleDeserializer(), "interval"); + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeTermsAggregationSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeTermsAggregationSource.java new file mode 100644 index 0000000000..891f6618d7 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeTermsAggregationSource.java @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.client.opensearch._types.aggregations; + +import jakarta.json.stream.JsonGenerator; +import java.util.function.Function; +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.util.ObjectBuilder; + +@JsonpDeserializable +public class CompositeTermsAggregationSource extends CompositeValuesSource { + + private CompositeTermsAggregationSource(Builder builder) { + super(builder); + } + + public static CompositeTermsAggregationSource of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + /** + * Serialize this object to JSON. + */ + @Override + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + super.serializeInternal(generator, mapper); + } + + /** + * Builder for {@link CompositeTermsAggregationSource}. + */ + public static class Builder extends CompositeValuesSource.AbstractBuilder + implements + ObjectBuilder { + + /** + * Builds a {@link CompositeTermsAggregationSource}. + * + * @throws NullPointerException + * if some of the required fields are null. + */ + public CompositeTermsAggregationSource build() { + _checkSingleUse(); + + return new CompositeTermsAggregationSource(this); + } + + @Override + protected Builder self() { + return this; + } + } + + /** + * Json deserializer for {@link CompositeTermsAggregationSource} + */ + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy( + Builder::new, + CompositeTermsAggregationSource::setupCompositeGeoTileGridAggregationSourceDeserializer + ); + + protected static void setupCompositeGeoTileGridAggregationSourceDeserializer( + ObjectDeserializer op + ) { + CompositeValuesSource.setupCompositeValuesSourceDeserializer(op); + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeValuesSource.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeValuesSource.java new file mode 100644 index 0000000000..efd973b1cb --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/CompositeValuesSource.java @@ -0,0 +1,252 @@ +package org.opensearch.client.opensearch._types.aggregations; + +import jakarta.json.stream.JsonGenerator; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.JsonpSerializable; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch._types.Script; +import org.opensearch.client.opensearch._types.SortOrder; +import org.opensearch.client.util.ObjectBuilder; +import org.opensearch.client.util.ObjectBuilderBase; + +public abstract class CompositeValuesSource implements JsonpSerializable { + + @Nullable + private final String field; + + @Nullable + private final Script script; + + @Nullable + private final ValueType valueType; + + @Nullable + private Boolean missingBucket; + + @Nullable + private MissingOrder missingOrder; + + @Nullable + private SortOrder order; + + @Nullable + private String format; + + protected CompositeValuesSource(AbstractBuilder builder) { + this.field = builder.field; + this.script = builder.script; + this.valueType = builder.valueType; + this.missingBucket = builder.missingBucket; + this.missingOrder = builder.missingOrder; + this.order = builder.order; + this.format = builder.format; + } + + /** + * API name: {@code field} + */ + @Nullable + public final String field() { + return this.field; + } + + /** + * API name: {@code script} + */ + @Nullable + public final Script script() { + return this.script; + } + + /** + * API name: {@code date_histogram} + */ + @Nullable + public final ValueType valueType() { + return this.valueType; + } + + /** + * API name: {@code missingBucket} + */ + @Nullable + public final Boolean missingBucket() { + return this.missingBucket; + } + + /** + * API name: {@code missingOrder} + */ + @Nullable + public final MissingOrder missingOrder() { + return this.missingOrder; + } + + /** + * API name: {@code order} + */ + @Nullable + public final SortOrder order() { + return this.order; + } + + /** + * API name: {@code format} + */ + @Nullable + public final String format() { + return this.format; + } + + /** + * Serialize this object to JSON. + */ + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + if (this.field != null) { + generator.writeKey("field"); + generator.write(this.field); + } + + if (this.script != null) { + generator.writeKey("script"); + this.script.serialize(generator, mapper); + + } + if (this.valueType != null) { + generator.writeKey("value_type"); + this.valueType.serialize(generator, mapper); + + } + if (this.missingBucket != null) { + generator.writeKey("missing_bucket"); + generator.write(this.missingBucket); + } + if (this.missingOrder != null) { + generator.writeKey("missing_order"); + this.missingOrder.serialize(generator, mapper); + } + if (this.order != null) { + generator.writeKey("order"); + this.order.serialize(generator, mapper); + } + if (this.format != null) { + generator.writeKey("format"); + generator.write(this.format); + } + } + + /** + * Builder for {@link CompositeValuesSource}. + */ + + protected abstract static class AbstractBuilder> extends ObjectBuilderBase { + + @Nullable + private String field; + + @Nullable + private Script script; + + @Nullable + private ValueType valueType; + + @Nullable + private Boolean missingBucket; + + @Nullable + private MissingOrder missingOrder; + + @Nullable + private SortOrder order; + + @Nullable + private String format; + + /** + * API name: {@code field} + */ + public final BuilderT field(@Nullable String field) { + this.field = field; + return self(); + } + + /** + * API name: {@code script} + */ + public final BuilderT script(@Nullable Script script) { + this.script = script; + return self(); + } + + /** + * API name: {@code script} + */ + public final BuilderT script(Function> fn) { + return this.script(fn.apply(new Script.Builder()).build()); + } + + /** + * API name: {@code valueType} + */ + public final BuilderT valueType(@Nullable ValueType valueType) { + this.valueType = valueType; + return self(); + } + + /** + * API name: {@code missingBucket} + */ + public final BuilderT missingBucket(@Nullable Boolean missingBucket) { + this.missingBucket = missingBucket; + return self(); + } + + /** + * API name: {@code missingOrder} + */ + public final BuilderT missingOrder(@Nullable MissingOrder missingOrder) { + this.missingOrder = missingOrder; + return self(); + } + + /** + * API name: {@code order} + */ + public final BuilderT order(@Nullable SortOrder order) { + this.order = order; + return self(); + } + + /** + * API name: {@code format} + */ + public final BuilderT format(@Nullable String format) { + this.format = format; + return self(); + } + + protected abstract BuilderT self(); + } + + protected static > void setupCompositeValuesSourceDeserializer( + ObjectDeserializer op + ) { + op.add(AbstractBuilder::field, JsonpDeserializer.stringDeserializer(), "field"); + op.add(AbstractBuilder::script, Script._DESERIALIZER, "script"); + op.add(AbstractBuilder::valueType, ValueType._DESERIALIZER, "value_type"); + op.add(AbstractBuilder::missingBucket, JsonpDeserializer.booleanDeserializer(), "missing_bucket"); + op.add(AbstractBuilder::missingOrder, MissingOrder._DESERIALIZER, "missing_order"); + op.add(AbstractBuilder::order, SortOrder._DESERIALIZER, "order"); + op.add(AbstractBuilder::format, JsonpDeserializer.stringDeserializer(), "format"); + } + +} diff --git a/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractSearchRequestIT.java b/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractSearchRequestIT.java index a34584f9f9..98914ec5c1 100644 --- a/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractSearchRequestIT.java +++ b/java-client/src/test/java11/org/opensearch/client/opensearch/integTest/AbstractSearchRequestIT.java @@ -10,12 +10,18 @@ import java.io.IOException; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.junit.Test; import org.opensearch.Version; import org.opensearch.client.opensearch._types.FieldValue; import org.opensearch.client.opensearch._types.SortOrder; +import org.opensearch.client.opensearch._types.aggregations.Aggregation; +import org.opensearch.client.opensearch._types.aggregations.CompositeAggregation; +import org.opensearch.client.opensearch._types.aggregations.CompositeAggregationSource; +import org.opensearch.client.opensearch._types.aggregations.CompositeBucket; import org.opensearch.client.opensearch._types.mapping.Property; import org.opensearch.client.opensearch._types.query_dsl.MatchQuery; import org.opensearch.client.opensearch._types.query_dsl.Query; @@ -62,6 +68,53 @@ public void shouldReturnSearchResults() throws Exception { ); } + @Test + public void shouldReturnSearchResultsWithCompositeAgg() throws Exception { + final String index = "search_request"; + createIndex(index); + + final Query query = Query.of( + q -> q.bool( + builder -> builder.filter(filter -> filter.term(TermQuery.of(term -> term.field("size").value(FieldValue.of("huge"))))) + ) + ); + + final Map comAggrSrcMap = new HashMap<>(); + CompositeAggregationSource compositeAggregationSource1 = new CompositeAggregationSource.Builder().terms( + termsAggrBuilder -> termsAggrBuilder.field("quantity").missingBucket(false).order(SortOrder.Asc) + ).build(); + comAggrSrcMap.put("quantity", compositeAggregationSource1); + + CompositeAggregation compAgg = new CompositeAggregation.Builder().sources(comAggrSrcMap).build(); + + Aggregation aggregation = new Aggregation.Builder().composite(compAgg).build(); + + final SearchRequest request = SearchRequest.of(r -> r.index(index).query(query).aggregations("my_buckets", aggregation)); + + final SearchResponse response = javaClient().search(request, ShopItem.class); + assertEquals(response.hits().hits().size(), 2); + for (Map.Entry entry : response.aggregations().entrySet()) { + CompositeAggregation compositeAggregation = entry.getValue().composite(); + assertEquals(2, compositeAggregation.buckets().size()); + assertEquals(1, Integer.parseInt(compositeAggregation.buckets().get(0).key().get("quantity").toString())); + assertEquals(1, compositeAggregation.buckets().get(0).docCount()); + assertEquals(2, Integer.parseInt(compositeAggregation.buckets().get(1).key().get("quantity").toString())); + assertEquals(1, compositeAggregation.buckets().get(1).docCount()); + } + List buckets = response.aggregations() + .entrySet() + .stream() + .filter(e -> e.getKey().equals("my_buckets")) + .map(e -> e.getValue().composite().buckets().array()) + .flatMap(List::stream) + .collect(Collectors.toList()); + assertEquals(2, buckets.size()); + assertEquals(1, Integer.parseInt(buckets.get(0).key().get("quantity").toString())); + assertEquals(1, buckets.get(0).docCount()); + assertEquals(2, Integer.parseInt(buckets.get(1).key().get("quantity").toString())); + assertEquals(1, buckets.get(1).docCount()); + } + @Test public void hybridSearchShouldReturnSearchResults() throws Exception { assumeTrue("Hybrid search is supported from 2.10.0", getServerVersion().onOrAfter(Version.V_2_10_0)); diff --git a/samples/src/main/java/org/opensearch/client/samples/Search.java b/samples/src/main/java/org/opensearch/client/samples/Search.java index 417ecba409..e6f02b8d92 100644 --- a/samples/src/main/java/org/opensearch/client/samples/Search.java +++ b/samples/src/main/java/org/opensearch/client/samples/Search.java @@ -10,14 +10,18 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.opensearch._types.FieldValue; import org.opensearch.client.opensearch._types.Refresh; +import org.opensearch.client.opensearch._types.SortOrder; import org.opensearch.client.opensearch._types.aggregations.Aggregate; import org.opensearch.client.opensearch._types.aggregations.Aggregation; +import org.opensearch.client.opensearch._types.aggregations.CompositeAggregation; +import org.opensearch.client.opensearch._types.aggregations.CompositeAggregationSource; import org.opensearch.client.opensearch._types.analysis.Analyzer; import org.opensearch.client.opensearch._types.analysis.CustomAnalyzer; import org.opensearch.client.opensearch._types.analysis.ShingleTokenFilter; @@ -106,6 +110,27 @@ public static void main(String[] args) { entry.getValue().sterms().buckets().array().forEach(b -> LOGGER.info("{} : {}", b.key(), b.docCount())); } + // Custom Aggregations + final Map comAggrSrcMap = new HashMap<>(); + CompositeAggregationSource compositeAggregationSource1 = new CompositeAggregationSource.Builder().terms( + termsAggrBuilder -> termsAggrBuilder.field("title.keyword").missingBucket(false).order(SortOrder.Asc) + ).build(); + comAggrSrcMap.put("titles", compositeAggregationSource1); + + CompositeAggregation compAgg = new CompositeAggregation.Builder().sources(comAggrSrcMap).build(); + Aggregation aggregation = new Aggregation.Builder().composite(compAgg).build(); + + SearchRequest request = SearchRequest.of( + r -> r.index(indexName) + .query(q -> q.match(m -> m.field("title").query(FieldValue.of("Document 1")))) + .aggregations("my_buckets", aggregation) + ); + SearchResponse response = client.search(request, IndexData.class); + for (Map.Entry entry : response.aggregations().entrySet()) { + LOGGER.info("Agg - {}", entry.getKey()); + entry.getValue().composite().buckets().array().forEach(b -> LOGGER.info("{} : {}", b.key(), b.docCount())); + } + // HybridSearch Query searchQuery = Query.of( h -> h.hybrid(