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
- |
\ 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 @@
|