elastic-builder
Version:
A JavaScript implementation of the elasticsearch Query DSL
169 lines (143 loc) • 5.01 kB
JavaScript
'use strict';
const has = require('lodash.has');
const isEmpty = require('lodash.isempty');
const { checkType, recursiveToJSON } = require('./util');
/**
* Base class implementation for all aggregation types.
*
* **NOTE:** Instantiating this directly should not be required.
* However, if you wish to add a custom implementation for whatever reason,
* this class should be extended and used, as validation against the class
* type is present in various places.
*
* @param {string} name
* @param {string} aggType Type of aggregation
*
* @throws {Error} if `name` is empty
* @throws {Error} if `aggType` is empty
*/
class Aggregation {
// eslint-disable-next-line require-jsdoc
constructor(name, aggType) {
if (isEmpty(aggType))
throw new Error('Aggregation `aggType` cannot be empty');
this._name = name;
this.aggType = aggType;
this._aggs = {};
this._aggsDef = this._aggs[aggType] = {};
this._nestedAggs = [];
}
// TODO: Investigate case when getter for aggregation will be required
/**
* Sets name for aggregation.
*
* @param {string} name returns `this` so that calls can be chained.
* @returns {Aggregation}
*/
name(name) {
this._name = name;
return this;
}
/**
* Sets nested aggregations.
* This method can be called multiple times in order to set multiple nested aggregations.
*
* @param {Aggregation} agg Any valid {@link Aggregation}
* @returns {Aggregation} returns `this` so that calls can be chained.
* @throws {TypeError} If `agg` is not an instance of `Aggregation`
*/
aggregation(agg) {
checkType(agg, Aggregation);
// Possible to check for Global aggregation?
// Global aggregation can only be at the top level.
this._nestedAggs.push(agg);
return this;
}
/**
* Sets nested aggregation.
* This method can be called multiple times in order to set multiple nested aggregations.
*
* @param {Aggregation} agg Any valid {@link Aggregation}
* @returns {Aggregation} returns `this` so that calls can be chained.
*/
agg(agg) {
return this.aggregation(agg);
}
/**
* Sets multiple nested aggregation items.
* This method accepts an array to set multiple nested aggregations in one call.
*
* @param {Array<Aggregation>} aggs Array of valid {@link Aggregation} items
* @returns {Aggregation} returns `this` so that calls can be chained.
* @throws {TypeError} If `aggs` is not an instance of `Array`
* @throws {TypeError} If `aggs` contains instances not of type `Aggregation`
*/
aggregations(aggs) {
checkType(aggs, Array);
aggs.forEach(agg => this.aggregation(agg));
return this;
}
/**
* Sets multiple nested aggregation items.
* Alias for method `aggregations`
*
* @param {Array<Aggregation>} aggs Array of valid {@link Aggregation} items
* @returns {Aggregation} returns `this` so that calls can be chained.
* @throws {TypeError} If `aggs` is not an instance of `Array`
* @throws {TypeError} If `aggs` contains instances not of type `Aggregation`
*/
aggs(aggs) {
return this.aggregations(aggs);
}
/**
* You can associate a piece of metadata with individual aggregations at request time
* that will be returned in place at response time.
*
* @param {Object} meta
* @returns {Aggregation} returns `this` so that calls can be chained.
*/
meta(meta) {
this._aggs.meta = meta;
return this;
}
/**
* Internal helper function for determining the aggregation name.
*
* @returns {string} Aggregation name
* @private
*/
_aggsName() {
if (!isEmpty(this._name)) return this._name;
if (has(this._aggsDef, 'field')) {
return `agg_${this.aggType}_${this._aggsDef.field}`;
}
// At this point, it would be difficult to construct a unique
// aggregation name. Error out.
throw new Error('Aggregation name could not be determined');
}
/**
* Build and returns DSL representation of the `Aggregation` class instance.
*
* @returns {Object} returns an Object which maps to the elasticsearch query DSL
*/
getDSL() {
return this.toJSON();
}
/**
* Override default `toJSON` to return DSL representation for the `aggregation` query.
*
* @override
* @returns {Object} returns an Object which maps to the elasticsearch query DSL
*/
toJSON() {
const mainAggs = recursiveToJSON(this._aggs);
if (!isEmpty(this._nestedAggs)) {
mainAggs.aggs = Object.assign(
{},
...recursiveToJSON(this._nestedAggs)
);
}
return { [this._aggsName()]: mainAggs };
}
}
module.exports = Aggregation;