From 73874a421c9db3ff6a4e26549c2549e1cf54d251 Mon Sep 17 00:00:00 2001 From: Spencer Alger Date: Tue, 15 Apr 2014 16:57:47 -0700 Subject: [PATCH] Added saving and loading of visualizations from within the visualization editor. #33 --- .../discover/saved_searches/_saved_search.js | 2 +- .../discover/saved_searches/saved_searches.js | 1 + .../apps/visualize/controllers/editor.js | 125 ++++++++++++------ src/kibana/apps/visualize/imgs/histogram.jpg | Bin 0 -> 9845 bytes src/kibana/apps/visualize/index.html | 28 +++- src/kibana/apps/visualize/partials/load.html | 20 +++ src/kibana/apps/visualize/partials/save.html | 12 ++ .../saved_visualizations/_saved_vis.js | 4 + .../saved_visualizations/_type_defs.js | 2 +- .../saved_visualizations.js | 29 +++- src/kibana/apps/visualize/styles/main.css | 38 +++--- src/kibana/apps/visualize/styles/main.less | 108 ++++++++------- src/kibana/directives/config.js | 19 ++- 13 files changed, 273 insertions(+), 115 deletions(-) create mode 100644 src/kibana/apps/visualize/imgs/histogram.jpg create mode 100644 src/kibana/apps/visualize/partials/load.html create mode 100644 src/kibana/apps/visualize/partials/save.html diff --git a/src/kibana/apps/discover/saved_searches/_saved_search.js b/src/kibana/apps/discover/saved_searches/_saved_search.js index ba2cd5d49cc3f6..613c4ba44e086f 100644 --- a/src/kibana/apps/discover/saved_searches/_saved_search.js +++ b/src/kibana/apps/discover/saved_searches/_saved_search.js @@ -19,7 +19,7 @@ define(function (require) { mapping: { title: 'string', description: 'string', - hits: 'number', + hits: 'number' }, defaults: { diff --git a/src/kibana/apps/discover/saved_searches/saved_searches.js b/src/kibana/apps/discover/saved_searches/saved_searches.js index 70d6860f9105d9..da4773201cd930 100644 --- a/src/kibana/apps/discover/saved_searches/saved_searches.js +++ b/src/kibana/apps/discover/saved_searches/saved_searches.js @@ -36,6 +36,7 @@ define(function (require) { .then(function (resp) { return resp.hits.hits.map(function (hit) { var source = hit._source; + source.id = hit._id; source.url = '/discover/' + hit._id; return source; }); diff --git a/src/kibana/apps/visualize/controllers/editor.js b/src/kibana/apps/visualize/controllers/editor.js index 5d1fdabd497185..c5f20761f2500f 100644 --- a/src/kibana/apps/visualize/controllers/editor.js +++ b/src/kibana/apps/visualize/controllers/editor.js @@ -1,5 +1,6 @@ define(function (require) { var _ = require('lodash'); + var ConfigTemplate = require('utils/config_template'); require('../saved_visualizations/saved_visualizations'); require('notify/notify'); @@ -12,54 +13,46 @@ define(function (require) { var aggs = require('../saved_visualizations/_aggs'); var visConfigCategories = require('../saved_visualizations/_config_categories'); - app.controller('VisualizeEditor', function ($route, $scope, courier, createNotifier, config, $location) { + app.controller('VisualizeEditor', function ($route, $scope, courier, createNotifier, config, $location, savedVisualizations) { var notify = createNotifier({ location: 'Visualization Editor' }); + // get the vis loaded in from the routes var vis = $route.current.locals.vis; - $scope.refreshFields = function () { - $scope.fields = null; - vis.searchSource.clearFieldCache().then(getFields, notify.error); - }; - - function getFields() { - return vis.searchSource.getFields() - .then(function (fieldsHash) { - // create a sorted list of the fields for display purposes - $scope.fields = _(fieldsHash) - .keys() - .sort() - .transform(function (fields, name) { - var field = fieldsHash[name]; - field.name = name; - fields.push(field); - }) - .value(); - - $scope.fields.byName = fieldsHash; - }); - } - - getFields(); + // get the current field list + vis.searchSource.getFields() + .then(function (fieldsHash) { + // create a sorted list of the fields for display purposes + $scope.fields = _(fieldsHash) + .keys() + .sort() + .transform(function (fields, name) { + var field = fieldsHash[name]; + field.name = name; + fields.push(field); + }) + .value(); + + $scope.fields.byName = fieldsHash; + }); $scope.vis = vis; $scope.aggs = aggs; $scope.visConfigCategories = visConfigCategories; - $scope.$on('change:config.defaultIndex', function () { - if (!vis.searchSource.get('index')) { - vis.searchSource.index(config.get('defaultIndex')); - getFields(); - } - }); - + /** + * (Re)set the aggs key on the vis.searchSource based on the + * current config + */ var updateDataSource = function () { notify.event('update data source'); + // get the config objects form the visualization var config = vis.getConfig(); + // group the segments by their categoryName, but merge the segments and groups config = _.groupBy(config, function (config) { switch (config.categoryName) { case 'group': @@ -70,17 +63,21 @@ define(function (require) { } }); + // use the global aggregation if we don't have any dimensions if (!config.dimension) { - // use the global aggregation if we don't have any dimensions config.dimension = [{ agg: 'global', aggParams: {} }]; } + // stores the config objects in queryDsl var dsl = {}; + // counter to ensure unique agg names var i = 0; + // continue to nest the aggs under each other + // writes to the dsl object var nest = (function () { var current = dsl; return function (config) { @@ -94,31 +91,79 @@ define(function (require) { }; }()); + // nest each config type in order config.split && config.split.forEach(nest); config.dimension && config.dimension.forEach(nest); config.metric && config.metric.forEach(nest); - notify.log('config', config); - notify.log('aggs', dsl.aggs); - + // set the dsl to the searchSource vis.searchSource.aggs(dsl.aggs); notify.event('update data source', true); }; - /********* - *** BUTTON EVENT HANDLERS - *********/ + /** + * Refresh Visualization + */ $scope.doVisualize = function () { updateDataSource(); vis.searchSource.fetch(); }; + + /** + * Restart on a new visualization + */ + $scope.startOver = function () { + $location.url('/visualize'); + }; + + /** + * Save the current vis state + */ $scope.doSave = function () { updateDataSource(); vis.save() .then(function () { - $location.url('/visualize/' + vis.typeName + '/' + vis.get('id')); + $location.url('/visualize/' + vis.typeName + '/' + vis.id); }, notify.fatal); }; + // templates that can be used in the config panel + var configTemplate = $scope.configTemplate = new ConfigTemplate({ + save: require('text!../partials/save.html'), + load: require('text!../partials/load.html') + }); + + /** + * Toggle the save config panel + */ + $scope.toggleSave = function () { + configTemplate.toggle('save'); + }; + + // stash for vars related to loading a vis + var loadVis = $scope.loadVis = { + filter: '', + list: [], + setList: function (hits) { + $scope.loadVis.list = hits.filter(function (hit) { + return hit.id !== $scope.vis.id; + }); + } + }; + + /** + * Toggle the load config panel + */ + $scope.toggleLoad = function () { + configTemplate.toggle('load') && savedVisualizations.find().then(loadVis.setList); + }; + + // when the filter changes, load in the new vis objects + $scope.$watch('loadVis.filter', function () { + savedVisualizations.find(loadVis.filter).then(loadVis.setList); + }); + + // objects to make available within the config panel's scope + $scope.conf = _.pick($scope, 'doSave', 'doLoad', 'loadVis', 'vis'); }); }); \ No newline at end of file diff --git a/src/kibana/apps/visualize/imgs/histogram.jpg b/src/kibana/apps/visualize/imgs/histogram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..219be5f19bda5a6487c08c34b25977351c0b7dea GIT binary patch literal 9845 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 -
-
+
+ + + +
+
    @@ -12,8 +32,6 @@
- -
diff --git a/src/kibana/apps/visualize/partials/load.html b/src/kibana/apps/visualize/partials/load.html new file mode 100644 index 00000000000000..30819a2917ff5e --- /dev/null +++ b/src/kibana/apps/visualize/partials/load.html @@ -0,0 +1,20 @@ +
+
+ + +
+
+ + +

No matching visualizations

+ \ No newline at end of file diff --git a/src/kibana/apps/visualize/partials/save.html b/src/kibana/apps/visualize/partials/save.html new file mode 100644 index 00000000000000..d0dfc43b405490 --- /dev/null +++ b/src/kibana/apps/visualize/partials/save.html @@ -0,0 +1,12 @@ +
+
+ + +
+
+ + +
+ + +
\ No newline at end of file diff --git a/src/kibana/apps/visualize/saved_visualizations/_saved_vis.js b/src/kibana/apps/visualize/saved_visualizations/_saved_vis.js index 2289802378148d..0d913967db04fd 100644 --- a/src/kibana/apps/visualize/saved_visualizations/_saved_vis.js +++ b/src/kibana/apps/visualize/saved_visualizations/_saved_vis.js @@ -21,12 +21,16 @@ define(function (require) { id: id, mapping: { + title: 'string', + description: 'string', stateJSON: 'string', savedSearchId: 'string', typeName: 'string', }, defaults: { + title: '', + description: '', stateJSON: '{}', typeName: type, }, diff --git a/src/kibana/apps/visualize/saved_visualizations/_type_defs.js b/src/kibana/apps/visualize/saved_visualizations/_type_defs.js index 842a5b54fa7434..5809bc7b1ddc9f 100644 --- a/src/kibana/apps/visualize/saved_visualizations/_type_defs.js +++ b/src/kibana/apps/visualize/saved_visualizations/_type_defs.js @@ -5,7 +5,7 @@ define(function (require) { var typeDefs = [ { name: 'histogram', - previewUrl: '', + previewUrl: 'kibana/apps/visualize/imgs/histogram.jpg', config: { metric: { label: 'Y-Axis', diff --git a/src/kibana/apps/visualize/saved_visualizations/saved_visualizations.js b/src/kibana/apps/visualize/saved_visualizations/saved_visualizations.js index 93fe7d079507a0..c425ad4588a30e 100644 --- a/src/kibana/apps/visualize/saved_visualizations/saved_visualizations.js +++ b/src/kibana/apps/visualize/saved_visualizations/saved_visualizations.js @@ -1,11 +1,38 @@ define(function (require) { var app = require('modules').get('app/visualize'); + var typeDefs = require('./_type_defs'); require('./_saved_vis'); - app.service('savedVisualizations', function (es, courier, $q, $timeout, SavedVis) { + app.service('savedVisualizations', function (es, config, courier, $q, $timeout, SavedVis) { this.get = function (type, id) { return (new SavedVis(type, id)).init(); }; + + this.find = function (searchString) { + return es.search({ + index: config.file.kibanaIndex, + type: 'visualization', + body: { + query: { + multi_match: { + query: searchString || '', + type: 'phrase_prefix', + fields: ['title^3', 'description'], + zero_terms_query: 'all' + } + } + } + }) + .then(function (resp) { + return resp.hits.hits.map(function (hit) { + var source = hit._source; + source.id = hit._id; + source.url = '/visualize/' + source.typeName + '/' + hit._id; + source.typeDef = typeDefs.byName[source.typeName]; + return source; + }); + }); + }; }); }); \ No newline at end of file diff --git a/src/kibana/apps/visualize/styles/main.css b/src/kibana/apps/visualize/styles/main.css index 560d5bc8d914e6..90c7659ffdacf5 100644 --- a/src/kibana/apps/visualize/styles/main.css +++ b/src/kibana/apps/visualize/styles/main.css @@ -68,51 +68,51 @@ .modal-footer:after { clear: both; } -.vis-config-panel { +.vis-content { + padding: 15px 0; +} +.vis-content .vis-config-panel { padding: 0; } -.vis-config-panel .panel-heading { +.vis-content .vis-config-panel .panel-heading { position: relative; } -.vis-config-panel .panel-heading button { +.vis-content .vis-config-panel .panel-heading button { position: absolute; top: 6px; right: 7px; padding: 3px 8px; } -.vis-config-panel .panel-body { +.vis-content .vis-config-panel .panel-body { padding: 10px 0; } -.vis-config-panel vis-config-editor { +.vis-content .vis-config-panel vis-config-editor { display: block; border-top: 1px dashed #dddddd; padding: 10px 10px 0; } -.vis-config-panel vis-config-editor:first-child { +.vis-content .vis-config-panel vis-config-editor:first-child { border-top: none; } -.vis-config-panel vis-config-editor .config-controls { +.vis-content .vis-config-panel vis-config-editor .config-controls { float: right; margin-top: -2px; } -.vis-config-panel vis-config-editor .config-controls button { +.vis-content .vis-config-panel vis-config-editor .config-controls button { font-size: 10px; padding: 2px 4px; font-weight: 100; } -.vis-config-panel > li { +.vis-content .vis-config-panel > li { list-style: none; } -.vis-config-panel .agg-config-interval td { +.vis-content .vis-config-panel .agg-config-interval td { padding-left: 10px; } -.vis-config-panel .agg-config-interval td:first-child { +.vis-content .vis-config-panel .agg-config-interval td:first-child { padding-left: 0px; } -.vis-canvas { - padding: 15px 0; -} -.vis-canvas visualization { +.vis-content .vis-canvas visualization { display: block; background: black; height: 800px; @@ -120,3 +120,11 @@ margin: 0 auto; overflow: auto; } +.vis-load-list .media-object { + width: 65px; + height: 65px; +} +.vis-preview-histogram { + background-image: url(../imgs/histogram.jpg); + background-size: contain; +} diff --git a/src/kibana/apps/visualize/styles/main.less b/src/kibana/apps/visualize/styles/main.less index 2baddf06007cab..1112b59340c484 100644 --- a/src/kibana/apps/visualize/styles/main.less +++ b/src/kibana/apps/visualize/styles/main.less @@ -1,69 +1,83 @@ @import (reference) "../../../styles/_bootstrap.less"; @import (reference) "../../../styles/theme/_theme.less"; -.vis-config-panel { - padding: 0; +.vis-content { + padding: 15px 0; + + .vis-config-panel { + padding: 0; - .panel-heading { - position: relative; + .panel-heading { + position: relative; - button { - position: absolute; - top: 6px; - right: 7px; - padding: 3px 8px; + button { + position: absolute; + top: 6px; + right: 7px; + padding: 3px 8px; + } } - } - .panel-body { - padding: 10px 0; - } + .panel-body { + padding: 10px 0; + } - vis-config-editor { - display: block; - border-top: 1px dashed @panel-inner-border; - padding: 10px 10px 0; + vis-config-editor { + display: block; + border-top: 1px dashed @panel-inner-border; + padding: 10px 10px 0; - &:first-child { - border-top: none; - } + &:first-child { + border-top: none; + } - .config-controls { - float: right; - // just a tiny boost up - margin-top: -2px; + .config-controls { + float: right; + // just a tiny boost up + margin-top: -2px; - button { - font-size: 10px; - padding: 2px 4px; - font-weight: 100; + button { + font-size: 10px; + padding: 2px 4px; + font-weight: 100; + } } } - } - > li { - list-style: none; - } + > li { + list-style: none; + } - .agg-config-interval { - td { - padding-left: 10px; - &:first-child { - padding-left: 0px; + .agg-config-interval { + td { + padding-left: 10px; + &:first-child { + padding-left: 0px; + } } } } -} -.vis-canvas { - padding: 15px 0; + .vis-canvas { + visualization { + display: block; + background: black; + height: 800px; + width: 95%; + margin: 0 auto; + overflow: auto; + } + } +} - visualization { - display: block; - background: black; - height: 800px; - width: 95%; - margin: 0 auto; - overflow: auto; +.vis-load-list { + .media-object { + width: 65px; + height: 65px; } +} + +.vis-preview-histogram { + background-image: url(../imgs/histogram.jpg); + background-size: contain; } \ No newline at end of file diff --git a/src/kibana/directives/config.js b/src/kibana/directives/config.js index 71ae54588b0251..69e733b24ae956 100644 --- a/src/kibana/directives/config.js +++ b/src/kibana/directives/config.js @@ -26,7 +26,15 @@ define(function (require) { link: function ($scope, element, attr) { $scope[attr.configObject] = $scope.configObject; - $scope.$watch('configTemplate.current || configTemplate', function (newTemplate, oldTemplate) { + var wrapTmpl = function (tmpl) { + if ($scope.configSubmit) { + return '
' + tmpl + '
'; + } else { + return '
' + tmpl + '
'; + } + }; + + var render = function (newTemplate, oldTemplate) { var tmpl = $scope.configTemplate; if (tmpl instanceof ConfigTemplate) { tmpl = tmpl.toString(); @@ -34,16 +42,17 @@ define(function (require) { element.html(!tmpl ? '' : $compile('' + '
' + - '
' + - tmpl + - '
' + + wrapTmpl(tmpl) + '
' + ' ' + '
' + '
' + '' )($scope)); - }); + }; + + $scope.$watch('configSubmit', render); + $scope.$watch('configTemplate.current || configTemplate', render); $scope.close = function () { if (_.isFunction($scope.configClose)) $scope.configClose();