Check sort is valid before attempting to sort on it. Closes #2755

This commit is contained in:
Rashid Khan 2015-01-29 15:45:21 -07:00
parent d7972bca28
commit eaf11f1673
6 changed files with 70 additions and 40 deletions

View file

@ -9,14 +9,29 @@ define(function (require) {
*/
return function (sort, indexPattern) {
var sortObj = {};
if (_.isArray(sort) && sort.length === 2) {
var field, direction;
function isSortable(field) {
return (indexPattern.fields.byName[field] && indexPattern.fields.byName[field].sortable);
}
if (_.isArray(sort) && sort.length === 2 && isSortable(sort[0])) {
// At some point we need to refact the sorting logic, this array sucks.
sortObj[sort[0]] = sort[1];
} else if (indexPattern.timeFieldName) {
sortObj[indexPattern.timeFieldName] = 'desc';
field = sort[0];
direction = sort[1];
} else if (indexPattern.timeFieldName && isSortable(indexPattern.timeFieldName)) {
field = indexPattern.timeFieldName;
direction = 'desc';
}
if (field) {
sortObj[field] = direction;
} else {
sortObj._score = 'desc';
}
return sortObj;
};
});

View file

@ -425,15 +425,8 @@ define(function (require) {
$scope.searchSource
.size($scope.opts.sampleSize)
.sort(function () {
var sort = {};
if (_.isArray($state.sort) && $state.sort.length === 2) {
sort[$state.sort[0]] = $state.sort[1];
} else if ($scope.indexPattern.timeFieldName) {
// Use the watcher to set sort in this case, the above `if` will now be true
$state.sort = [$scope.indexPattern.timeFieldName, 'desc'];
} else {
sort._score = 'desc';
}
var sort = getSort($state.sort, $scope.indexPattern);
$state.sort = _.pairs(sort)[0];
return sort;
})
.query(!$state.query ? null : $state.query)

View file

@ -1,20 +1,21 @@
define(function (require) {
function stubbedLogstashFields() {
var sourceData = [
{ name: 'bytes', type: 'number', indexed: true, analyzed: true, count: 10 },
{ name: 'ssl', type: 'boolean', indexed: true, analyzed: true, count: 20 },
{ name: '@timestamp', type: 'date', indexed: true, analyzed: true, count: 30 },
{ name: 'time', type: 'date', indexed: true, analyzed: true, count: 30 },
{ name: 'utc_time', type: 'date', indexed: true, analyzed: true },
{ name: 'phpmemory', type: 'number', indexed: true, analyzed: true },
{ name: 'ip', type: 'ip', indexed: true, analyzed: true },
{ name: 'request_body', type: 'attachment', indexed: true, analyzed: true },
{ name: 'point', type: 'geo_point', indexed: true, analyzed: true },
{ name: 'area', type: 'geo_shape', indexed: true, analyzed: true },
{ name: 'extension', type: 'string', indexed: true, analyzed: true },
{ name: 'machine.os', type: 'string', indexed: true, analyzed: true },
{ name: 'geo.src', type: 'string', indexed: true, analyzed: true },
{ name: '_type', type: 'string', indexed: true, analyzed: true },
{ name: 'bytes', type: 'number', indexed: true, analyzed: true, sortable: true, filterable: true, count: 10 },
{ name: 'ssl', type: 'boolean', indexed: true, analyzed: true, sortable: true, filterable: true, count: 20 },
{ name: '@timestamp', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
{ name: 'time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true, count: 30 },
{ name: 'utc_time', type: 'date', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'phpmemory', type: 'number', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'ip', type: 'ip', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'request_body', type: 'attachment', indexed: true, analyzed: true, sortable: false, filterable: true },
{ name: 'point', type: 'geo_point', indexed: true, analyzed: true, sortable: false, filterable: false },
{ name: 'area', type: 'geo_shape', indexed: true, analyzed: true, sortable: true, filterable: false },
{ name: 'extension', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'machine.os', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: 'geo.src', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: '_type', type: 'string', indexed: true, analyzed: true, sortable: true, filterable: true },
{ name: '_id', type: 'string', indexed: false, analyzed: false, sortable: false, filterable: true},
{ name: 'custom_user_field', type: 'conflict', indexed: false, analyzed: false },
{ name: 'script string', type: 'string', scripted: true, script: '\'i am a string\''},
{ name: 'script number', type: 'number', scripted: true, script: '1234'},

View file

@ -4,30 +4,43 @@ define(function (require) {
describe('docTable', function () {
describe('getSort function', function () {
var timePattern = {
timeFieldName: 'time'
};
var noTimePattern = {};
beforeEach(module('kibana'));
beforeEach(inject(function (Private, _$rootScope_, Promise) {
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
}));
it('should be a function', function () {
expect(getSort).to.be.a(Function);
});
it('should return an object if passed a 2 item array', function () {
expect(getSort(['foo', 'bar'], timePattern)).to.eql({foo: 'bar'});
expect(getSort(['foo', 'bar'], noTimePattern)).to.eql({foo: 'bar'});
expect(getSort(['bytes', 'desc'], indexPattern)).to.eql({bytes: 'desc'});
delete indexPattern.timeFieldName;
expect(getSort(['bytes', 'desc'], indexPattern)).to.eql({bytes: 'desc'});
});
it('should sort by the default when passed an unsortable field', function () {
expect(getSort(['_id', 'asc'], indexPattern)).to.eql({time: 'desc'});
expect(getSort(['lol_nope', 'asc'], indexPattern)).to.eql({time: 'desc'});
delete indexPattern.timeFieldName;
expect(getSort(['_id', 'asc'], indexPattern)).to.eql({_score: 'desc'});
});
it('should sort in reverse chrono order otherwise on time based patterns', function () {
expect(getSort([], timePattern)).to.eql({time: 'desc'});
expect(getSort(['foo'], timePattern)).to.eql({time: 'desc'});
expect(getSort({foo: 'bar'}, timePattern)).to.eql({time: 'desc'});
expect(getSort([], indexPattern)).to.eql({time: 'desc'});
expect(getSort(['foo'], indexPattern)).to.eql({time: 'desc'});
expect(getSort({foo: 'bar'}, indexPattern)).to.eql({time: 'desc'});
});
it('should sort by score on non-time patterns', function () {
expect(getSort([], noTimePattern)).to.eql({_score: 'desc'});
expect(getSort(['foo'], noTimePattern)).to.eql({_score: 'desc'});
expect(getSort({foo: 'bar'}, noTimePattern)).to.eql({_score: 'desc'});
delete indexPattern.timeFieldName;
expect(getSort([], indexPattern)).to.eql({_score: 'desc'});
expect(getSort(['foo'], indexPattern)).to.eql({_score: 'desc'});
expect(getSort({foo: 'bar'}, indexPattern)).to.eql({_score: 'desc'});
});
});

View file

@ -102,7 +102,7 @@ define(function (require) {
describe('getFields', function () {
it('should return all non-scripted fields', function () {
var indexed = _.where(mockLogstashFields, { scripted: false });
expect(indexPattern.getFields()).to.eql(indexed);
expect(indexPattern.getFields().length).to.eql(indexed.length);
});
it('should return all scripted fields', function () {

View file

@ -4,6 +4,10 @@ define(function (require) {
var sinon = require('sinon/sinon');
var IndexedArray = require('utils/indexed_array/index');
var fieldFormats = Private(require('components/index_patterns/_field_formats'));
var flattenSearchResponse = require('components/index_patterns/_flatten_search_response');
var flattenHit = require('components/index_patterns/_flatten_hit');
var getComputedFields = require('components/index_patterns/_get_computed_fields');
function StubIndexPattern(pattern, timeField, fields) {
this.popularizeField = sinon.spy();
@ -27,6 +31,10 @@ define(function (require) {
});
this.getFields = sinon.spy();
this.toIndexList = _.constant([pattern]);
this.getComputedFields = getComputedFields;
this.flattenSearchResponse = flattenSearchResponse;
this.flattenHit = flattenHit;
this.metaFields = ['_id', '_type', '_source'];
}
return StubIndexPattern;