Created an AdhocVis factory to make non-saved visualizations easier

This commit is contained in:
Rashid Khan 2014-05-16 15:47:02 -07:00
parent fa6633b7fd
commit 82fb6c3822
8 changed files with 172 additions and 56 deletions

View file

@ -17,7 +17,10 @@ define(function (require) {
require('state_management/app_state');
require('services/timefilter');
require('apps/visualize/saved_visualizations/_adhoc_vis');
var app = require('modules').get('app/discover', [
'kibana/services',
'kibana/notify',
'kibana/courier'
]);
@ -40,7 +43,7 @@ define(function (require) {
app.controller('discover', function ($scope, config, courier, $route, savedSearches, savedVisualizations,
Notifier, $location, globalState, AppState, timefilter) {
Notifier, $location, globalState, AppState, timefilter, AdhocVis) {
var notify = new Notifier({
location: 'Discover'
@ -139,6 +142,7 @@ define(function (require) {
// with the results
searchSource.onResults().then(function onResults(resp) {
var complete = notify.event('on results');
$scope.hits = resp.hits.total;
$scope.rows = resp.hits.hits;
$scope.rows.forEach(function (hit) {
hit._formatted = _.mapValues(hit._source, function (value, name) {
@ -147,20 +151,6 @@ define(function (require) {
hit._formatted._source = angular.toJson(hit._source);
});
$scope.chart = !!resp.aggregations ? {
label: 'Events over time',
xAxisLabel: 'DateTime',
yAxisLabel: 'Hits',
layers: [
{
key: 'somekey',
values: _.map(resp.aggregations.events.buckets, function (bucket) {
return { y: bucket.doc_count, x: bucket.key_as_string };
})
}
]
} : undefined;
complete();
return searchSource.onResults().then(onResults);
}).catch(function (err) {
@ -377,25 +367,38 @@ define(function (require) {
if ($scope.vis) return;
searchSource.disable();
var prom = savedVisualizations.tempForDiscover(searchSource)
.then(function (vis) {
if ($scope.vis !== prom) return;
$scope.vis = vis;
var config = vis.segment.configs.pop() || {};
config.agg = 'date_histogram';
config.field = $scope.opts.timefield;
config.min_doc_count = 0;
vis.segment.configs.push(config);
// enable the source, but wait for the visualization to be ready before running
searchSource.enable();
var vis = new AdhocVis({
searchSource: searchSource,
type: 'histogram',
listeners: {
onClick: function (e) {
console.log(e);
}
},
config: {
metric: {
configs: [{
agg: 'count',
}]
},
segment: {
configs: [{
agg: 'date_histogram',
field: $scope.opts.timefield,
min_doc_count: 0,
}]
},
group: { configs: [] },
split: { configs: [] },
}
});
// enable the source, but wait for the visualization to be ready before running
searchSource.enable();
// set it to the promise, so that we don't try to fetch it again
$scope.vis = prom;
return prom;
$scope.vis = vis;
return vis;
};
init();

View file

@ -20,6 +20,8 @@
<div class="container-fluid">
<div class="row">
<div class="discover-hits"><strong>{{hits || 0}}</strong> hits</div>
<div class="col-md-2 sidebar-container">
<disc-field-chooser
fields="fields"

View file

@ -73,13 +73,20 @@
overflow-x: hidden;
}
.discover-timechart {
margin-top: -20px;
display: block;
height: 150px;
height: 200px;
}
.discover-hits {
background-color: #ecf0f1;
float: right;
padding: 5px 10px;
border-bottom-left-radius: 4px;
}
.discover-table {
margin-top: -30px;
overflow-y: auto;
overflow-x: auto;
margin-top: 10px;
padding-left: 0px !important;
padding-right: 0px !important;
}

View file

@ -7,14 +7,22 @@
}
.discover-timechart {
margin-top: -20px; // TODO: Remove this when the vis CSS is better
display: block;
height: 150px
height: 200px
}
.discover-hits {
background-color: @well-bg;
float: right;
padding: 5px 10px;
border-bottom-left-radius: @border-radius-base;
}
.discover-table {
margin-top: -30px; // TODO: Remove this when the vis CSS is better
overflow-y: auto;
overflow-x: auto;
margin-top: 10px;
padding-left: 0px !important;
padding-right: 0px !important;
}

View file

@ -18,8 +18,6 @@ define(function (require) {
link: function ($scope, $el) {
var chart; // set in "vis" watcher
$scope.$watch('vis', function (vis, prevVis) {
var typeDefinition = typeDefs.byName[vis.typeName];
@ -29,7 +27,8 @@ define(function (require) {
chart.off('click');
chart.destroy();
}
if (!(vis instanceof SavedVis)) return;
//if (!(vis instanceof SavedVis)) return;
var notify = createNotifier({
location: vis.typeName + ' visualization'
@ -39,15 +38,17 @@ define(function (require) {
type: vis.typeName,
};
_.merge(vis.params, params);
_.merge(params, vis.params);
_.defaults(params, typeDefinition.params);
chart = new k4d3.Chart($el[0], params);
if (!!typeDefinition.onHover) chart.on('hover', typeDefinition.onHover);
if (!!typeDefinition.onClick) chart.on('click', typeDefinition.onClick);
if (!!typeDefinition.onBrush) chart.on('brush', typeDefinition.onBrush);
// For each type of interaction, assign the the handler if the vis object has it
// otherwise use the typeDef, otherwise, do nothing.
_.each({hover: 'onHover', click: 'onClick', brush: 'onBrush'}, function (func, event) {
var callback = vis[func] || typeDefinition[func];
if (!!callback) chart.on(event, callback);
});
vis.searchSource.onResults(function onResults(resp) {
courier.indexPatterns.get(vis.searchSource.get('index'))

View file

@ -0,0 +1,108 @@
define(function (require) {
var _ = require('lodash');
var module = require('modules').get('kibana/services');
var configCats = require('./_config_categories');
module.factory('AdhocVis', function (courier, Private) {
var aggs = Private(require('./_aggs'));
/**
opts params:
{
type: 'histogram', // The chart type
listeners : {
onClick: function,
onHover: function,
onBrush: function,
},
params: {}, // top level chart parameters
searchSource: SearchSource // the search source for the visualization
}
*/
function AdhocVis(opts) {
var vis = this;
var params;
// Must get an object for this one
if (typeof opts !== 'object') return;
vis.typeName = opts.type || 'histogram';
vis.params = _.cloneDeep(opts.params);
vis.searchSource = opts.searchSource || courier.SavedObject.rootSearch();
// give this the properties of config
_.merge(vis, opts.config);
// also give it the on* interaction functions, if any
_.merge(vis, opts.listeners);
// TODO: Should we abtract out the agg building stuff?
vis.searchSource
// reads the vis' config and write the agg to the searchSource
.aggs(function () {
// stores the config objects in queryDsl
var dsl = {};
// counter to ensure unique agg names
var i = 0;
// start at the root, but the current will move
var current = dsl;
// continue to nest the aggs under each other
// writes to the dsl object
vis.getConfig().forEach(function (config) {
current.aggs = {};
var key = '_agg_' + (i++);
var aggDsl = {};
aggDsl[config.agg] = config.aggParams;
current = current.aggs[key] = aggDsl;
});
// set the dsl to the searchSource
return dsl.aggs || {};
});
// TODO: Should this be abstracted somewhere? Its a copy/paste from _saved_vis.js
vis._fillConfigsToMinimum = function () {
// satify the min count for each category
configCats.fetchOrder.forEach(function (category) {
var myCat = vis[category.name];
if (myCat.configs.length < myCat.min) {
_.times(myCat.min - myCat.configs.length, function () {
vis.addConfig(category.name);
});
}
});
};
vis._fillConfigsToMinimum();
// Need these, but we have nothing to destroy for now;
vis.destroy = function () {};
/**
* Create a list of config objects, which are ready to be turned into aggregations,
* in the order which they should be executed.
*
* @return {Array} - The list of config objects
*/
vis.getConfig = Private(require('./_read_config'));
/**
* Transform an ES Response into data for this visualization
* @param {object} resp The elasticsearch response
* @return {array} An array of flattened response rows
*/
vis.buildChartDataFromResponse = Private(require('./_build_chart_data'));
}
return AdhocVis;
});
});

View file

@ -11,12 +11,6 @@ define(function (require) {
addTooltip: true,
addLegend: true
},
onClick: function (e) {
console.log('click', e);
},
onHover: function (e) {
console.log('hover', e);
},
config: {
metric: {
label: 'Y-Axis',

View file

@ -9,13 +9,6 @@ define(function (require) {
return (new SavedVis(id)).init();
};
this.tempForDiscover = function (searchSource) {
return (new SavedVis({
parentSearchSource: searchSource,
type: 'histogram'
})).init();
};
this.find = function (searchString) {
var body = searchString.length ? {
query: {