Skip to content

Commit

Permalink
feat: Implement 'Composite Aggregation'
Browse files Browse the repository at this point in the history
Closes #47
  • Loading branch information
sudo-suhas committed Jun 18, 2018
1 parent 49d648d commit 8e909d9
Show file tree
Hide file tree
Showing 16 changed files with 875 additions and 1 deletion.
12 changes: 12 additions & 0 deletions docs/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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');
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 8e909d9

Please sign in to comment.