kibana-123
Version:
Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic
327 lines (269 loc) • 9.17 kB
JavaScript
/**
* @name AggConfig
*
* @description This class represents an aggregation, which is displayed in the left-hand nav of
* the Visualize app.
*/
import _ from 'lodash';
import RegistryFieldFormatsProvider from 'ui/registry/field_formats';
export default function AggConfigFactory(Private, fieldTypeFilter) {
let fieldFormats = Private(RegistryFieldFormatsProvider);
function AggConfig(vis, opts) {
let self = this;
self.id = String(opts.id || AggConfig.nextId(vis.aggs));
self.vis = vis;
self._opts = opts = (opts || {});
self.enabled = typeof opts.enabled === 'boolean' ? opts.enabled : true;
// setters
self.type = opts.type;
self.schema = opts.schema;
// resolve the params
self.fillDefaults(opts.params);
}
/**
* Ensure that all of the objects in the list have ids, the objects
* and list are modified by reference.
*
* @param {array[object]} list - a list of objects, objects can be anything really
* @return {array} - the list that was passed in
*/
AggConfig.ensureIds = function (list) {
let have = [];
let haveNot = [];
list.forEach(function (obj) {
(obj.id ? have : haveNot).push(obj);
});
let nextId = AggConfig.nextId(have);
haveNot.forEach(function (obj) {
obj.id = String(nextId++);
});
return list;
};
/**
* Calculate the next id based on the ids in this list
*
* @return {array} list - a list of objects with id properties
*/
AggConfig.nextId = function (list) {
return 1 + list.reduce(function (max, obj) {
return Math.max(max, +obj.id || 0);
}, 0);
};
Object.defineProperties(AggConfig.prototype, {
type: {
get: function () {
return this.__type;
},
set: function (type) {
if (this.__typeDecorations) {
_.forOwn(this.__typeDecorations, function (prop, name) {
delete this[name];
}, this);
}
if (_.isString(type)) {
type = AggConfig.aggTypes.byName[type];
}
if (type && _.isFunction(type.decorateAggConfig)) {
this.__typeDecorations = type.decorateAggConfig();
Object.defineProperties(this, this.__typeDecorations);
}
this.__type = type;
}
},
schema: {
get: function () {
return this.__schema;
},
set: function (schema) {
if (_.isString(schema)) {
schema = this.vis.type.schemas.all.byName[schema];
}
this.__schema = schema;
}
}
});
/**
* Write the current values to this.params, filling in the defaults as we go
*
* @param {object} [from] - optional object to read values from,
* used when initializing
* @return {undefined}
*/
AggConfig.prototype.fillDefaults = function (from) {
let self = this;
from = from || self.params || {};
let to = self.params = {};
self.getAggParams().forEach(function (aggParam) {
let val = from[aggParam.name];
if (val == null) {
if (aggParam.default == null) return;
if (!_.isFunction(aggParam.default)) {
val = aggParam.default;
} else {
val = aggParam.default(self);
if (val == null) return;
}
}
if (aggParam.deserialize) {
let isTyped = _.isFunction(aggParam.type);
let isType = isTyped && (val instanceof aggParam.type);
let isObject = !isTyped && _.isObject(val);
let isDeserialized = (isType || isObject);
if (!isDeserialized) {
val = aggParam.deserialize(val, self);
}
to[aggParam.name] = val;
return;
}
to[aggParam.name] = _.cloneDeep(val);
});
};
/**
* Clear the parameters for this aggConfig
*
* @return {object} the new params object
*/
AggConfig.prototype.resetParams = function () {
let field;
const fieldOptions = this.getFieldOptions();
if (fieldOptions) {
field = fieldOptions.byName[this.fieldName()] || null;
}
return this.fillDefaults({ row: this.params.row, field: field });
};
AggConfig.prototype.write = function () {
return this.type.params.write(this);
};
AggConfig.prototype.createFilter = function (key) {
if (!_.isFunction(this.type.createFilter)) {
throw new TypeError('The "' + this.type.title + '" aggregation does not support filtering.');
}
let field = this.getField();
let label = this.getFieldDisplayName();
if (field && !field.filterable) {
let message = 'The "' + label + '" field can not be used for filtering.';
if (field.scripted) {
message = 'The "' + label + '" field is scripted and can not be used for filtering.';
}
throw new TypeError(message);
}
return this.type.createFilter(this, key);
};
/**
* Hook into param onRequest handling, and tell the aggConfig that it
* is being sent to elasticsearch.
*
* @return {[type]} [description]
*/
AggConfig.prototype.requesting = function () {
let self = this;
self.type && self.type.params.forEach(function (param) {
if (param.onRequest) param.onRequest(self);
});
};
/**
* Convert this aggConfig to its dsl syntax.
*
* Adds params and adhoc subaggs to a pojo, then returns it
*
* @param {AggConfig} aggConfig - the config object to convert
* @return {void|Object} - if the config has a dsl representation, it is
* returned, else undefined is returned
*/
AggConfig.prototype.toDsl = function () {
if (this.type.hasNoDsl) return;
let output = this.write();
let configDsl = {};
configDsl[this.type.dslName || this.type.name] = output.params;
// if the config requires subAggs, write them to the dsl as well
if (output.subAggs) {
let subDslLvl = configDsl.aggs || (configDsl.aggs = {});
output.subAggs.forEach(function nestAdhocSubAggs(subAggConfig) {
subDslLvl[subAggConfig.id] = subAggConfig.toDsl();
});
}
return configDsl;
};
AggConfig.prototype.toJSON = function () {
let self = this;
let params = self.params;
let outParams = _.transform(self.getAggParams(), function (out, aggParam) {
let val = params[aggParam.name];
// don't serialize undefined/null values
if (val == null) return;
if (aggParam.serialize) val = aggParam.serialize(val, self);
if (val == null) return;
// to prevent accidental leaking, we will clone all complex values
out[aggParam.name] = _.cloneDeep(val);
}, {});
return {
id: self.id,
enabled: self.enabled,
type: self.type && self.type.name,
schema: self.schema && self.schema.name,
params: outParams
};
};
AggConfig.prototype.getAggParams = function () {
return [].concat(
(this.type) ? this.type.params.raw : [],
(this.schema) ? this.schema.params.raw : []
);
};
AggConfig.prototype.getResponseAggs = function () {
if (!this.type) return;
return this.type.getResponseAggs(this) || [this];
};
AggConfig.prototype.getValue = function (bucket) {
return this.type.getValue(this, bucket);
};
AggConfig.prototype.getKey = function (bucket, key) {
return this.type.getKey(bucket, key, this);
};
AggConfig.prototype.getFieldDisplayName = function () {
let field = this.getField();
return field ? (field.displayName || this.fieldName()) : '';
};
AggConfig.prototype.getField = function () {
return this.params.field;
};
AggConfig.prototype.makeLabel = function () {
if (this.params.customLabel) {
return this.params.customLabel;
}
if (!this.type) return '';
let pre = (_.get(this.vis, 'params.mode') === 'percentage') ? 'Percentage of ' : '';
return pre += this.type.makeLabel(this);
};
AggConfig.prototype.getIndexPattern = function () {
return this.vis.indexPattern;
};
AggConfig.prototype.getFieldOptions = function () {
const fieldParamType = this.type && this.type.params.byName.field;
if (!fieldParamType || !fieldParamType.getFieldOptions) {
return null;
}
return fieldParamType.getFieldOptions(this);
};
AggConfig.prototype.fieldFormatter = function (contentType, defaultFormat) {
let format = this.type && this.type.getFormat(this);
if (format) return format.getConverterFor(contentType);
return this.fieldOwnFormatter(contentType, defaultFormat);
};
AggConfig.prototype.fieldOwnFormatter = function (contentType, defaultFormat) {
let field = this.getField();
let format = field && field.format;
if (!format) format = defaultFormat;
if (!format) format = fieldFormats.getDefaultInstance('string');
return format.getConverterFor(contentType);
};
AggConfig.prototype.fieldName = function () {
let field = this.getField();
return field ? field.name : '';
};
AggConfig.prototype.fieldIsTimeField = function () {
let timeFieldName = this.vis.indexPattern.timeFieldName;
return timeFieldName && this.fieldName() === timeFieldName;
};
return AggConfig;
};