Merge pull request #3571 from w33ble/sort-by-scripted

Allow sorting by scripted fields
This commit is contained in:
Spencer 2015-06-11 14:03:34 -07:00
commit 4c3d06fdf0
8 changed files with 134 additions and 57 deletions

View file

@ -1,29 +1,53 @@
define(function (require) {
var _ = require('lodash');
return function normalizeSortRequest(config) {
var defaultSortOptions = config.get('sort:options');
/**
* Decorate queries with default parameters
* @param {query} query object
* @returns {object}
*/
return function (sortObject) {
if (!_.isArray(sortObject)) sortObject = [sortObject];
var defaultSortOptions = config.get('sort:options');
return function (sortObject, indexPattern) {
var normalizedSort = [];
/*
Normalize the sort description to the more verbose format:
{ someField: "desc" } into { someField: { "order": "desc"}}
*/
_.each(sortObject, function (sortable) {
var sortField = _.keys(sortable)[0];
var sortValue = sortable[sortField];
if (_.isString(sortValue)) {
sortValue = sortable[sortField] = { order: sortValue };
}
_.defaults(sortValue, defaultSortOptions);
// [].concat({}) -> [{}], [].concat([{}]) -> [{}]
return [].concat(sortObject).map(function (sortable) {
return normalize(sortable, indexPattern);
});
return sortObject;
};
/*
Normalize the sort description to the more verbose format:
{ someField: "desc" } into { someField: { "order": "desc"}}
*/
function normalize(sortable, indexPattern) {
var normalized = {};
var sortField = _.keys(sortable)[0];
var sortValue = sortable[sortField];
var indexField = indexPattern.fields.byName[sortField];
if (indexField && indexField.scripted && indexField.sortable) {
var direction;
if (_.isString(sortValue)) direction = sortValue;
if (_.isObject(sortValue) && sortValue.order) direction = sortValue.order;
sortField = '_script';
sortValue = {
script: indexField.script,
type: indexField.type,
order: direction
};
} else {
if (_.isString(sortValue)) {
sortValue = { order: sortValue };
}
sortValue = _.defaults({}, sortValue, defaultSortOptions);
}
normalized[sortField] = sortValue;
return normalized;
}
};
});

View file

@ -8,7 +8,6 @@ define(function (require) {
var SegmentedRequest = Private(require('components/courier/fetch/request/segmented'));
var normalizeSortRequest = Private(require('components/courier/data_source/_normalize_sort_request'));
_(SearchSource).inherits(SourceAbstract);
function SearchSource(initialState) {
SearchSource.Super.call(this, initialState);
@ -179,7 +178,7 @@ define(function (require) {
key = '_source';
/* fall through */
case 'sort':
val = normalizeSortRequest(val);
val = normalizeSortRequest(val, this.get('index'));
/* fall through */
default:
state.body = state.body || {};

View file

@ -23,7 +23,8 @@ define(function (require) {
};
$scope.tooltip = function (column) {
if (!sortableField(column)) return ''; else return 'Sort by ' + shortDotsFilter(column);
if (!sortableField(column)) return '';
return 'Sort by ' + shortDotsFilter(column);
};
$scope.canRemove = function (name) {

View file

@ -40,4 +40,4 @@ define(function (require) {
};
return getSort;
});
});

View file

@ -38,7 +38,8 @@ define(function (require) {
var indexed = !!spec.indexed;
var scripted = !!spec.scripted;
var sortable = indexed && type.sortable;
var sortable = (indexed || scripted) && type.sortable;
var bucketable = indexed || scripted;
var filterable = spec.name === '_id' || scripted || (indexed && type.filterable);

View file

@ -23,6 +23,7 @@ define(function (require) {
{ name: 'custom_user_field', type: 'conflict', indexed: false, analyzed: false, sortable: false, filterable: true },
{ name: 'script string', type: 'string', scripted: true, script: '\'i am a string\'', lang: 'expression' },
{ name: 'script number', type: 'number', scripted: true, script: '1234', lang: 'expression' },
{ name: 'script murmur3', type: 'murmur3', scripted: true, script: '1234', lang: 'expression'},
].map(function (field) {
field.count = field.count || 0;
field.scripted = field.scripted || false;

View file

@ -4,45 +4,86 @@ define(function (require) {
require('angular').module('normalizeSortRequest', ['kibana']);
var normalizeSortRequest;
var indexPattern;
var normalizedSort;
beforeEach(module('kibana'));
beforeEach(inject(function (Private) {
normalizeSortRequest = Private(require('components/courier/data_source/_normalize_sort_request'));
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
normalizedSort = [{
someField: {
order: 'desc',
unmapped_type: 'boolean'
}
}];
}));
var normalizedSort = [{
someField: {
order: 'desc',
unmapped_type: 'boolean'
}
}];
it('make sure sort is an array', function () {
var result = normalizeSortRequest(
{ someField: 'desc'}
);
it('should return an array', function () {
var sortable = { someField: 'desc'};
var result = normalizeSortRequest(sortable, indexPattern);
expect(result).to.be.an(Array);
expect(result).to.eql(normalizedSort);
// ensure object passed in is not mutated
expect(result[0]).to.not.be.equal(sortable);
expect(sortable).to.eql({ someField: 'desc'});
});
it('makes plain string sort into the more verbose format', function () {
var result = normalizeSortRequest(
[{ someField: 'desc'}]
);
it('should make plain string sort into the more verbose format', function () {
var result = normalizeSortRequest([{ someField: 'desc'}], indexPattern);
expect(result).to.eql(normalizedSort);
});
it('appends default sort options', function () {
var result = normalizeSortRequest(
[{
someField: {
order: 'desc',
unmapped_type: 'boolean'
}
}]
);
it('should append default sort options', function () {
var sortState = [{
someField: {
order: 'desc',
unmapped_type: 'boolean'
}
}];
var result = normalizeSortRequest(sortState, indexPattern);
expect(result).to.eql(normalizedSort);
});
it('should enable script based sorting', function () {
var fieldName = 'script string';
var direction = 'desc';
var indexField = indexPattern.fields.byName[fieldName];
var sortState = {};
sortState[fieldName] = direction;
normalizedSort = {
_script: {
script: indexField.script,
type: indexField.type,
order: direction
}
};
var result = normalizeSortRequest(sortState, indexPattern);
expect(result).to.eql([normalizedSort]);
sortState[fieldName] = { order: direction };
result = normalizeSortRequest([sortState], indexPattern);
expect(result).to.eql([normalizedSort]);
});
it('should use script based sorting only on sortable types', function () {
var fieldName = 'script murmur3';
var direction = 'asc';
var indexField = indexPattern.fields.byName[fieldName];
var sortState = {};
sortState[fieldName] = direction;
normalizedSort = {};
normalizedSort[fieldName] = {
order: direction,
unmapped_type: 'boolean'
};
var result = normalizeSortRequest([sortState], indexPattern);
expect(result).to.eql([normalizedSort]);
});
});
});

View file

@ -1,15 +1,16 @@
define(function (require) {
var getSort = require('components/doc_table/lib/get_sort');
var indexPattern =
var defaultSort = {time: 'desc'};
var indexPattern;
describe('docTable', function () {
beforeEach(module('kibana'));
beforeEach(inject(function (Private, _$rootScope_, Promise) {
indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern'));
}));
describe('getSort function', function () {
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);
});
@ -22,17 +23,17 @@ define(function (require) {
});
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'});
expect(getSort(['_id', 'asc'], indexPattern)).to.eql(defaultSort);
expect(getSort(['lol_nope', 'asc'], indexPattern)).to.eql(defaultSort);
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([], indexPattern)).to.eql({time: 'desc'});
expect(getSort(['foo'], indexPattern)).to.eql({time: 'desc'});
expect(getSort({foo: 'bar'}, indexPattern)).to.eql({time: 'desc'});
expect(getSort([], indexPattern)).to.eql(defaultSort);
expect(getSort(['foo'], indexPattern)).to.eql(defaultSort);
expect(getSort({foo: 'bar'}, indexPattern)).to.eql(defaultSort);
});
it('should sort by score on non-time patterns', function () {
@ -42,7 +43,16 @@ define(function (require) {
expect(getSort(['foo'], indexPattern)).to.eql({_score: 'desc'});
expect(getSort({foo: 'bar'}, indexPattern)).to.eql({_score: 'desc'});
});
});
describe('getSort.array function', function () {
it('should have an array method', function () {
expect(getSort.array).to.be.a(Function);
});
it('should return an array for sortable fields', function () {
expect(getSort.array(['bytes', 'desc'], indexPattern)).to.eql([ 'bytes', 'desc' ]);
});
});
});
});