Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
style="display: none;">
</div>

<div ng-if="agg.error" class="form-group ng-scope">
<p class="vis-editor-agg-error ng-binding">
{{agg.error}}
</p>
</div>

<div ng-if="agg.schema.deprecate" class="form-group">
<p ng-show="agg.schema.deprecateMessage" class="vis-editor-agg-error">
{{ agg.schema.deprecateMessage }}
Expand Down
1 change: 1 addition & 0 deletions src/core_plugins/metric_vis/public/metric_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function MetricVisProvider(Private) {
name: 'metric',
title: 'Metric',
min: 1,
aggFilter: ['!derivative'],
defaults: [
{ type: 'count', schema: 'metric' }
]
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/tagcloud/public/tag_cloud_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ visTypes.register(function TagCloudProvider(Private) {
title: 'Tag Size',
min: 1,
max: 1,
aggFilter: ['!std_dev', '!percentiles', '!percentile_ranks'],
aggFilter: ['!std_dev', '!percentiles', '!percentile_ranks', '!derivative'],
defaults: [
{ schema: 'metric', type: 'count' }
]
Expand Down
128 changes: 128 additions & 0 deletions src/ui/public/agg_types/__tests__/metrics/derivative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import _ from 'lodash';
import expect from 'expect.js';
import ngMock from 'ng_mock';
import DerivativeProvider from 'ui/agg_types/metrics/derivative';
import VisProvider from 'ui/vis';
import StubbedIndexPattern from 'fixtures/stubbed_logstash_index_pattern';

describe('Derivative metric', function () {
let aggDsl;
let derivativeMetric;
let aggConfig;

function init(settings) {
ngMock.module('kibana');
ngMock.inject(function (Private) {
const Vis = Private(VisProvider);
const indexPattern = Private(StubbedIndexPattern);
derivativeMetric = Private(DerivativeProvider);

const params = settings || {
metricAgg: '1',
customMetric: null
};

const vis = new Vis(indexPattern, {
title: 'New Visualization',
type: 'metric',
params: {
fontSize: 60,
handleNoResults: true
},
aggs: [
{
id: '1',
type: 'count',
schema: 'metric'
},
{
id: '2',
type: 'derivative',
schema: 'metric',
params
}
],
listeners: {}
});

// Grab the aggConfig off the vis (we don't actually use the vis for anything else)
aggConfig = vis.aggs[1];
aggDsl = aggConfig.toDsl();
});
}

it('should return a label prefixed with Derivative of', function () {
init();
expect(derivativeMetric.makeLabel(aggConfig)).to.eql('Derivative of Count');
});

it('should return a label Derivative of max bytes', function () {
init({
metricAgg: 'custom',
customMetric: {
id:'1-orderAgg',
type: 'max',
params: { field: 'bytes' },
schema: 'orderAgg'
}
});
expect(derivativeMetric.makeLabel(aggConfig)).to.eql('Derivative of Max bytes');
});

it('should return a label prefixed with number of derivative', function () {
init({
metricAgg: 'custom',
customMetric: {
id:'2-orderAgg',
type: 'derivative',
params: {
buckets_path: 'custom',
customMetric: {
id:'2-orderAgg-orderAgg',
type: 'count',
schema: 'orderAgg'
}
},
schema: 'orderAgg'
}
});
expect(derivativeMetric.makeLabel(aggConfig)).to.eql('2. derivative of Count');
});

it('should set parent aggs', function () {
init({
metricAgg: 'custom',
customMetric: {
id:'2-metric',
type: 'max',
params: { field: 'bytes' },
schema: 'orderAgg'
}
});
expect(aggDsl.derivative.buckets_path).to.be('2-metric');
expect(aggDsl.parentAggs['2-metric'].max.field).to.be('bytes');
});

it('should set nested parent aggs', function () {
init({
metricAgg: 'custom',
customMetric: {
id:'2-metric',
type: 'derivative',
params: {
buckets_path: 'custom',
customMetric: {
id:'2-metric-metric',
type: 'max',
params: { field: 'bytes' },
schema: 'orderAgg'
}
},
schema: 'orderAgg'
}
});
expect(aggDsl.derivative.buckets_path).to.be('2-metric');
expect(aggDsl.parentAggs['2-metric'].derivative.buckets_path).to.be('2-metric-metric');
});

});
34 changes: 34 additions & 0 deletions src/ui/public/agg_types/__tests__/metrics/lib/make_nested_label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import expect from 'expect.js';
import { makeNestedLabel } from 'ui/agg_types/metrics/lib/make_nested_label';

describe('metric agg make_nested_label', function () {

function generateAggConfig(metricLabel) {
return {
params: {
customMetric: {
makeLabel: () => { return metricLabel; }
}
}
};
}

it('should return a metric label with prefix', function () {
const aggConfig = generateAggConfig('Count');
const label = makeNestedLabel(aggConfig, 'derivative');
expect(label).to.eql('Derivative of Count');
});

it('should return a numbered prefix', function () {
const aggConfig = generateAggConfig('Derivative of Count');
const label = makeNestedLabel(aggConfig, 'derivative');
expect(label).to.eql('2. derivative of Count');
});

it('should return a prefix with correct order', function () {
const aggConfig = generateAggConfig('3. derivative of Count');
const label = makeNestedLabel(aggConfig, 'derivative');
expect(label).to.eql('4. derivative of Count');
});

});
2 changes: 1 addition & 1 deletion src/ui/public/agg_types/buckets/terms.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default function TermsAggDefinition(Private) {
const createFilter = Private(AggTypesBucketsCreateFilterTermsProvider);
const routeBasedNotifier = Private(routeBasedNotifierProvider);

const aggFilter = ['!top_hits', '!percentiles', '!median', '!std_dev'];
const aggFilter = ['!top_hits', '!percentiles', '!median', '!std_dev', '!derivative'];
const orderAggSchema = (new Schemas([
{
group: 'none',
Expand Down
30 changes: 30 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,30 @@
<div ng-controller="aggParam.controller">
<div class="form-group">
<label>Metric</label>
<select
name="metricAgg"
ng-model="agg.params.metricAgg"
required
class="form-control">
<option
ng-repeat="respAgg in responseValueAggs track by respAgg.id"
value="{{respAgg.id}}"
ng-if="respAgg.type.name !== agg.type.name"
ng-disabled="isDisabledAgg(respAgg)"
ng-selected="agg.params.metricAgg === respAgg.id">
metric: {{safeMakeLabel(respAgg)}}
</option>
<option value="custom" ng-selected="agg.params.metricAgg === 'custom'">
Custom Metric
</option>
</select>
</div>
<div ng-if="agg.params.metricAgg === 'custom'" class="vis-editor-agg-order-agg">
<ng-form name="customMetricForm">
<vis-editor-agg-params
agg="agg.params.customMetric"
group-name="'metrics'">
</vis-editor-agg-params>
</ng-form>
</div>
</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
62 changes: 62 additions & 0 deletions src/ui/public/agg_types/metrics/derivative.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import AggTypesMetricsMetricAggTypeProvider from 'ui/agg_types/metrics/metric_agg_type';
import metricAggTemplate from 'ui/agg_types/controls/sub_agg.html';
import _ from 'lodash';
import VisAggConfigProvider from 'ui/vis/agg_config';
import VisSchemasProvider from 'ui/vis/schemas';
import { makeNestedLabel } from './lib/make_nested_label';
import { parentPipelineAggController } from './lib/parent_pipeline_agg_controller';
import { parentPipelineAggWritter } from './lib/parent_pipeline_agg_writter';

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

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

return new MetricAggType({
name: 'derivative',
title: 'Derivative',
makeLabel: agg => makeNestedLabel(agg, 'derivative'),
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 || { type: 'count' };
state.schema = orderAggSchema;
const metricAgg = new AggConfig(termsAgg.vis, state);
metricAgg.id = termsAgg.id + '-metric';
return metricAgg;
},
write: _.noop
},
{
name: 'buckets_path',
write: _.noop
},
{
name: 'metricAgg',
editor: metricAggTemplate,
default: 'custom',
controller: parentPipelineAggController,
write: parentPipelineAggWritter
}
]
});
}
23 changes: 23 additions & 0 deletions src/ui/public/agg_types/metrics/lib/make_nested_label.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import _ from 'lodash';

const makeNestedLabel = function (aggConfig, label) {
const uppercaseLabel = _.startCase(label);
if (aggConfig.params.customMetric) {
let metricLabel = aggConfig.params.customMetric.makeLabel();
if (metricLabel.includes(`${uppercaseLabel} of `)) {
metricLabel = metricLabel.substring(`${uppercaseLabel} of `.length);
metricLabel = `2. ${label} of ${metricLabel}`;
}
else if (metricLabel.includes(`${label} of `)) {
metricLabel = (parseInt(metricLabel.substring(0, 1)) + 1) + metricLabel.substring(1);
}
else {
metricLabel = `${uppercaseLabel} of ${metricLabel}`;
}
return metricLabel;
}
const metric = aggConfig.vis.aggs.find(agg => agg.id === aggConfig.params.metricAgg);
return `${uppercaseLabel} of ${metric.makeLabel()}`;
};

export { makeNestedLabel };
Loading