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
- |
\ 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..d2c04a69d24fc4 100644
--- a/src/kibana/components/doc_table/doc_table.js
+++ b/src/kibana/components/doc_table/doc_table.js
@@ -11,7 +11,7 @@ define(function (require) {
require('components/doc_table/components/table_row');
require('modules').get('kibana')
- .directive('docTable', function (config, Notifier) {
+ .directive('docTable', function (config, Notifier, getAppState) {
return {
restrict: 'E',
template: html,
@@ -55,6 +55,24 @@ define(function (require) {
$scope.limit += 50;
};
+ // This exists to fix the problem of an empty initial column list not playing nice with watchCollection.
+ $scope.$watch('columns', function (columns) {
+ if (columns.length !== 0) return;
+
+ var $state = getAppState();
+ $scope.columns.push('_source');
+ if ($state) $state.replace();
+ });
+
+ $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 ae12ee2290d700..92c4cf861d3c07 100644
--- a/src/kibana/components/doc_viewer/doc_viewer.html
+++ b/src/kibana/components/doc_viewer/doc_viewer.html
@@ -15,13 +15,25 @@
-
-
+
+
+
+
+
|
diff --git a/src/kibana/components/doc_viewer/doc_viewer.js b/src/kibana/components/doc_viewer/doc_viewer.js
index 700e60ec5d5958..6925d5bf715918 100644
--- a/src/kibana/components/doc_viewer/doc_viewer.js
+++ b/src/kibana/components/doc_viewer/doc_viewer.js
@@ -17,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
@@ -37,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 a0be58fed0506a..6c5a045e709387 100644
--- a/src/kibana/components/index_patterns/_index_pattern.js
+++ b/src/kibana/components/index_patterns/_index_pattern.js
@@ -103,7 +103,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 () {
@@ -112,7 +112,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 835e9389bb9dd0..021ebe22e690df 100644
--- a/src/kibana/directives/field_name.js
+++ b/src/kibana/directives/field_name.js
@@ -41,12 +41,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 84811ac659abed..15b0af3ec5fe0c 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 () {
@@ -392,11 +354,6 @@ define(function (require) {
hit.$$_formatted = _.mapValues(flatHit, 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) {
@@ -442,66 +399,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;
@@ -512,12 +419,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 5a7517d9c94d27..09d278785de6c7 100644
--- a/src/kibana/plugins/discover/index.html
+++ b/src/kibana/plugins/discover/index.html
@@ -62,8 +62,7 @@
|