Skip to content

Commit cda3de6

Browse files
authored
Migrate legacy sort arrays on saved searches (#43038)
With my multi sort PR I changed the sort property on saved searches to contain a nested array. Discover and Dashboard were backwards compatible with the old format but it turns out the nested array caused issues for CSV export. Instead of trying to support single and two dimension arrays everywhere, this PR simply adds a migration for saved searches in 7.4 and updates our sample data sets so that we can always expect sort objects to be two dimensional arrays. I also cleaned up the backwards compatibility code in Discover and Dashboard.
1 parent 53cb9f9 commit cda3de6

File tree

9 files changed

+115
-19
lines changed

9 files changed

+115
-19
lines changed

src/legacy/core_plugins/kibana/migrations/migrations.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,24 @@ function replaceMovAvgToMovFn(doc, logger) {
365365
return doc;
366366
}
367367

368+
function migrateSearchSortToNestedArray(doc) {
369+
const sort = get(doc, 'attributes.sort');
370+
if (!sort) return doc;
371+
372+
// Don't do anything if we already have a two dimensional array
373+
if (Array.isArray(sort) && sort.length > 0 && Array.isArray(sort[0])) {
374+
return doc;
375+
}
376+
377+
return {
378+
...doc,
379+
attributes: {
380+
...doc.attributes,
381+
sort: [doc.attributes.sort],
382+
}
383+
};
384+
}
385+
368386
const executeMigrations720 = flow(
369387
migratePercentileRankAggregation,
370388
migrateDateHistogramAggregation
@@ -376,6 +394,10 @@ const executeMigrations730 = flow(
376394
replaceMovAvgToMovFn
377395
);
378396

397+
const executeSearchMigrations740 = flow(
398+
migrateSearchSortToNestedArray,
399+
);
400+
379401
export const migrations = {
380402
'index-pattern': {
381403
'6.5.0': doc => {
@@ -530,5 +552,6 @@ export const migrations = {
530552
migrateIndexPattern(doc);
531553
return doc;
532554
},
555+
'7.4.0': executeSearchMigrations740,
533556
},
534557
};

src/legacy/core_plugins/kibana/migrations/migrations.test.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,4 +1788,56 @@ Object {
17881788
/* eslint-enable max-len */
17891789
});
17901790
});
1791+
1792+
describe('7.4.0', function () {
1793+
const migration = migrations.search['7.4.0'];
1794+
1795+
test('transforms one dimensional sort arrays into two dimensional arrays', () => {
1796+
const doc = {
1797+
id: '123',
1798+
type: 'search',
1799+
attributes: {
1800+
sort: ['bytes', 'desc'],
1801+
},
1802+
};
1803+
1804+
const expected = {
1805+
id: '123',
1806+
type: 'search',
1807+
attributes: {
1808+
sort: [['bytes', 'desc']],
1809+
},
1810+
};
1811+
1812+
const migratedDoc = migration(doc);
1813+
1814+
expect(migratedDoc).toEqual(expected);
1815+
});
1816+
1817+
test('doesn\'t modify search docs that already have two dimensional sort arrays', () => {
1818+
const doc = {
1819+
id: '123',
1820+
type: 'search',
1821+
attributes: {
1822+
sort: [['bytes', 'desc']],
1823+
},
1824+
};
1825+
1826+
const migratedDoc = migration(doc);
1827+
1828+
expect(migratedDoc).toEqual(doc);
1829+
});
1830+
1831+
test('doesn\'t modify search docs that have no sort array', () => {
1832+
const doc = {
1833+
id: '123',
1834+
type: 'search',
1835+
attributes: {},
1836+
};
1837+
1838+
const migratedDoc = migration(doc);
1839+
1840+
expect(migratedDoc).toEqual(doc);
1841+
});
1842+
});
17911843
});

src/legacy/core_plugins/kibana/public/discover/doc_table/__tests__/lib/get_sort.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,15 @@ describe('docTable', function () {
3838
expect(getSort).to.be.a(Function);
3939
});
4040

41-
it('should return an array of objects if passed a 2 item array', function () {
42-
expect(getSort(['bytes', 'desc'], indexPattern)).to.eql([{ bytes: 'desc' }]);
41+
it('should return an array of objects', function () {
42+
expect(getSort([['bytes', 'desc']], indexPattern)).to.eql([{ bytes: 'desc' }]);
4343

4444
delete indexPattern.timeFieldName;
45-
expect(getSort(['bytes', 'desc'], indexPattern)).to.eql([{ bytes: 'desc' }]);
45+
expect(getSort([['bytes', 'desc']], indexPattern)).to.eql([{ bytes: 'desc' }]);
46+
});
47+
48+
it('should passthrough arrays of objects', () => {
49+
expect(getSort([{ bytes: 'desc' }], indexPattern)).to.eql([{ bytes: 'desc' }]);
4650
});
4751

4852
it('should sort by the default when passed an unsortable field', function () {
@@ -74,7 +78,26 @@ describe('docTable', function () {
7478
});
7579

7680
it('should return an array of arrays for sortable fields', function () {
77-
expect(getSort.array(['bytes', 'desc'], indexPattern)).to.eql([[ 'bytes', 'desc' ]]);
81+
expect(getSort.array([['bytes', 'desc']], indexPattern)).to.eql([[ 'bytes', 'desc' ]]);
82+
});
83+
84+
it('should return an array of arrays from an array of elasticsearch sort objects', function () {
85+
expect(getSort.array([{ bytes: 'desc' }], indexPattern)).to.eql([[ 'bytes', 'desc' ]]);
86+
});
87+
88+
it('should sort by the default when passed an unsortable field', function () {
89+
expect(getSort.array([{ 'non-sortable': 'asc' }], indexPattern)).to.eql([['time', 'desc']]);
90+
expect(getSort.array([{ lol_nope: 'asc' }], indexPattern)).to.eql([['time', 'desc']]);
91+
92+
delete indexPattern.timeFieldName;
93+
expect(getSort.array([{ 'non-sortable': 'asc' }], indexPattern)).to.eql([[ '_score', 'desc' ]]);
94+
});
95+
96+
it('should sort by the default when passed an empty sort', () => {
97+
expect(getSort.array([], indexPattern)).to.eql([['time', 'desc']]);
98+
99+
delete indexPattern.timeFieldName;
100+
expect(getSort.array([], indexPattern)).to.eql([[ '_score', 'desc' ]]);
78101
});
79102
});
80103
});

src/legacy/core_plugins/kibana/public/discover/doc_table/lib/get_sort.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,25 @@ function createSortObject(sortPair, indexPattern) {
2929
const [ field, direction ] = sortPair;
3030
return { [field]: direction };
3131
}
32+
else if (_.isPlainObject(sortPair) && isSortable(Object.keys(sortPair)[0], indexPattern)) {
33+
return sortPair;
34+
}
3235
else {
3336
return undefined;
3437
}
3538
}
3639

3740
/**
3841
* Take a sorting array and make it into an object
39-
* @param {array} sort 2 item array [fieldToSort, directionToSort]
42+
* @param {array} sort two dimensional array [[fieldToSort, directionToSort]]
43+
* or an array of objects [{fieldToSort: directionToSort}]
4044
* @param {object} indexPattern used for determining default sort
4145
* @returns {object} a sort object suitable for returning to elasticsearch
4246
*/
4347
export function getSort(sort, indexPattern, defaultSortOrder = 'desc') {
4448

4549
let sortObjects;
4650
if (Array.isArray(sort)) {
47-
if (sort.length > 0 && !Array.isArray(sort[0])) {
48-
sort = [sort];
49-
}
5051
sortObjects = _.compact(sort.map((sortPair) => createSortObject(sortPair, indexPattern)));
5152
}
5253

src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ import { ISearchEmbeddable, SearchInput, SearchOutput } from './types';
4545
interface SearchScope extends ng.IScope {
4646
columns?: string[];
4747
description?: string;
48-
sort?: string[] | string[][];
48+
sort?: string[][];
4949
searchSource?: SearchSource;
5050
sharedItemTitle?: string;
5151
inspectorAdapters?: Adapters;
52-
setSortOrder?: (sortPair: [string, string]) => void;
52+
setSortOrder?: (sortPair: [[string, string]]) => void;
5353
removeColumn?: (column: string) => void;
5454
addColumn?: (column: string) => void;
5555
moveColumn?: (column: string, index: number) => void;
@@ -264,9 +264,6 @@ export class SearchEmbeddable extends Embeddable<SearchInput, SearchOutput>
264264
// been overridden in a dashboard.
265265
searchScope.columns = this.input.columns || this.savedSearch.columns;
266266
searchScope.sort = this.input.sort || this.savedSearch.sort;
267-
if (searchScope.sort.length && !Array.isArray(searchScope.sort[0])) {
268-
searchScope.sort = [searchScope.sort];
269-
}
270267
searchScope.sharedItemTitle = this.panelTitle;
271268

272269
if (

src/legacy/core_plugins/kibana/public/discover/embeddable/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface SearchInput extends EmbeddableInput {
3434
filters?: Filter[];
3535
hidePanelTitles?: boolean;
3636
columns?: string[];
37-
sort?: string[];
37+
sort?: string[][];
3838
}
3939

4040
export interface SearchOutput extends EmbeddableOutput {

src/legacy/core_plugins/kibana/public/discover/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface SavedSearch {
2525
searchSource: SearchSource;
2626
description?: string;
2727
columns: string[];
28-
sort: string[];
28+
sort: string[][];
2929
destroy: () => void;
3030
}
3131
export interface SavedSearchLoader {

src/legacy/server/sample_data/data_sets/ecommerce/saved_objects.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,10 @@ export const getSavedObjects = () => [
212212
"taxful_total_price",
213213
"total_quantity"
214214
],
215-
"sort": [
215+
"sort": [[
216216
"order_date",
217217
"desc"
218-
],
218+
]],
219219
"version": 1,
220220
"kibanaSavedObjectMeta": {
221221
"searchSourceJSON": "{\"index\":\"ff959d40-b880-11e8-a6d9-e546fe2bba5f\",\"highlightAll\":true,\"version\":true,\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"

src/legacy/server/sample_data/data_sets/flights/saved_objects.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,10 @@ export const getSavedObjects = () => [
8484
"Cancelled",
8585
"FlightDelayType"
8686
],
87-
"sort": [
87+
"sort": [[
8888
"timestamp",
8989
"desc"
90-
],
90+
]],
9191
"version": 1,
9292
"kibanaSavedObjectMeta": {
9393
"searchSourceJSON": "{\"index\":\"d3d7af60-4c81-11e8-b3d7-01146121b73d\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[]}"

0 commit comments

Comments
 (0)