From 8e909d9e6121c0677e3a3641f4187c7672f4c059 Mon Sep 17 00:00:00 2001 From: Suhas Karanth Date: Mon, 18 Jun 2018 11:30:04 +0530 Subject: [PATCH] feat: Implement 'Composite Aggregation' Closes #47 --- docs/documentation.yml | 12 + .../date-histogram-values-source.js | 86 ++++++ .../histogram-values-source.js | 50 ++++ .../composite-agg-values-sources/index.js | 7 + .../terms-values-source.js | 33 +++ .../values-source-base.js | 118 ++++++++ .../composite-aggregation.js | 137 +++++++++ src/aggregations/bucket-aggregations/index.js | 1 + src/index.d.ts | 260 ++++++++++++++++++ src/index.js | 4 + test/_macros.js | 2 +- .../date-histogram-values-source.test.js | 35 +++ .../histogram-values-source.test.js | 33 +++ .../terms-values-source.test.js | 46 ++++ test/aggregations-test/composite-agg.test.js | 37 +++ test/index.test.js | 15 + 16 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 src/aggregations/bucket-aggregations/composite-agg-values-sources/date-histogram-values-source.js create mode 100644 src/aggregations/bucket-aggregations/composite-agg-values-sources/histogram-values-source.js create mode 100644 src/aggregations/bucket-aggregations/composite-agg-values-sources/index.js create mode 100644 src/aggregations/bucket-aggregations/composite-agg-values-sources/terms-values-source.js create mode 100644 src/aggregations/bucket-aggregations/composite-agg-values-sources/values-source-base.js create mode 100644 src/aggregations/bucket-aggregations/composite-aggregation.js create mode 100644 test/aggregations-test/composite-agg-values-sources-test/date-histogram-values-source.test.js create mode 100644 test/aggregations-test/composite-agg-values-sources-test/histogram-values-source.test.js create mode 100644 test/aggregations-test/composite-agg-values-sources-test/terms-values-source.test.js create mode 100644 test/aggregations-test/composite-agg.test.js diff --git a/docs/documentation.yml b/docs/documentation.yml index 418c17d0..ed8fc2ad 100644 --- a/docs/documentation.yml +++ b/docs/documentation.yml @@ -95,6 +95,18 @@ toc: - BucketAggregationBase - AdjacencyMatrixAggregation - ChildrenAggregation + - CompositeAggregation + - name: Values Source + description: | + The sources parameter controls the sources that should be used to build + the composite buckets. There are three different types of values source: + - [`Terms`](#termsvaluessource) + - [`Histogram`](#histogramvaluessource) + - [`Date Histogram`](#datehistogramvaluessource) + - ValuesSourceBase + - TermsValuesSource + - HistogramValuesSource + - DateHistogramValuesSource - HistogramAggregationBase - DateHistogramAggregation - RangeAggregationBase diff --git a/src/aggregations/bucket-aggregations/composite-agg-values-sources/date-histogram-values-source.js b/src/aggregations/bucket-aggregations/composite-agg-values-sources/date-histogram-values-source.js new file mode 100644 index 00000000..2ee7a6a8 --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-agg-values-sources/date-histogram-values-source.js @@ -0,0 +1,86 @@ +'use strict'; + +const isNil = require('lodash.isnil'); + +const ValuesSourceBase = require('./values-source-base'); + +const REF_URL = + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_date_histogram'; + +/** + * `DateHistogramValuesSource` is a source for the `CompositeAggregation` that + * handles date histograms. It works very similar to a histogram aggregation + * with a slightly different syntax. + * + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_date_histogram) + * + * @example + * const valueSrc = esb.CompositeAggregation.dateHistogramValuesSource( + * 'date', // name + * 'timestamp', // field + * '1d' // interval + * ); + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {string|number=} interval Interval to generate histogram over. + * + * @extends ValuesSourceBase + */ +class DateHistogramValuesSource extends ValuesSourceBase { + // eslint-disable-next-line require-jsdoc + constructor(name, field, interval) { + super('date_histogram', REF_URL, name, field); + + if (!isNil(interval)) this._opts.interval = interval; + } + + /** + * Sets the histogram interval. Buckets are generated based on this interval value. + * + * @param {string|number} interval Interval to generate histogram over. + * @returns {DateHistogramValuesSource} returns `this` so that calls can be chained + */ + interval(interval) { + this._opts.interval = interval; + return this; + } + + /** + * Sets the date time zone + * + * Date-times are stored in Elasticsearch in UTC. By default, all bucketing + * and rounding is also done in UTC. The `time_zone` parameter can be used + * to indicate that bucketing should use a different time zone. + * + * @param {string} tz Time zone. Time zones may either be specified + * as an ISO 8601 UTC offset (e.g. +01:00 or -08:00) or as a timezone id, + * an identifier used in the TZ database like America/Los_Angeles. + * @returns {DateHistogramValuesSource} returns `this` so that calls can be chained + */ + timeZone(tz) { + this._opts.time_zone = tz; + return this; + } + + /** + * Sets the format expression for `key_as_string` in response buckets. + * If no format is specified, then it will use the first format specified + * in the field mapping. + * + * @example + * const valueSrc = esb.CompositeAggregation.valuesSource + * .dateHistogram('date', 'timestamp', '1d') + * .format('yyyy-MM-dd'); + * + * @param {string} fmt Format mask to apply on aggregation response. + * For Date Histograms, supports expressive [date format pattern](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html#date-format-pattern) + * @returns {DateHistogramValuesSource} returns `this` so that calls can be chained + */ + format(fmt) { + this._opts.format = fmt; + return this; + } +} + +module.exports = DateHistogramValuesSource; diff --git a/src/aggregations/bucket-aggregations/composite-agg-values-sources/histogram-values-source.js b/src/aggregations/bucket-aggregations/composite-agg-values-sources/histogram-values-source.js new file mode 100644 index 00000000..5748a903 --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-agg-values-sources/histogram-values-source.js @@ -0,0 +1,50 @@ +'use strict'; + +const isNil = require('lodash.isnil'); + +const ValuesSourceBase = require('./values-source-base'); + +const REF_URL = + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_histogram'; + +/** + * `HistogramValuesSource` is a source for the `CompositeAggregation` that handles + * histograms. It works very similar to a histogram aggregation with a slightly + * different syntax. + * + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_histogram) + * + * @example + * const valueSrc = esb.CompositeAggregation.histogramValuesSource( + * 'histo', // name + * 'price', // field + * 5 // interval + * ); + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {number=} interval Interval to generate histogram over. + * + * @extends ValuesSourceBase + */ +class HistogramValuesSource extends ValuesSourceBase { + // eslint-disable-next-line require-jsdoc + constructor(name, field, interval) { + super('histogram', REF_URL, name, field); + + if (!isNil(interval)) this._opts.interval = interval; + } + + /** + * Sets the histogram interval. Buckets are generated based on this interval value. + * + * @param {number} interval Interval to generate histogram over. + * @returns {HistogramValuesSource} returns `this` so that calls can be chained + */ + interval(interval) { + this._opts.interval = interval; + return this; + } +} + +module.exports = HistogramValuesSource; diff --git a/src/aggregations/bucket-aggregations/composite-agg-values-sources/index.js b/src/aggregations/bucket-aggregations/composite-agg-values-sources/index.js new file mode 100644 index 00000000..fb71174b --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-agg-values-sources/index.js @@ -0,0 +1,7 @@ +'use strict'; + +exports.ValuesSourceBase = require('./values-source-base'); + +exports.TermsValuesSource = require('./terms-values-source'); +exports.HistogramValuesSource = require('./histogram-values-source'); +exports.DateHistogramValuesSource = require('./date-histogram-values-source'); diff --git a/src/aggregations/bucket-aggregations/composite-agg-values-sources/terms-values-source.js b/src/aggregations/bucket-aggregations/composite-agg-values-sources/terms-values-source.js new file mode 100644 index 00000000..c3693ee4 --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-agg-values-sources/terms-values-source.js @@ -0,0 +1,33 @@ +'use strict'; + +const ValuesSourceBase = require('./values-source-base'); + +const REF_URL = + 'https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_terms'; + +/** + * `TermsValuesSource` is a source for the `CompositeAggregation` that handles + * terms. It works very similar to a terms aggregation with a slightly different + * syntax. + * + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html#_terms) + * + * @example + * const valueSrc = esb.CompositeAggregation.termsValuesSource('product').script({ + * source: "doc['product'].value", + * lang: 'painless' + * }); + * + * @param {string} name + * @param {string=} field The field to aggregate on + * + * @extends ValuesSourceBase + */ +class TermsValuesSource extends ValuesSourceBase { + // eslint-disable-next-line require-jsdoc + constructor(name, field) { + super('terms', REF_URL, name, field); + } +} + +module.exports = TermsValuesSource; diff --git a/src/aggregations/bucket-aggregations/composite-agg-values-sources/values-source-base.js b/src/aggregations/bucket-aggregations/composite-agg-values-sources/values-source-base.js new file mode 100644 index 00000000..59bb81ee --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-agg-values-sources/values-source-base.js @@ -0,0 +1,118 @@ +'use strict'; + +const isEmpty = require('lodash.isempty'); +const isNil = require('lodash.isnil'); + +const { util: { invalidParam, recursiveToJSON } } = require('../../../core'); + +const invalidOrderParam = invalidParam('', 'order', "'asc' or 'desc'"); + +/** + * Base class implementation for all Composite Aggregation values sources. + * + * **NOTE:** Instantiating this directly should not be required. + * + * @param {string} valueSrcType Type of value source + * @param {string} refUrl Elasticsearch reference URL + * @param {string} name + * @param {string=} field The field to aggregate on + * + * @throws {Error} if `name` is empty + * @throws {Error} if `valueSrcType` is empty + */ +class ValuesSourceBase { + // eslint-disable-next-line require-jsdoc + constructor(valueSrcType, refUrl, name, field) { + if (isEmpty(valueSrcType)) + throw new Error('ValuesSourceBase `valueSrcType` cannot be empty'); + + this._name = name; + this._valueSrcType = valueSrcType; + this._refUrl = refUrl; + + this._body = {}; + this._opts = this._body[valueSrcType] = {}; + + if (!isNil(field)) this._opts.field = field; + } + + /** + * Field to use for this source. + * + * @param {string} field a valid field name + * @returns {ValuesSourceBase} returns `this` so that calls can be chained + */ + field(field) { + this._opts.field = field; + return this; + } + + /** + * Script to use for this source. + * + * @param {Script|Object|string} script + * @returns {ValuesSourceBase} returns `this` so that calls can be chained + * @throws {TypeError} If `script` is not an instance of `Script` + */ + script(script) { + this._opts.script = script; + return this; + } + + /** + * Specifies the type of values produced by this source, e.g. `string` or + * `date`. + * + * @param {string} valueType + * @returns {ValuesSourceBase} returns `this` so that calls can be chained + */ + valueType(valueType) { + this._opts.value_type = valueType; + return this; + } + + /** + * Order specifies the order in the values produced by this source. It can + * be either `asc` or `desc`. + * + * @param {string} order The `order` option can have the following values. + * `asc`, `desc` to sort in ascending, descending order respectively. + * @returns {ValuesSourceBase} returns `this` so that calls can be chained. + */ + order(order) { + if (isNil(order)) invalidOrderParam(order, this._refUrl); + + const orderLower = order.toLowerCase(); + if (orderLower !== 'asc' && orderLower !== 'desc') { + invalidOrderParam(order, this._refUrl); + } + + this._opts.order = orderLower; + return this; + } + + /** + * Missing specifies the value to use when the source finds a missing value + * in a document. + * + * @param {string} value + * @returns {ValuesSourceBase} returns `this` so that calls can be chained + */ + missing(value) { + this._opts.missing = value; + return this; + } + + /** + * Override default `toJSON` to return DSL representation for the Composite + * Aggregation values source. + * + * @override + * @returns {Object} returns an Object which maps to the elasticsearch query DSL + */ + toJSON() { + return { [this._name]: recursiveToJSON(this._body) }; + } +} + +module.exports = ValuesSourceBase; diff --git a/src/aggregations/bucket-aggregations/composite-aggregation.js b/src/aggregations/bucket-aggregations/composite-aggregation.js new file mode 100644 index 00000000..fe5dae4f --- /dev/null +++ b/src/aggregations/bucket-aggregations/composite-aggregation.js @@ -0,0 +1,137 @@ +'use strict'; + +const { + Aggregation, + util: { checkType, constructorWrapper } +} = require('../../core'); + +const { + ValuesSourceBase, + TermsValuesSource, + HistogramValuesSource, + DateHistogramValuesSource +} = require('./composite-agg-values-sources'); + +/** + * CompositeAggregation is a multi-bucket values source based aggregation that + * can be used to calculate unique composite values from source documents. + * + * Unlike the other multi-bucket aggregation the composite aggregation can be + * used to paginate **all** buckets from a multi-level aggregation efficiently. + * This aggregation provides a way to stream **all** buckets of a specific + * aggregation similarly to what scroll does for documents. + * + * [Elasticsearch reference](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-composite-aggregation.html) + * + * @example + * const reqBody = esb.requestBodySearch() + * .agg( + * esb.compositeAggregation('my_buckets') + * .sources( + * esb.CompositeAggregation.termsValuesSource('product', 'product') + * ) + * ) + * + * NOTE: This query was added in elasticsearch v6.1. + * + * @param {string} name a valid aggregation name + * + * @extends Aggregation + */ +class CompositeAggregation extends Aggregation { + // eslint-disable-next-line require-jsdoc + constructor(name) { + super(name, 'composite'); + + this._aggsDef.sources = []; + } + + /** + * Specifies the Composite Aggregation values sources to use in the + * aggregation. + * + * @example + * const { CompositeAggregation } = esb; + * const reqBody = esb.requestBodySearch() + * .agg( + * esb.compositeAggregation('my_buckets') + * .sources( + * CompositeAggregation.dateHistogramValuesSource( + * 'date', + * 'timestamp', + * '1d' + * ), + * CompositeAggregation.termsValuesSource('product', 'product') + * ) + * ); + * + * @param {...ValuesSourceBase} sources + * @returns {CompositeAggregation} returns `this` so that calls can be chained + * @throws {TypeError} If any of the rest parameters `sources` is not an + * instance of `ValuesSourceBase` + */ + sources(...sources) { + sources.forEach(valueSrc => checkType(valueSrc, ValuesSourceBase)); + + this._aggsDef.sources = this._aggsDef.sources.concat(sources); + return this; + } + + /** + * Defines how many composite buckets should be returned. Each composite + * bucket is considered as a single bucket so setting a size of 10 will + * return the first 10 composite buckets created from the values source. The + * response contains the values for each composite bucket in an array + * containing the values extracted from each value source. + * + * @param {number} size + * @returns {CompositeAggregation} returns `this` so that calls can be chained + */ + size(size) { + this._aggsDef.size = size; + return this; + } + + /** + * The `after` parameter can be used to retrieve the composite buckets that + * are after the last composite buckets returned in a previous round. + * + * @example + * const { CompositeAggregation } = esb; + * const reqBody = esb.requestBodySearch().agg( + * esb.compositeAggregation('my_buckets') + * .size(2) + * .sources( + * CompositeAggregation.dateHistogramValuesSource( + * 'date', + * 'timestamp', + * '1d' + * ).order('desc'), + * CompositeAggregation.termsValuesSource('product', 'product').order('asc') + * ) + * .after({ date: 1494288000000, product: 'mad max' }) + * ); + * + * @param {Object} afterKey + * @returns {CompositeAggregation} returns `this` so that calls can be chained + */ + after(afterKey) { + this._aggsDef.after = afterKey; + return this; + } +} + +CompositeAggregation.TermsValuesSource = TermsValuesSource; +CompositeAggregation.termsValuesSource = constructorWrapper(TermsValuesSource); + +CompositeAggregation.HistogramValuesSource = HistogramValuesSource; +CompositeAggregation.histogramValuesSource = constructorWrapper( + HistogramValuesSource +); + +CompositeAggregation.DateHistogramValuesSource = DateHistogramValuesSource; +CompositeAggregation.dateHistogramValuesSource = constructorWrapper( + DateHistogramValuesSource +); + +module.exports = CompositeAggregation; diff --git a/src/aggregations/bucket-aggregations/index.js b/src/aggregations/bucket-aggregations/index.js index 8b332e8e..acc5b5d9 100644 --- a/src/aggregations/bucket-aggregations/index.js +++ b/src/aggregations/bucket-aggregations/index.js @@ -8,6 +8,7 @@ exports.SignificantAggregationBase = require('./significant-aggregation-base'); exports.AdjacencyMatrixAggregation = require('./adjacency-matrix-aggregation'); exports.ChildrenAggregation = require('./children-aggregation'); +exports.CompositeAggregation = require('./composite-aggregation'); exports.DateHistogramAggregation = require('./date-histogram-aggregation'); exports.DateRangeAggregation = require('./date-range-aggregation'); exports.DiversifiedSamplerAggregation = require('./diversified-sampler-aggregation'); diff --git a/src/index.d.ts b/src/index.d.ts index ce331395..e5cf67c0 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -4514,6 +4514,266 @@ declare namespace esb { */ export function childrenAggregation(name: string): ChildrenAggregation; + /** + * CompositeAggregation is a multi-bucket values source based aggregation that + * can be used to calculate unique composite values from source documents. + * + * Unlike the other multi-bucket aggregation the composite aggregation can be + * used to paginate **all** buckets from a multi-level aggregation efficiently. + * This aggregation provides a way to stream **all** buckets of a specific + * aggregation similarly to what scroll does for documents. + * + * NOTE: This query was added in elasticsearch v6.1. + * + * @param {string} name a valid aggregation name + * + * @extends Aggregation + */ + export class CompositeAggregation extends Aggregation { + constructor(name: string); + + /** + * Specifies the Composite Aggregation values sources to use in the + * aggregation. + * + * @param {...ValuesSourceBase} sources + * @throws {TypeError} If any of the rest parameters `sources` is not an + * instance of `ValuesSourceBase` + */ + sources(...sources: CompositeAggregation.ValuesSourceBase[]): this; + + /** + * Defines how many composite buckets should be returned. Each composite + * bucket is considered as a single bucket so setting a size of 10 will + * return the first 10 composite buckets created from the values source. The + * response contains the values for each composite bucket in an array + * containing the values extracted from each value source. + * + * @param {number} size + */ + size(size: number): this; + + /** + * The `after` parameter can be used to retrieve the composite buckets that + * are after the last composite buckets returned in a previous round. + * + * @param {Object} afterKey + */ + after(afterKey: object): this; + } + + /** + * CompositeAggregation is a multi-bucket values source based aggregation that + * can be used to calculate unique composite values from source documents. + * + * Unlike the other multi-bucket aggregation the composite aggregation can be + * used to paginate **all** buckets from a multi-level aggregation efficiently. + * This aggregation provides a way to stream **all** buckets of a specific + * aggregation similarly to what scroll does for documents. + * + * NOTE: This query was added in elasticsearch v6.1. + * + * @param {string} name a valid aggregation name + */ + export function compositeAggregation(name: string): CompositeAggregation; + + namespace CompositeAggregation { + /** + * Base class implementation for all Composite Aggregation values sources. + * + * **NOTE:** Instantiating this directly should not be required. + * + * @param {string} valueSrcType Type of value source. + * @param {string} refUrl Elasticsearch reference URL + * @param {string} name + * @param {string=} field The field to aggregate on + * + * @throws {Error} if `name` is empty + * @throws {Error} if `valueSrcType` is empty + */ + class ValuesSourceBase { + constructor( + valueSrcType: string, + refUrl: string, + name: string, + field?: string + ); + + /** + * Field to use for this source. + * + * @param {string} field a valid field name + */ + field(field: string): this; + + /** + * Script to use for this source. + * + * @param {Script|Object|string} script + * @throws {TypeError} If `script` is not an instance of `Script` + */ + script(script: Script | object | string): this; + + /** + * Specifies the type of values produced by this source, e.g. `string` or + * `date`. + * + * @param {string} valueType + */ + valueType(valueType: string): this; + + /** + * Order specifies the order in the values produced by this source. It can + * be either `asc` or `desc`. + * + * @param {string} order The `order` option can have the following values. + * `asc`, `desc` to sort in ascending, descending order respectively.. + */ + order(order: 'asc' | 'desc'): this; + + /** + * Missing specifies the value to use when the source finds a missing value + * in a document. + * + * @param {string} value + */ + missing(value: string): this; + + /** + * Override default `toJSON` to return DSL representation for the Composite + * Aggregation values source. + * + * @override + */ + toJSON(): object; + } + + /** + * `TermsValuesSource` is a source for the `CompositeAggregation` that handles + * terms. It works very similar to a terms aggregation with a slightly different + * syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + * + * @extends ValuesSourceBase + */ + export class TermsValuesSource extends ValuesSourceBase { + constructor(name: string, field?: string); + } + + /** + * `TermsValuesSource` is a source for the `CompositeAggregation` that handles + * terms. It works very similar to a terms aggregation with a slightly different + * syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + */ + export function termsValuesSource( + name: string, + field?: string + ): TermsValuesSource; + + /** + * `HistogramValuesSource` is a source for the `CompositeAggregation` that handles + * histograms. It works very similar to a histogram aggregation with a slightly + * different syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {number=} interval Interval to generate histogram over. + * + * @extends ValuesSourceBase + */ + export class HistogramValuesSource extends ValuesSourceBase { + constructor(name: string, field?: string, interval?: number); + + /** + * Sets the histogram interval. Buckets are generated based on this interval value. + * + * @param {number} interval Interval to generate histogram over. + */ + interval(interval: number): this; + } + + /** + * `HistogramValuesSource` is a source for the `CompositeAggregation` that handles + * histograms. It works very similar to a histogram aggregation with a slightly + * different syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {number=} interval Interval to generate histogram over. + */ + export function histogramValuesSource( + name: string, + field?: string, + interval?: number + ): HistogramValuesSource; + + /** + * `DateHistogramValuesSource` is a source for the `CompositeAggregation` that + * handles date histograms. It works very similar to a histogram aggregation + * with a slightly different syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {string|number=} interval Interval to generate histogram over. + * + * @extends ValuesSourceBase + */ + export class DateHistogramValuesSource extends ValuesSourceBase { + constructor(name: string, field?: string, interval?: string | number); + + /** + * Sets the histogram interval. Buckets are generated based on this interval value. + * + * @param {string|number} interval Interval to generate histogram over. + */ + interval(interval: string | number): this; + + /** + * Sets the date time zone + * + * Date-times are stored in Elasticsearch in UTC. By default, all bucketing + * and rounding is also done in UTC. The `time_zone` parameter can be used + * to indicate that bucketing should use a different time zone. + * + * @param {string} tz Time zone. Time zones may either be specified + * as an ISO 8601 UTC offset (e.g. +01:00 or -08:00) or as a timezone id, + * an identifier used in the TZ database like America/Los_Angeles. + */ + timeZone(tz: string): this; + + /** + * Sets the format expression for `key_as_string` in response buckets. + * If no format is specified, then it will use the first format specified + * in the field mapping. + * + * @param {string} fmt Format mask to apply on aggregation response. + * For Date Histograms, supports expressive [date format pattern](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-daterange-aggregation.html#date-format-pattern) + */ + format(fmt: string): this; + } + + + /** + * `DateHistogramValuesSource` is a source for the `CompositeAggregation` that + * handles date histograms. It works very similar to a histogram aggregation + * with a slightly different syntax. + * + * @param {string} name + * @param {string=} field The field to aggregate on + * @param {string|number=} interval Interval to generate histogram over. + */ + export function dateHistogramValuesSource( + name: string, + field?: string, + interval?: string | number + ): DateHistogramValuesSource; + } + /** * The `HistogramAggregationBase` provides support for common options used across * various histogram `Aggregation` implementations like Histogram Aggregation, diff --git a/src/index.js b/src/index.js index d7b580f8..b82285a0 100644 --- a/src/index.js +++ b/src/index.js @@ -101,6 +101,7 @@ const { bucketAggregations: { AdjacencyMatrixAggregation, ChildrenAggregation, + CompositeAggregation, DateHistogramAggregation, DateRangeAggregation, DiversifiedSamplerAggregation, @@ -368,6 +369,9 @@ exports.adjacencyMatrixAggregation = constructorWrapper( exports.ChildrenAggregation = ChildrenAggregation; exports.childrenAggregation = constructorWrapper(ChildrenAggregation); +exports.CompositeAggregation = CompositeAggregation; +exports.compositeAggregation = constructorWrapper(CompositeAggregation); + exports.DateHistogramAggregation = DateHistogramAggregation; exports.dateHistogramAggregation = constructorWrapper(DateHistogramAggregation); diff --git a/test/_macros.js b/test/_macros.js index 3a71b34b..58188144 100644 --- a/test/_macros.js +++ b/test/_macros.js @@ -111,7 +111,7 @@ export function simpleExpect(keyName, propValue) { export function nameTypeExpectStrategy(name, type, defaultDef) { return (keyName, propValue) => ({ [name]: { - [type]: Object.assign({ [keyName]: propValue }, defaultDef) + [type]: Object.assign({}, defaultDef, { [keyName]: propValue }) } }); } diff --git a/test/aggregations-test/composite-agg-values-sources-test/date-histogram-values-source.test.js b/test/aggregations-test/composite-agg-values-sources-test/date-histogram-values-source.test.js new file mode 100644 index 00000000..ebcbe07f --- /dev/null +++ b/test/aggregations-test/composite-agg-values-sources-test/date-histogram-values-source.test.js @@ -0,0 +1,35 @@ +import test from 'ava'; +import { CompositeAggregation } from '../../../src'; +import { + setsAggType, + nameTypeExpectStrategy, + makeSetsOptionMacro +} from '../../_macros'; + +const { DateHistogramValuesSource } = CompositeAggregation; + +const getInstance = (...args) => + new DateHistogramValuesSource('my_val_src', ...args); + +const setsOption = makeSetsOptionMacro( + getInstance, + nameTypeExpectStrategy('my_val_src', 'date_histogram') +); + +test('constructor sets arguments', t => { + const value = getInstance('my_field', '1d').toJSON(); + const expected = { + my_val_src: { + date_histogram: { + field: 'my_field', + interval: '1d' + } + } + }; + t.deepEqual(value, expected); +}); + +test(setsAggType, DateHistogramValuesSource, 'date_histogram'); +test(setsOption, 'interval', { param: 5 }); +test(setsOption, 'timeZone', { param: 'America/Los_Angeles' }); +test(setsOption, 'format', { param: 'yyyy-MM-dd' }); diff --git a/test/aggregations-test/composite-agg-values-sources-test/histogram-values-source.test.js b/test/aggregations-test/composite-agg-values-sources-test/histogram-values-source.test.js new file mode 100644 index 00000000..19bfc093 --- /dev/null +++ b/test/aggregations-test/composite-agg-values-sources-test/histogram-values-source.test.js @@ -0,0 +1,33 @@ +import test from 'ava'; +import { CompositeAggregation } from '../../../src'; +import { + setsAggType, + nameTypeExpectStrategy, + makeSetsOptionMacro +} from '../../_macros'; + +const { HistogramValuesSource } = CompositeAggregation; + +const getInstance = (...args) => + new HistogramValuesSource('my_val_src', ...args); + +const setsOption = makeSetsOptionMacro( + getInstance, + nameTypeExpectStrategy('my_val_src', 'histogram') +); + +test('constructor sets arguments', t => { + const value = getInstance('my_field', 10).toJSON(); + const expected = { + my_val_src: { + histogram: { + field: 'my_field', + interval: 10 + } + } + }; + t.deepEqual(value, expected); +}); + +test(setsAggType, HistogramValuesSource, 'histogram'); +test(setsOption, 'interval', { param: 5 }); diff --git a/test/aggregations-test/composite-agg-values-sources-test/terms-values-source.test.js b/test/aggregations-test/composite-agg-values-sources-test/terms-values-source.test.js new file mode 100644 index 00000000..ba61b96a --- /dev/null +++ b/test/aggregations-test/composite-agg-values-sources-test/terms-values-source.test.js @@ -0,0 +1,46 @@ +import test from 'ava'; +import { Script } from '../../../src'; +import { + TermsValuesSource, + ValuesSourceBase +} from '../../../src/aggregations/bucket-aggregations/composite-agg-values-sources'; +import { + setsAggType, + validatedCorrectly, + nameTypeExpectStrategy, + makeSetsOptionMacro +} from '../../_macros'; + +const getInstance = field => new TermsValuesSource('my_val_src', field); + +const setsOption = makeSetsOptionMacro( + getInstance, + nameTypeExpectStrategy('my_val_src', 'terms') +); + +test('base class type cannot be empty', t => { + const err = t.throws(() => new ValuesSourceBase()); + t.is(err.message, 'ValuesSourceBase `valueSrcType` cannot be empty'); +}); + +test('constructor sets arguments', t => { + const value = getInstance('my_field').toJSON(); + const expected = { + my_val_src: { + terms: { field: 'my_field' } + } + }; + t.deepEqual(value, expected); +}); + +test(setsAggType, TermsValuesSource, 'terms'); +test(validatedCorrectly, getInstance, 'order', ['asc', 'desc']); +test(setsOption, 'missing', { param: 42 }); +test(setsOption, 'field', { param: 'my_field' }); +test(setsOption, 'script', { + param: new Script() + .lang('groovy') + .file('calculate-score') + .params({ my_modifier: 2 }) +}); +test(setsOption, 'valueType', { param: 'date' }); diff --git a/test/aggregations-test/composite-agg.test.js b/test/aggregations-test/composite-agg.test.js new file mode 100644 index 00000000..48019748 --- /dev/null +++ b/test/aggregations-test/composite-agg.test.js @@ -0,0 +1,37 @@ +import test from 'ava'; +import { CompositeAggregation } from '../../src'; +import { + illegalParamType, + setsAggType, + nameTypeExpectStrategy, + makeSetsOptionMacro +} from '../_macros'; + +const getInstance = () => new CompositeAggregation('my_cmpt_agg'); + +const dateHistoValSrc = CompositeAggregation.dateHistogramValuesSource( + 'date', + 'timestamp', + '1d' +); +const termsValSrc = CompositeAggregation.termsValuesSource( + 'product', + 'product' +); + +const setsOption = makeSetsOptionMacro( + getInstance, + nameTypeExpectStrategy('my_cmpt_agg', 'composite', { sources: [] }) +); + +test(setsAggType, CompositeAggregation, 'composite', { sources: [] }); +test(illegalParamType, getInstance(), 'sources', 'ValuesSourceBase'); + +test(setsOption, 'sources', { + param: [dateHistoValSrc, termsValSrc], + paramValue: [dateHistoValSrc, termsValSrc] +}); +test(setsOption, 'size', { param: 2 }); +test(setsOption, 'after', { + param: { date: 1494288000000, product: 'mad max' } +}); diff --git a/test/index.test.js b/test/index.test.js index 9252467f..2367731b 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -223,6 +223,9 @@ test('aggregations are exported', t => { t.truthy(esb.ChildrenAggregation); t.truthy(esb.childrenAggregation); + t.truthy(esb.CompositeAggregation); + t.truthy(esb.compositeAggregation); + t.truthy(esb.DateHistogramAggregation); t.truthy(esb.dateHistogramAggregation); @@ -326,6 +329,18 @@ test('aggregations are exported', t => { t.truthy(esb.matrixStatsAggregation); }); +test('Composite Aggregation values sources are exported', t => { + const { CompositeAggregation } = esb; + t.truthy(CompositeAggregation.TermsValuesSource); + t.truthy(CompositeAggregation.termsValuesSource); + + t.truthy(CompositeAggregation.HistogramValuesSource); + t.truthy(CompositeAggregation.histogramValuesSource); + + t.truthy(CompositeAggregation.DateHistogramValuesSource); + t.truthy(CompositeAggregation.dateHistogramValuesSource); +}); + test('suggesters are exported', t => { t.truthy(esb.TermSuggester); t.truthy(esb.termSuggester);