diff --git a/docs/overview/aggregations/elastic_aggregation_bucket_selector.md b/docs/overview/aggregations/elastic_aggregation_bucket_selector.md new file mode 100644 index 000000000..7e1a05e9b --- /dev/null +++ b/docs/overview/aggregations/elastic_aggregation_bucket_selector.md @@ -0,0 +1,22 @@ +--- +id: elastic_aggregation_bucket_selector +title: "Bucket Selector Aggregation" +--- + +This aggregation is a parent pipeline aggregation which executes a script which determines whether the current bucket will be retained in the parent multi-bucket aggregation. + +To create a `BucketSelector` aggregation do the following: +```scala +import zio.elasticsearch.aggregation.BucketSelectorAggregation +import zio.elasticsearch.ElasticAggregation.bucketSelectorAggregation +import zio.elasticsearch.script.Script + +val aggregation: BucketSelectorAggregation = bucketSelectorAggregation(name = "aggregationSelector", script = Script("params.value > 10"), bucketsPath = Map("value" -> "otherAggregation")) +``` + +If you want to add aggregation (on the same level), you can use `withAgg` method: +```scala +val multipleAggregations: MultipleAggregations = bucketSelectorAggregation(name = "aggregationSelector", script = Script("params.value > 10"), bucketsPath = Map("value" -> "otherAggregation")).withAgg(maxAggregation(name = "maxAggregation", field = Document.doubleField)) +``` + +You can find more information about `BucketSelector` aggregation [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-pipeline-bucket-selector-aggregation.html). diff --git a/docs/overview/aggregations/elastic_aggregation_bucket_sort.md b/docs/overview/aggregations/elastic_aggregation_bucket_sort.md new file mode 100644 index 000000000..2e6f72a74 --- /dev/null +++ b/docs/overview/aggregations/elastic_aggregation_bucket_sort.md @@ -0,0 +1,38 @@ +--- +id: elastic_aggregation_bucket_sort +title: "Bucket Sort Aggregation" +--- + +The `BucketSort` aggregation is a parent pipeline aggregation which sorts the buckets of its parent multi-bucket aggregation. Zero or more sort fields may be specified together with the corresponding sort order. + +To create a `BucketSort` aggregation do the following: +```scala +import zio.elasticsearch.aggregation.BucketSortAggregation +import zio.elasticsearch.ElasticAggregation.bucketSortAggregation + +val aggregation: BucketSortAggregation = bucketSortAggregation(name = "aggregationSort") +``` + +If you want to change the `from`, you can use `from` method: +```scala +val aggregationWithFrom: BucketSortAggregation = bucketSortAggregation(name = "aggregationSort").from(5) +``` + +If you want to change the `size`, you can use `size` method: +```scala +val aggregationWithSize: BucketSortAggregation = bucketSortAggregation(name = "aggregationSort").size(5) +``` + +If you want to change the `sort`, you can use `sort` method: +```scala +import zio.elasticsearch.query.sort.SortByField.{byCount, byKey} + +val aggregationWithSort: BucketSortAggregation = bucketSortAggregation(name = "aggregationSort").sort(byCount, byKey) +``` + +If you want to add aggregation (on the same level), you can use `withAgg` method: +```scala +val multipleAggregations: MultipleAggregations = bucketSortAggregation(name = "aggregationSort").withAgg(maxAggregation(name = "maxAggregation", field = Document.doubleField)) +``` + +You can find more information about `BucketSort` aggregation [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-pipeline-bucket-sort-aggregation.html). diff --git a/docs/overview/aggregations/elastic_aggregation_cardinality.md b/docs/overview/aggregations/elastic_aggregation_cardinality.md new file mode 100644 index 000000000..f807ffc02 --- /dev/null +++ b/docs/overview/aggregations/elastic_aggregation_cardinality.md @@ -0,0 +1,34 @@ +--- +id: elastic_aggregation_cardinality +title: "Cardinality Aggregation" +--- + +The `Cardinality` aggregation is a single-value metrics aggregation that calculates an approximate count of distinct values. + +In order to use the `Cardinality` aggregation import the following: +```scala +import zio.elasticsearch.aggregation.CardinalityAggregation +import zio.elasticsearch.ElasticAggregation.cardinalityAggregation +``` + +You can create a `Cardinality` aggregation using the `cardinalityAggregation` method in the following manner: +```scala +val aggregation: CardinalityAggregation = cardinalityAggregation(name = "cardinalityAggregation", field = "intField") +``` + +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `Cardinality` aggregation using the `cardinalityAggregation` method in the following manner: +```scala +val aggregation: CardinalityAggregation = cardinalityAggregation(name = "cardinalityAggregation", field = Document.intField) +``` + +If you want to change the `missing`, you can use `missing` method: +```scala +val aggregationWithMissing: CardinalityAggregation = cardinalityAggregation(name = "cardinalityAggregation", field = Document.intField).missing(10.0) +``` + +If you want to add aggregation (on the same level), you can use `withAgg` method: +```scala +val multipleAggregations: MultipleAggregations = cardinalityAggregation(name = "cardinalityAggregation", field = Document.intField).withAgg(maxAggregation(name = "maxAggregation", field = Document.doubleField)) +``` + +You can find more information about `Cardinality` aggregation [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-metrics-cardinality-aggregation.html). diff --git a/docs/overview/aggregations/elastic_aggregation_max.md b/docs/overview/aggregations/elastic_aggregation_max.md new file mode 100644 index 000000000..fde2e41a2 --- /dev/null +++ b/docs/overview/aggregations/elastic_aggregation_max.md @@ -0,0 +1,35 @@ +--- +id: elastic_aggregation_max +title: "Max Aggregation" +--- + +The `Max` aggregation is a single-value metrics aggregation that keeps track and returns the maximum value among the numeric values extracted from the aggregated documents. + +In order to use the `Max` aggregation import the following: +```scala +import zio.elasticsearch.aggregation.MaxAggregation +import zio.elasticsearch.ElasticAggregation.maxAggregation +``` + +You can create a `Max` aggregation using the `maxAggregation` method this way: +```scala +val aggregation: MaxAggregation = maxAggregation(name = "maxAggregation", field = "intField") +``` + +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `Max` aggregation using the `maxAggregation` method this way: +```scala +// Document.intField must be number value, because of Max aggregation +val aggregation: MaxAggregation = maxAggregation(name = "maxAggregation", field = Document.intField) +``` + +If you want to change the `missing`, you can use `missing` method: +```scala +val aggregationWithMissing: MaxAggregation = maxAggregation(name = "maxAggregation", field = Document.intField).missing(10.0) +``` + +If you want to add aggregation (on the same level), you can use `withAgg` method: +```scala +val multipleAggregations: MultipleAggregations = maxAggregation(name = "maxAggregation1", field = Document.intField).withAgg(maxAggregation(name = "maxAggregation2", field = Document.doubleField)) +``` + +You can find more information about `Max` aggregation [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-metrics-max-aggregation.html). diff --git a/docs/overview/aggregations/elastic_aggregation_terms.md b/docs/overview/aggregations/elastic_aggregation_terms.md index 98c1b5158..a42b6d9f8 100644 --- a/docs/overview/aggregations/elastic_aggregation_terms.md +++ b/docs/overview/aggregations/elastic_aggregation_terms.md @@ -3,4 +3,50 @@ id: elastic_aggregation_terms title: "Terms Aggregation" --- -TBD +This aggregation is a multi-bucket value source based aggregation where buckets are dynamically built - one per unique value. + +In order to use the `Terms` aggregation import the following: +```scala +import zio.elasticsearch.aggregation.TermsAggregation +import zio.elasticsearch.ElasticAggregation.termsAggregation +``` + +You can create a `Terms` aggregation using the `termsAggregation` method this way: +```scala +val aggregation: TermsAggregation = termsAggregation(name = "termsAggregation", field = "stringField.keyword") +``` + +You can create a [type-safe](https://lambdaworks.github.io/zio-elasticsearch/overview/overview_zio_prelude_schema) `Terms` aggregation using the `termsAggregation` method this way: +```scala +// Document.stringField must be string value, because of Terms aggregation +val aggregation: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField.keyword) +``` + +If you want to change the `order`, you can use `orderBy`, `orderByCountAsc`, `orderByCountDesc`, `orderByKeyAsc` or `orderByKeyDesc` method: +```scala +import zio.elasticsearch.aggregation.AggregationOrder +import zio.elasticsearch.query.sort.SortOrder.Asc + +val aggregationWithOrder1: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).orderBy(AggregationOrder("otherAggregation", Asc)) +val aggregationWithOrder2: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).orderByCountAsc +val aggregationWithOrder3: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).orderByCountDesc +val aggregationWithOrder4: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).orderByKeyAsc +val aggregationWithOrder5: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).orderByKeyDesc +``` + +If you want to change the `size`, you can use `size` method: +```scala +val aggregationWithSize: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).size(5) +``` + +If you want to add aggregation (on the same level), you can use `withAgg` method: +```scala +val multipleAggregations: MultipleAggregations = termsAggregation(name = "termsAggregation", field = Document.stringField).withAgg(maxAggregation(name = "maxAggregation", field = Document.intField)) +``` + +If you want to add another sub-aggregation, you can use `withSubAgg` method: +```scala +val aggregationWithSubAgg: TermsAggregation = termsAggregation(name = "termsAggregation", field = Document.stringField).withSubAgg(maxAggregation(name = "maxAggregation", field = Document.intField)) +``` + +You can find more information about `Terms` aggregation [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations-bucket-terms-aggregation.html#search-aggregations-bucket-terms-aggregation). diff --git a/docs/overview/elastic_aggregation.md b/docs/overview/elastic_aggregation.md index 80fa018d5..48ca535fc 100644 --- a/docs/overview/elastic_aggregation.md +++ b/docs/overview/elastic_aggregation.md @@ -3,11 +3,94 @@ id: elastic_aggregation title: "Overview" --- -In order to execute Elasticsearch aggregation requests... +In order to execute Elasticsearch aggregation requests, you first must specify the type of the aggregation along with the corresponding parameters for that type. Aggregations are described with the `ElasticAggregation` data type, which can be constructed from the DSL methods found under the following import: ```scala import zio.elasticsearch.ElasticAggregation._ ``` -TBD +Aggregation DSL methods that require a field solely accept field types that are defined as Elasticsearch primitives. +You can pass field names simply as strings, or you can use the type-safe aggregation methods that make use of ZIO Schema's accessors. +An example with a `max` aggregation is shown below: + +```scala +import zio.elasticsearch.ElasticAggregation._ + +final case class User(id: Int, name: String, age: Int) + +object User { + implicit val schema: Schema.CaseClass3[Int, String, Int, User] = + DeriveSchema.gen[User] + + val (id, name, age) = schema.makeAccessors(FieldAccessorBuilder) +} + +maxAggregation(name = "maxAggregation", field = "age") + +// type-safe method +maxAggregation(name = "maxAggregation", field = User.age) +``` + +You can also represent a field from nested structures with type-safe aggregation methods, using the `/` operator on accessors: + +```scala +import zio._ +import zio.elasticsearch._ +import zio.elasticsearch.ElasticAggregation._ +import zio.schema.annotation.fieldName +import zio.schema.{DeriveSchema, Schema} + +final case class Name( + @fieldName("first_name") + firstName: String, + @fieldName("last_name") + lastName: String +) + +object Name { + implicit val schema: Schema.CaseClass2[String, String, Name] = DeriveSchema.gen[Name] + + val (firstName, lastName) = schema.makeAccessors(FieldAccessorBuilder) +} + +final case class User(id: String, name: Name, email: String, age: Int) + +object User { + implicit val schema: Schema.CaseClass4[String, Name, String, Int, User] = + DeriveSchema.gen[User] + + val (id, name, email, age) = schema.makeAccessors(FieldAccessorBuilder) +} + +termsAggregation(name = "termsAggregation", field = "name.first_name") + +// type-safe method +termsAggregation(name = "termsAggregation", field = User.name / Name.firstName) +``` + +Accessors also have a `suffix` method, in case you want to use one in aggregations: + +```scala +ElasticAggregation.cardinality(name = "cardinalityAggregation", field = "email.keyword") + +// type-safe method +ElasticAggregation.cardinality(name = "cardinalityAggregation", field = User.email.suffix("keyword")) +``` + +In case the suffix is `"keyword"` or `"raw"` you can use `keyword` and `raw` methods respectively. + +Now, after describing an aggregation, you can pass it to the `aggregate`/`search` method to obtain the `ElasticRequest` corresponding to that aggregation: + +```scala +import zio.elasticsearch.ElasticAggregation._ +import zio.elasticsearch.ElasticQuery._ + +ElasticRequest.aggregate(index = IndexName("index"), aggregation = termsAggregation(name = "termsAggregation", field = "name.first_name.keyword")) +ElasticRequest.search(IndexName("index"), query = matchAll, aggregation = termsAggregation(name = "termsAggregation", field = "name.first_name.keyword")) + +// type-safe methods +ElasticRequest.aggregate(index = IndexName("index"), aggregation = termsAggregation(name = "termsAggregation", field = User.name / Name.firstName.keyword)) +ElasticRequest.search(index = IndexName("index"), query = matchAll, aggregation = termsAggregation(name = "termsAggregation", field = User.name / Name.firstName.keyword)) + +``` diff --git a/docs/overview/elastic_query.md b/docs/overview/elastic_query.md index 644f10f6a..8f6899501 100644 --- a/docs/overview/elastic_query.md +++ b/docs/overview/elastic_query.md @@ -16,6 +16,8 @@ You can pass field names simply as strings, or you can use the type-safe query m An example with a `term` query is shown below: ```scala +import zio.elasticsearch.ElasticQuery._ + final case class User(id: Int, name: String) object User { diff --git a/docs/overview/queries/elastic_query_bool.md b/docs/overview/queries/elastic_query_bool.md index 7f4577a30..ceef50a1c 100644 --- a/docs/overview/queries/elastic_query_bool.md +++ b/docs/overview/queries/elastic_query_bool.md @@ -55,4 +55,4 @@ If you want to change the `minimum_should_match` parameter, you can use the `min val queryWithMinimumShouldMatch: BoolQuery = should(contains(field = Document.name, value = "a")).minimumShouldMatch(2) ``` -You can find more information about Boolean query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-bool-query.html). +You can find more information about `Bool` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-bool-query.html). diff --git a/docs/overview/queries/elastic_query_exists.md b/docs/overview/queries/elastic_query_exists.md index fbcd2009e..37f7d1f1d 100644 --- a/docs/overview/queries/elastic_query_exists.md +++ b/docs/overview/queries/elastic_query_exists.md @@ -26,4 +26,4 @@ If you want to change the `boost`, you can use `boost` method: val queryWithBoost: ExistsQuery = exists(field = "name").boost(2.0) ``` -You can find more information about Exists query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html#query-dsl-exists-query). +You can find more information about `Exists` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-query.html#query-dsl-exists-query). diff --git a/docs/overview/queries/elastic_query_range.md b/docs/overview/queries/elastic_query_range.md index ee679beb2..6f0b612c0 100644 --- a/docs/overview/queries/elastic_query_range.md +++ b/docs/overview/queries/elastic_query_range.md @@ -51,4 +51,4 @@ If you want to change `lte` (less than or equal to), you can use the `lte` metho val queryWithLte: RangeQuery = range(field = Document.intField).lte(100) ``` -You can find more information about Range query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-range-query.html). +You can find more information about `Range` query [here](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/query-dsl-range-query.html). diff --git a/docs/overview/queries/elastic_query_term.md b/docs/overview/queries/elastic_query_term.md index 63ade9c25..e0812188b 100644 --- a/docs/overview/queries/elastic_query_term.md +++ b/docs/overview/queries/elastic_query_term.md @@ -5,7 +5,7 @@ title: "Term Query" The `Term` query returns documents that contain an exact term in a provided field. -In order to use the `TermQuery` query import the following: +In order to use the `Term` query import the following: ```scala import zio.elasticsearch.query.TermQuery import zio.elasticsearch.ElasticQuery._ diff --git a/docs/overview/queries/elastic_query_terms.md b/docs/overview/queries/elastic_query_terms.md index e8115d5f0..f8cddd250 100644 --- a/docs/overview/queries/elastic_query_terms.md +++ b/docs/overview/queries/elastic_query_terms.md @@ -6,10 +6,10 @@ title: "Terms Query" The `Terms` query returns documents that contain one or more exact terms in a provided field. This query is the same as the Term query, except you can search for multiple values. -In order to use the `TermsQuery` query import the following: +In order to use the `Terms` query import the following: ```scala import zio.elasticsearch.query.TermsQuery -import zio.elasticsearch.ElasticQuery._ +import zio.elasticsearch.ElasticQuery.terms ``` You can create a `Terms` query using the `terms` method this way: diff --git a/modules/library/src/main/scala/zio/elasticsearch/ElasticAggregation.scala b/modules/library/src/main/scala/zio/elasticsearch/ElasticAggregation.scala index 222417d99..e50bd4692 100644 --- a/modules/library/src/main/scala/zio/elasticsearch/ElasticAggregation.scala +++ b/modules/library/src/main/scala/zio/elasticsearch/ElasticAggregation.scala @@ -43,6 +43,18 @@ object ElasticAggregation { ): BucketSelectorAggregation = BucketSelector(name = name, script = script, bucketsPath = bucketsPath) + /** + * Constructs an instance of [[zio.elasticsearch.aggregation.BucketSortAggregation]] using the specified parameters. + * + * @param name + * aggregation name + * @return + * an instance of [[zio.elasticsearch.aggregation.BucketSortAggregation]] that represents bucket sort aggregation to + * be performed. + */ + final def bucketSortAggregation(name: String): BucketSortAggregation = + BucketSort(name = name, sortBy = Chunk.empty, from = None, size = None) + /** * Constructs a type-safe instance of [[zio.elasticsearch.aggregation.CardinalityAggregation]] using the specified * parameters. It calculates an approximate count of distinct values. @@ -73,6 +85,34 @@ object ElasticAggregation { final def cardinalityAggregation(name: String, field: String): CardinalityAggregation = Cardinality(name = name, field = field, missing = None) + /** + * Constructs a type-safe instance of [[zio.elasticsearch.aggregation.MaxAggregation]] using the specified parameters. + * + * @param name + * aggregation name + * @param field + * the type-safe field for which max aggregation will be executed + * @tparam A + * expected number type + * @return + * an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] that represents max aggregation to be performed. + */ + final def maxAggregation[A: Numeric](name: String, field: Field[_, A]): MaxAggregation = + Max(name = name, field = field.toString, missing = None) + + /** + * Constructs an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] using the specified parameters. + * + * @param name + * aggregation name + * @param field + * the field for which max aggregation will be executed + * @return + * an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] that represents max aggregation to be performed. + */ + final def maxAggregation(name: String, field: String): MaxAggregation = + Max(name = name, field = field, missing = None) + /** * Constructs an empty instance of the [[zio.elasticsearch.aggregation.MultipleAggregations]]. * @@ -110,44 +150,4 @@ object ElasticAggregation { */ final def termsAggregation(name: String, field: String): TermsAggregation = Terms(name = name, field = field, order = Chunk.empty, subAggregations = Chunk.empty, size = None) - - /** - * Constructs a type-safe instance of [[zio.elasticsearch.aggregation.MaxAggregation]] using the specified parameters. - * - * @param name - * aggregation name - * @param field - * the type-safe field for which max aggregation will be executed - * @tparam A - * expected number type - * @return - * an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] that represents max aggregation to be performed. - */ - final def maxAggregation[A: Numeric](name: String, field: Field[_, A]): MaxAggregation = - Max(name = name, field = field.toString, missing = None) - - /** - * Constructs an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] using the specified parameters. - * - * @param name - * aggregation name - * @param field - * the field for which max aggregation will be executed - * @return - * an instance of [[zio.elasticsearch.aggregation.MaxAggregation]] that represents max aggregation to be performed. - */ - final def maxAggregation(name: String, field: String): MaxAggregation = - Max(name = name, field = field, missing = None) - - /** - * Constructs an instance of [[zio.elasticsearch.aggregation.BucketSortAggregation]] using the specified parameters. - * - * @param name - * aggregation name - * @return - * an instance of [[zio.elasticsearch.aggregation.BucketSortAggregation]] that represents bucket sort aggregation to - * be performed. - */ - final def bucketSortAggregation(name: String): BucketSortAggregation = - BucketSort(name = name, sortBy = Chunk.empty, from = None, size = None) } diff --git a/website/sidebars.js b/website/sidebars.js index 7c25fccce..64376675a 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -35,6 +35,10 @@ module.exports = { label: 'Elastic Aggregation', items: [ 'overview/elastic_aggregation', + 'overview/aggregations/elastic_aggregation_bucket_selector', + 'overview/aggregations/elastic_aggregation_bucket_sort', + 'overview/aggregations/elastic_aggregation_cardinality', + 'overview/aggregations/elastic_aggregation_max', 'overview/aggregations/elastic_aggregation_terms', ], },