mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Created an AdhocVis factory to make non-saved visualizations easier
This commit is contained in:
parent
fa6633b7fd
commit
82fb6c3822
8 changed files with 172 additions and 56 deletions
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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'))
|
||||
|
|
108
src/kibana/apps/visualize/saved_visualizations/_adhoc_vis.js
Normal file
108
src/kibana/apps/visualize/saved_visualizations/_adhoc_vis.js
Normal 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;
|
||||
});
|
||||
});
|
|
@ -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',
|
||||
|
|
|
@ -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: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue