@spalger/kibana
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
300 lines (247 loc) • 8.74 kB
JavaScript
define(function (require) {
return function AggConfigFactory(Private, fieldTypeFilter) {
var _ = require('lodash');
var fieldFormats = Private(require('ui/registry/field_formats'));
function AggConfig(vis, opts) {
var self = this;
self.id = String(opts.id || AggConfig.nextId(vis.aggs));
self.vis = vis;
self._opts = opts = (opts || {});
// 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) {
var have = [];
var haveNot = [];
list.forEach(function (obj) {
(obj.id ? have : haveNot).push(obj);
});
var 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) {
var self = this;
from = from || self.params || {};
var to = self.params = {};
self.getAggParams().forEach(function (aggParam) {
var 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) {
var isTyped = _.isFunction(aggParam.type);
var isType = isTyped && (val instanceof aggParam.type);
var isObject = !isTyped && _.isObject(val);
var 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 () {
var fieldParam = this.type && this.type.params.byName.field;
var field;
if (fieldParam) {
var prevField = this.params.field;
var fieldOpts = fieldTypeFilter(this.vis.indexPattern.fields, fieldParam.filterFieldTypes);
field = _.contains(fieldOpts, prevField) ? prevField : 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.');
}
var field = this.field();
var label = this.fieldDisplayName();
if (field && !field.filterable) {
var 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 elasticsearc.
*
* @return {[type]} [description]
*/
AggConfig.prototype.requesting = function () {
var self = this;
self.type && self.type.params.forEach(function (param) {
if (param.onRequest) param.onRequest(self);
});
};
/**
* Convert this aggConfig to it's 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;
var output = this.write();
var 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) {
var subDslLvl = configDsl.aggs || (configDsl.aggs = {});
output.subAggs.forEach(function nestAdhocSubAggs(subAggConfig) {
subDslLvl[subAggConfig.id] = subAggConfig.toDsl();
});
}
return configDsl;
};
AggConfig.prototype.toJSON = function () {
var self = this;
var params = self.params;
var outParams = _.transform(self.getAggParams(), function (out, aggParam) {
var 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,
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.makeLabel = function () {
if (!this.type) return '';
return this.type.makeLabel(this);
};
AggConfig.prototype.field = function () {
return this.params.field;
};
AggConfig.prototype.fieldFormatter = function (contentType) {
var field = this.field();
var format = field && field.format;
var strFormat = fieldFormats.getDefaultInstance('string');
if (this.type) {
format = this.type.getFormat(this) || format;
}
return (format || strFormat).getConverterFor(contentType);
};
AggConfig.prototype.fieldName = function () {
var field = this.field();
return field ? field.name : '';
};
AggConfig.prototype.fieldDisplayName = function () {
var field = this.field();
return field ? (field.displayName || this.fieldName()) : '';
};
AggConfig.prototype.fieldIsTimeField = function () {
var timeFieldName = this.vis.indexPattern.timeFieldName;
return timeFieldName && this.fieldName() === timeFieldName;
};
return AggConfig;
};
});