Skip to content

Commit

Permalink
derivative metric aggregation
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar committed Feb 13, 2017
1 parent f83a3af commit c04b6c9
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 1 deletion.
28 changes: 28 additions & 0 deletions src/ui/public/agg_types/controls/sub_agg.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<div ng-controller="aggParam.controller">
<div class="form-group">
<label>Metric</label>
<select
name="buckets_path"
ng-model="agg.params.buckets_path"
required
class="form-control">
<option
ng-repeat="respAgg in responseValueAggs track by respAgg.id"
value="{{respAgg.id}}"
ng-if="respAgg.type.name !== 'derivative'"
ng-disabled="rejectAgg(respAgg)"
ng-selected="agg.params.buckets_path === respAgg.id">
metric: {{safeMakeLabel(respAgg)}}
</option>
<option value="custom" ng-selected="agg.params.buckets_path === 'custom'">
Custom Metric
</option>
</select>
</div>
<ng-form name="customMetricForm" ng-if="agg.params.buckets_path === 'custom'" class="vis-editor-agg-order-agg">
<vis-editor-agg-params
agg="agg.params.customMetric"
group-name="'metrics'">
</vis-editor-agg-params>
</ng-form>
</div>
4 changes: 3 additions & 1 deletion src/ui/public/agg_types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import AggTypesMetricsStdDeviationProvider from 'ui/agg_types/metrics/std_deviat
import AggTypesMetricsCardinalityProvider from 'ui/agg_types/metrics/cardinality';
import AggTypesMetricsPercentilesProvider from 'ui/agg_types/metrics/percentiles';
import AggTypesMetricsPercentileRanksProvider from 'ui/agg_types/metrics/percentile_ranks';
import AggTypesMetricsDerivativeProvider from 'ui/agg_types/metrics/derivative';
import AggTypesBucketsDateHistogramProvider from 'ui/agg_types/buckets/date_histogram';
import AggTypesBucketsHistogramProvider from 'ui/agg_types/buckets/histogram';
import AggTypesBucketsRangeProvider from 'ui/agg_types/buckets/range';
Expand All @@ -34,7 +35,8 @@ export default function AggTypeService(Private) {
Private(AggTypesMetricsCardinalityProvider),
Private(AggTypesMetricsPercentilesProvider),
Private(AggTypesMetricsPercentileRanksProvider),
Private(AggTypesMetricsTopHitProvider)
Private(AggTypesMetricsTopHitProvider),
Private(AggTypesMetricsDerivativeProvider),
],
buckets: [
Private(AggTypesBucketsDateHistogramProvider),
Expand Down
142 changes: 142 additions & 0 deletions src/ui/public/agg_types/metrics/derivative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import AggTypesMetricsMetricAggTypeProvider from 'ui/agg_types/metrics/metric_agg_type';
import orderAggTemplate from 'ui/agg_types/controls/sub_agg.html';
import _ from 'lodash';
import $ from 'jquery';
import VisAggConfigProvider from 'ui/vis/agg_config';
import VisSchemasProvider from 'ui/vis/schemas';

export default function AggTypeMetricDerivativeProvider(Private) {
const DerivativeAggType = Private(AggTypesMetricsMetricAggTypeProvider);
const AggConfig = Private(VisAggConfigProvider);
const Schemas = Private(VisSchemasProvider);

const aggFilter = ['!top_hits', '!percentiles', '!median', '!std_dev'];
const orderAggSchema = (new Schemas([
{
group: 'none',
name: 'orderAgg',
title: 'Order Agg',
aggFilter: aggFilter
}
])).all[0];

return new DerivativeAggType({
name: 'derivative',
title: 'Derivative',
makeLabel: function (aggConfig) {
if (aggConfig.params.customMetric) {
let label = aggConfig.params.customMetric.makeLabel();
if (label.includes('Derivative of ')) {
label = '2. derivative of ' + label.substring('Derivative of '.length);
}
else if (label.includes('derivative of ')) {
label = (parseInt(label.substring(0, 1)) + 1) + label.substring(1);
}
else {
label = 'Derivative of ' + label;
}
return label;
}
const metric = aggConfig.vis.aggs.find(agg => agg.id === aggConfig.params.buckets_path);
return 'Derivative of ' + metric.makeLabel();
},
params: [
{
name: 'customMetric',
type: AggConfig,
default: null,
serialize: function (customMetric) {
return customMetric.toJSON();
},
deserialize: function (state, agg) {
return this.makeAgg(agg, state);
},
makeAgg: function (termsAgg, state) {
state = state || {};
state.schema = orderAggSchema;
const orderAgg = new AggConfig(termsAgg.vis, state);
orderAgg.id = termsAgg.id + '-orderAgg';
return orderAgg;
},
write: _.noop
},
{
name: 'buckets_path',
editor: orderAggTemplate,
controller: function ($scope, $element) {

$scope.safeMakeLabel = function (agg) {
try {
return agg.makeLabel();
} catch (e) {
return '- agg not valid -';
}
};

$scope.$watch('responseValueAggs', updateOrderAgg);
$scope.$watch('agg.params.buckets_path', updateOrderAgg);

$scope.$on('$destroy', function () {
if ($scope.aggForm && $scope.aggForm.agg) {
$scope.aggForm.agg.$setValidity('bucket', true);
}
});

// Returns true if the agg is not compatible with the terms bucket
$scope.rejectAgg = function (agg) {
// aggFilter elements all starts with a '!'
// so the index of agg.type.name in a filter is 1 if it is included
return Boolean(aggFilter.find((filter) => filter.indexOf(agg.type.name) === 1));
};

function checkBuckets() {
const buckets = $scope.vis.aggs.filter(agg => agg.schema.group === 'buckets');
const bucketIsHistogram = ['date_histogram', 'histogram'].includes(buckets[0].type.name);
const canUseDerivative = buckets.length === 1 && bucketIsHistogram;
if ($scope.aggForm.agg) $scope.aggForm.agg.$setValidity('bucket', canUseDerivative);
if (canUseDerivative) {
if (buckets[0].type.name === 'histogram') {
buckets[0].params.min_doc_count = 1;
}
else {
buckets[0].params.min_doc_count = 0;
}
}
}

function updateOrderAgg() {
const agg = $scope.agg;
const params = agg.params;
const bucketsPath = params.buckets_path;
const paramDef = agg.type.params.byName.customMetric;

checkBuckets();

// we aren't creating a custom aggConfig
if (bucketsPath !== 'custom') {
params.customMetric = null;
return;
}

params.customMetric = params.customMetric || paramDef.makeAgg(agg);
}
},
write: function (agg, output) {
const vis = agg.vis;
const orderAgg = agg.params.customMetric || vis.aggs.getResponseAggById(agg.params.buckets_path);

if (agg.params.customMetric && agg.params.customMetric.type.name !== 'count') {
output.parentAggs = (output.parentAggs || []).concat(orderAgg);
}

output.params = {};
if (orderAgg.type.name === 'count') {
output.params.buckets_path = '_count';
} else {
output.params.buckets_path = orderAgg.id;
}
}
}
]
});
}

0 comments on commit c04b6c9

Please sign in to comment.