diff --git a/.editorconfig b/.editorconfig index 6c5424edb8d893..488cb2e9c39e71 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,4 +7,7 @@ indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true -insert_final_newline = true \ No newline at end of file +insert_final_newline = true + +[*.md] +insert_final_newline = false diff --git a/Gruntfile.js b/Gruntfile.js index d5ce5da6bf9558..33089b19289c4d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -34,7 +34,7 @@ module.exports = function (grunt) { 'Gruntfile.js', '<%= root %>/tasks/**/*.js', '<%= src %>/kibana/*.js', - '<%= src %>/server/*.js', + '<%= src %>/server/**/*.js', '<%= src %>/kibana/{components,directives,factories,filters,plugins,registry,services,utils}/**/*.js', '<%= unitTestDir %>/**/*.js', '!<%= unitTestDir %>/specs/vislib/fixture/**/*' diff --git a/src/kibana/components/doc_table/components/table_header.html b/src/kibana/components/doc_table/components/table_header.html index 9a0c1e036ec541..0e43596bec8c2f 100644 --- a/src/kibana/components/doc_table/components/table_header.html +++ b/src/kibana/components/doc_table/components/table_header.html @@ -3,11 +3,12 @@ Time - - {{name | shortDots}} + + {{name | shortDots}} - - + + + \ No newline at end of file diff --git a/src/kibana/components/doc_table/components/table_header.js b/src/kibana/components/doc_table/components/table_header.js index c4611e6c1cda1b..3b73ca129001c9 100644 --- a/src/kibana/components/doc_table/components/table_header.js +++ b/src/kibana/components/doc_table/components/table_header.js @@ -18,13 +18,18 @@ define(function (require) { var sortableField = function (field) { if (!$scope.indexPattern) return; - return $scope.indexPattern.fields.byName[field].sortable; + var sortable = _.deepGet($scope.indexPattern.fields.byName[field], 'sortable'); + return sortable; }; $scope.tooltip = function (column) { if (!sortableField(column)) return ''; else return 'Sort by ' + shortDotsFilter(column); }; + $scope.canRemove = function (name) { + return (name !== '_source' || $scope.columns.length !== 1); + }; + $scope.headerClass = function (column) { if (!sortableField(column)) return; @@ -49,6 +54,10 @@ define(function (require) { _.move($scope.columns, index, ++index); }; + $scope.toggleColumn = function (fieldName) { + _.toggleInOut($scope.columns, fieldName); + }; + $scope.sort = function (column) { if (!column || !sortableField(column)) return; diff --git a/src/kibana/components/doc_table/components/table_row/details.html b/src/kibana/components/doc_table/components/table_row/details.html index e8be1219c5d095..d83d868dbefeb1 100644 --- a/src/kibana/components/doc_table/components/table_row/details.html +++ b/src/kibana/components/doc_table/components/table_row/details.html @@ -2,5 +2,5 @@ Link to /{{row._index}}/{{row._type}}/{{row._id | uriescape}} - + \ No newline at end of file diff --git a/src/kibana/components/doc_table/doc_table.js b/src/kibana/components/doc_table/doc_table.js index b1454f371e661b..e7e9e28f558cdc 100644 --- a/src/kibana/components/doc_table/doc_table.js +++ b/src/kibana/components/doc_table/doc_table.js @@ -55,6 +55,15 @@ define(function (require) { $scope.limit += 50; }; + $scope.$watchCollection('columns', function (columns, oldColumns) { + if (oldColumns.length === 1 && oldColumns[0] === '_source' && $scope.columns.length > 1) { + _.pull($scope.columns, '_source'); + } + + if ($scope.columns.length === 0) $scope.columns.push('_source'); + }); + + $scope.$watch('searchSource', prereq(function (searchSource) { if (!$scope.searchSource) return; diff --git a/src/kibana/components/doc_viewer/doc_viewer.html b/src/kibana/components/doc_viewer/doc_viewer.html index b196bfe01b868a..92c4cf861d3c07 100644 --- a/src/kibana/components/doc_viewer/doc_viewer.html +++ b/src/kibana/components/doc_viewer/doc_viewer.html @@ -15,13 +15,25 @@ - - + + + + + @@ -45,6 +57,6 @@ -
{{hit | json}}
+
diff --git a/src/kibana/components/doc_viewer/doc_viewer.js b/src/kibana/components/doc_viewer/doc_viewer.js index fdd65b759b9a88..6925d5bf715918 100644 --- a/src/kibana/components/doc_viewer/doc_viewer.js +++ b/src/kibana/components/doc_viewer/doc_viewer.js @@ -1,5 +1,7 @@ define(function (require) { var _ = require('lodash'); + var angular = require('angular'); + require('angular-ui-ace'); var html = require('text!components/doc_viewer/doc_viewer.html'); require('css!components/doc_viewer/doc_viewer.css'); @@ -15,6 +17,7 @@ define(function (require) { hit: '=', indexPattern: '=', filter: '=?', + columns: '=?' }, link: function ($scope, $el, attr) { // If a field isn't in the mapping, use this @@ -24,6 +27,7 @@ define(function (require) { $scope.mapping = $scope.indexPattern.fields.byName; $scope.flattened = $scope.indexPattern.flattenHit($scope.hit); + $scope.hit_json = angular.toJson($scope.hit, true); $scope.formatted = _.mapValues($scope.flattened, function (value, name) { var mapping = $scope.mapping[name]; var formatter = (mapping && mapping.format) ? mapping.format : defaultFormat; @@ -34,6 +38,10 @@ define(function (require) { }); $scope.fields = _.keys($scope.flattened).sort(); + $scope.toggleColumn = function (fieldName) { + _.toggleInOut($scope.columns, fieldName); + }; + $scope.showArrayInObjectsWarning = function (row, field) { var value = $scope.flattened[field]; return _.isArray(value) && typeof value[0] === 'object'; diff --git a/src/kibana/components/index_patterns/_index_pattern.js b/src/kibana/components/index_patterns/_index_pattern.js index dfda4e9867fd7d..c54f1bbdf70f93 100644 --- a/src/kibana/components/index_patterns/_index_pattern.js +++ b/src/kibana/components/index_patterns/_index_pattern.js @@ -104,7 +104,7 @@ define(function (require) { } }, filterable: { - value: field.name === '_id' || ((field.indexed && type.filterable) || field.scripted) + value: field.name === '_id' || ((field.indexed && type && type.filterable) || field.scripted) }, format: { get: function () { @@ -113,7 +113,7 @@ define(function (require) { } }, sortable: { - value: field.indexed && type.sortable + value: field.indexed && type && type.sortable }, scripted: { // enumerable properties end up in the JSON diff --git a/src/kibana/directives/field_name.js b/src/kibana/directives/field_name.js index 8b7eb52254bf4f..387434de3862d8 100644 --- a/src/kibana/directives/field_name.js +++ b/src/kibana/directives/field_name.js @@ -39,12 +39,12 @@ define(function (require) { 'field', 'fieldName', 'fieldType', - 'field.rowCount' + 'field.inData' ], function () { var type = $scope.field ? $scope.field.type : $scope.fieldType; var name = $scope.field ? $scope.field.name : $scope.fieldName; - var results = $scope.field ? !$scope.field.rowCount && !$scope.field.scripted : false; + var results = $scope.field ? !$scope.field.inData && !$scope.field.scripted : false; var scripted = $scope.field ? $scope.field.scripted : false; var displayName = $filter('shortDots')(name); diff --git a/src/kibana/plugins/dashboard/components/panel/lib/search.js b/src/kibana/plugins/dashboard/components/panel/lib/search.js index 5d54d5ce1d59d7..d5a147c187e66e 100644 --- a/src/kibana/plugins/dashboard/components/panel/lib/search.js +++ b/src/kibana/plugins/dashboard/components/panel/lib/search.js @@ -3,6 +3,17 @@ define(function (require) { return function (panel, $scope) { // Function parameters here return savedSearches.get(panel.id) .then(function (savedSearch) { + panel.columns = panel.columns || savedSearch.columns; + panel.sort = panel.sort || savedSearch.sort; + + $scope.$watchCollection('panel.columns', function () { + $scope.state.save(); + }); + + $scope.$watchCollection('panel.sort', function () { + $scope.state.save(); + }); + return { savedObj: savedSearch, panel: panel, diff --git a/src/kibana/plugins/dashboard/components/panel/panel.html b/src/kibana/plugins/dashboard/components/panel/panel.html index 636b9f51836ca7..c16d6c9e3dec1e 100644 --- a/src/kibana/plugins/dashboard/components/panel/panel.html +++ b/src/kibana/plugins/dashboard/components/panel/panel.html @@ -21,8 +21,8 @@ diff --git a/src/kibana/plugins/discover/components/field_chooser/field_chooser.html b/src/kibana/plugins/discover/components/field_chooser/field_chooser.html index d89e99c7fb31f3..6cf4725d62ec0d 100644 --- a/src/kibana/plugins/discover/components/field_chooser/field_chooser.html +++ b/src/kibana/plugins/discover/components/field_chooser/field_chooser.html @@ -25,12 +25,12 @@
Selected Fields
diff --git a/src/kibana/plugins/discover/components/field_chooser/field_chooser.js b/src/kibana/plugins/discover/components/field_chooser/field_chooser.js index 2795d1a3318a3c..144c856be9e0e2 100644 --- a/src/kibana/plugins/discover/components/field_chooser/field_chooser.js +++ b/src/kibana/plugins/discover/components/field_chooser/field_chooser.js @@ -5,6 +5,7 @@ define(function (require) { var rison = require('utils/rison'); var qs = require('utils/query_string'); var fieldCalculator = require('plugins/discover/components/field_chooser/lib/field_calculator'); + var IndexedArray = require('utils/indexed_array/index'); require('directives/css_truncate'); @@ -16,8 +17,7 @@ define(function (require) { return { restrict: 'E', scope: { - fields: '=', - toggle: '=', + columns: '=', data: '=', state: '=', indexPattern: '=', @@ -55,11 +55,14 @@ define(function (require) { reset: function () { filter.vals = _.clone(filter.defaults); }, + isFieldSelected: function (field) { + return field.display; + }, isFieldFiltered: function (field) { var matchFilter = (filter.vals.type == null || field.type === filter.vals.type); var isAnalyzed = (filter.vals.analyzed == null || field.analyzed === filter.vals.analyzed); var isIndexed = (filter.vals.indexed == null || field.indexed === filter.vals.indexed); - var rowsScritpedOrMissing = (!filter.vals.missing || field.scripted || field.rowCount > 0); + var rowsScritpedOrMissing = (!filter.vals.missing || field.scripted || field.inData); var matchName = (!filter.vals.name || field.name.indexOf(filter.vals.name) !== -1); return !field.display @@ -87,6 +90,11 @@ define(function (require) { filter.active = filter.getActive(); }); + $scope.toggle = function (fieldName) { + $scope.increaseFieldCounter(fieldName); + _.toggleInOut($scope.columns, fieldName); + }; + var calculateFields = function (newFields) { // Find the top N most popular fields $scope.popularFields = _(newFields) @@ -110,16 +118,47 @@ define(function (require) { }; $scope.$watch('fields', calculateFields); + + $scope.$watch('indexPattern', function (indexPattern) { + $scope.fields = new IndexedArray ({ + index: ['name'], + initialSet: _($scope.indexPattern.fields) + .sortBy('name') + .transform(function (fields, field) { + // clone the field with Object.create so that its getters + // and non-enumerable props are preserved + var clone = Object.create(field); + clone.display = _.contains($scope.columns, field.name); + fields.push(clone); + }, []) + .value() + }); + + }); + + $scope.$watchCollection('columns', function (columns, oldColumns) { + _.each($scope.fields, function (field) { + field.display = _.contains(columns, field.name) ? true : false; + }); + }); + $scope.$watch('data', function () { + + // Get all fields current in data set + var currentFields = _.chain($scope.data).map(function (d) { + return _.keys($scope.indexPattern.flattenHit(d)); + }).flatten().unique().sort().value(); + _.each($scope.fields, function (field) { + field.inData = _.contains(currentFields, field.name) ? true : false; if (field.details) { $scope.details(field, true); } }); }); - $scope.increaseFieldCounter = function (field) { - $scope.indexPattern.popularizeField(field.name, 1); + $scope.increaseFieldCounter = function (fieldName) { + $scope.indexPattern.popularizeField(fieldName, 1); }; $scope.runAgg = function (field) { diff --git a/src/kibana/plugins/discover/controllers/discover.js b/src/kibana/plugins/discover/controllers/discover.js index 6caff7f0ba7225..13994cda8c06d1 100644 --- a/src/kibana/plugins/discover/controllers/discover.js +++ b/src/kibana/plugins/discover/controllers/discover.js @@ -111,6 +111,10 @@ define(function (require) { $state.index = $scope.indexPattern.id; $state.sort = getSort.array($state.sort, $scope.indexPattern); + $scope.$watchCollection('state.columns', function (columns) { + $state.save(); + }); + var metaFields = config.get('metaFields'); filterManager.init($state); @@ -134,44 +138,6 @@ define(function (require) { $scope.failuresShown = showTotal; }; - // stores the complete list of fields - $scope.fields = _($scope.indexPattern.fields) - .sortBy('name') - .transform(function (fields, field) { - // clone the field with Object.create so that its getters - // and non-enumerable props are preserved - var clone = Object.create(field); - clone.display = _.contains($state.columns, field.name); - clone.rowCount = $scope.rows ? $scope.rows.fieldCounts[field.name] : 0; - fields.push(clone); - }, []) - .value(); - - refreshColumns(); - - // listen for changes, and relisten everytime something happens - $scope.$listen($state, 'fetch_with_changes', updateFields); - $scope.$listen($state, 'reset_with_changes', updateFields); - function updateFields(changes) { - var newColumns = _.contains(changes, 'columns'); - var newIndex = _.contains(changes, 'index'); - var otherChanges = _.pull(changes, 'index', 'columns'); - - if (newIndex) { - // we will be reloading, don't need to juggle state - return; - } - - if (newColumns) { - $scope.fields.forEach(function (field) { - field.display = _.contains($state.columns, field.name); - }); - refreshColumns(); - } - - if (otherChanges.length) $scope.fetch(); - } - $scope.updateDataSource() .then(function () { $scope.$listen(timefilter, 'update', function () { @@ -287,10 +253,6 @@ define(function (require) { $scope.updateTime(); - if (_.isEmpty($state.columns)) { - refreshColumns(); - } - $scope.updateDataSource() .then(setupVisualization) .then(function () { @@ -394,11 +356,6 @@ define(function (require) { hit.$$_formatted = _.mapValues(hit.$$_flattened, formatAndCount); }); - // apply the field counts to the field list - // We could do this in the field_chooser but it would us to iterate the array again - $scope.fields.forEach(function (field) { - field.rowCount = counts[field.name] || 0; - }); })); segmented.on('mergedSegment', function (merged) { @@ -444,66 +401,16 @@ define(function (require) { .set('filter', $state.filters || []); }); - // This is a hacky optimization for comparing the contents of a large array to a short one. - function arrayToKeys(array, value) { - var obj = {}; - _.each(array, function (key) { - obj[key] = value || true; - }); - return obj; - } - // TODO: On array fields, negating does not negate the combination, rather all terms $scope.filterQuery = function (field, values, operation) { $scope.indexPattern.popularizeField(field, 1); filterManager.add(field, values, operation, $state.index); }; - $scope.toggleField = function (name) { - var field = _.find($scope.fields, { name: name }); - - // If we can't find the field in the mapping, ensure it isn't in the column list and abort - if (!field) { - $state.columns = _.without($state.columns, name); - return; - } - - // toggle the display property - field.display = !field.display; - - if ($state.columns.length === 1 && $state.columns[0] === '_source') { - $state.columns = _.toggleInOut($state.columns, name); - $state.columns = _.toggleInOut($state.columns, '_source'); - _.find($scope.fields, {name: '_source'}).display = false; - } else { - $state.columns = _.toggleInOut($state.columns, name); - } - - refreshColumns(); - }; - $scope.toTop = function () { $window.scrollTo(0, 0); }; - function refreshColumns() { - // Get all displayed field names; - var fields = _($scope.fields).filter('display').pluck('name').value(); - - // Make sure there are no columns added that aren't in the displayed field list. - $state.columns = _.intersection($state.columns, fields); - - // If no columns remain, use _source - if (!$state.columns.length) { - $scope.toggleField('_source'); - return; - } - - if (init.complete) { - $state.save(); - } - } - // TODO: Move to utility class var addSlashes = function (str) { if (!_.isString(str)) return str; @@ -514,12 +421,6 @@ define(function (require) { return str; }; - // TODO: Move to utility class - // https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - var regexEscape = function (str) { - return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - }; - var loadingVis; var setupVisualization = function () { // If we're not setting anything up we need to return an empty promise diff --git a/src/kibana/plugins/discover/index.html b/src/kibana/plugins/discover/index.html index 89bafb1a6c659f..21d2816f87c0b2 100644 --- a/src/kibana/plugins/discover/index.html +++ b/src/kibana/plugins/discover/index.html @@ -60,8 +60,7 @@