mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Indexed fields type filter (#10708)
* Add a dropdown to filter indexed fields by type Added tests and test subjects for index pattern filters Use UI Framework components Change index field filter to use kuiFieldGroup, move filters below tabs * Dynamically load indexed type filter based on existing field types * Iterate over fields once to get types / languages * Only check scripted flag for checking scripted lang fields, all other fields should have their type added to type filter
This commit is contained in:
parent
1914d159a5
commit
b4c18e4aa4
9 changed files with 246 additions and 17 deletions
|
@ -80,17 +80,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Search box -->
|
||||
<form role="form" class="kuiVerticalRhythm">
|
||||
<input
|
||||
aria-label="Filter"
|
||||
ng-model="fieldFilter"
|
||||
class="kuiTextInput fullWidth"
|
||||
type="text"
|
||||
placeholder="Filter"
|
||||
/>
|
||||
</form>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="kuiTabs kuiVerticalRhythm">
|
||||
<button
|
||||
|
@ -108,6 +97,52 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Field Filters -->
|
||||
<form role="form" class="kuiFieldGroup kuiVerticalRhythm">
|
||||
<div class="kuiFieldGroupSection kuiFieldGroupSection--wide">
|
||||
<div class="kuiSearchInput">
|
||||
<div class="kuiSearchInput__icon kuiIcon fa-search"></div>
|
||||
<input
|
||||
class="kuiSearchInput__input"
|
||||
type="text"
|
||||
aria-label="Filter"
|
||||
ng-model="fieldFilter"
|
||||
placeholder="Filter"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiFieldGroupSection"
|
||||
ng-if="state.tab == 'indexedFields' && indexedFieldTypes.length > 0"
|
||||
>
|
||||
<select
|
||||
data-test-subj="indexedFieldTypeFilterDropdown"
|
||||
class="kuiSelect"
|
||||
ng-model="indexedFieldTypeFilter"
|
||||
ng-change="changeFilter('indexedFieldTypeFilter', indexedFieldTypeFilter)"
|
||||
ng-options="o for o in indexedFieldTypes"
|
||||
>
|
||||
<option value="">All field types</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="kuiFieldGroupSection"
|
||||
ng-if="state.tab == 'scriptedFields' && scriptedFieldLanguages.length > 0"
|
||||
>
|
||||
<select
|
||||
data-test-subj="scriptedFieldLanguageFilterDropdown"
|
||||
class="kuiSelect"
|
||||
ng-model="scriptedFieldLanguageFilter"
|
||||
ng-change="changeFilter('scriptedFieldLanguageFilter', scriptedFieldLanguageFilter)"
|
||||
ng-options="o for o in scriptedFieldLanguages"
|
||||
>
|
||||
<option value="">All languages</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Tab content -->
|
||||
<indexed-fields
|
||||
ng-show="state.tab == 'indexedFields'"
|
||||
|
|
|
@ -52,8 +52,28 @@ uiModules.get('apps/management')
|
|||
|
||||
$scope.$watch('indexPattern.fields', function () {
|
||||
$scope.editSections = Private(IndicesEditSectionsProvider)($scope.indexPattern);
|
||||
$scope.refreshFilters();
|
||||
});
|
||||
|
||||
$scope.refreshFilters = function () {
|
||||
const indexedFieldTypes = [];
|
||||
const scriptedFieldLanguages = [];
|
||||
$scope.indexPattern.fields.forEach(field => {
|
||||
if (field.scripted) {
|
||||
scriptedFieldLanguages.push(field.lang);
|
||||
} else {
|
||||
indexedFieldTypes.push(field.type);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.indexedFieldTypes = _.unique(indexedFieldTypes);
|
||||
$scope.scriptedFieldLanguages = _.unique(scriptedFieldLanguages);
|
||||
};
|
||||
|
||||
$scope.changeFilter = function (filter, val) {
|
||||
$scope[filter] = val || ''; // null causes filter to check for null explicitly
|
||||
};
|
||||
|
||||
$scope.changeTab = function (obj) {
|
||||
$state.tab = obj.index;
|
||||
$state.save();
|
||||
|
|
|
@ -32,12 +32,15 @@ uiModules.get('apps/management')
|
|||
{ title: 'controls', sortable: false }
|
||||
];
|
||||
|
||||
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter'], refreshRows);
|
||||
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'indexedFieldTypeFilter'], refreshRows);
|
||||
|
||||
function refreshRows() {
|
||||
// clear and destroy row scopes
|
||||
_.invoke(rowScopes.splice(0), '$destroy');
|
||||
const fields = filter($scope.indexPattern.getNonScriptedFields(), { name: $scope.fieldFilter });
|
||||
const fields = filter($scope.indexPattern.getNonScriptedFields(), {
|
||||
name: $scope.fieldFilter,
|
||||
type: $scope.indexedFieldTypeFilter
|
||||
});
|
||||
const sourceFilters = $scope.indexPattern.sourceFilters && $scope.indexPattern.sourceFilters.map(f => f.value) || [];
|
||||
const fieldWildcardMatch = fieldWildcardMatcher(sourceFilters);
|
||||
_.find($scope.editSections, { index: 'indexedFields' }).count = fields.length; // Update the tab count
|
||||
|
@ -60,7 +63,10 @@ uiModules.get('apps/management')
|
|||
{
|
||||
markup: typeHtml,
|
||||
scope: childScope,
|
||||
value: field.type
|
||||
value: field.type,
|
||||
attr: {
|
||||
'data-test-subj': 'indexedFieldType'
|
||||
}
|
||||
},
|
||||
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
|
||||
{
|
||||
|
|
|
@ -30,13 +30,16 @@ uiModules.get('apps/management')
|
|||
{ title: 'controls', sortable: false }
|
||||
];
|
||||
|
||||
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter'], refreshRows);
|
||||
$scope.$watchMulti(['[]indexPattern.fields', 'fieldFilter', 'scriptedFieldLanguageFilter'], refreshRows);
|
||||
|
||||
function refreshRows() {
|
||||
_.invoke(rowScopes, '$destroy');
|
||||
rowScopes.length = 0;
|
||||
|
||||
const fields = filter($scope.indexPattern.getScriptedFields(), { name: $scope.fieldFilter });
|
||||
const fields = filter($scope.indexPattern.getScriptedFields(), {
|
||||
name: $scope.fieldFilter,
|
||||
lang: $scope.scriptedFieldLanguageFilter
|
||||
});
|
||||
_.find($scope.editSections, { index: 'scriptedFields' }).count = fields.length; // Update the tab count
|
||||
|
||||
$scope.rows = fields.map(function (field) {
|
||||
|
@ -46,7 +49,12 @@ uiModules.get('apps/management')
|
|||
|
||||
return [
|
||||
_.escape(field.name),
|
||||
_.escape(field.lang),
|
||||
{
|
||||
markup: field.lang,
|
||||
attr: {
|
||||
'data-test-subj': 'scriptedFieldLang'
|
||||
}
|
||||
},
|
||||
_.escape(field.script),
|
||||
_.get($scope.indexPattern, ['fieldFormatMap', field.name, 'type', 'title']),
|
||||
{
|
||||
|
|
|
@ -209,6 +209,7 @@ kbn-management-objects-view {
|
|||
|
||||
kbn-management-indices {
|
||||
.fields {
|
||||
display: block;
|
||||
table {
|
||||
.table-striped()
|
||||
}
|
||||
|
|
59
test/functional/apps/management/_index_pattern_filter.js
Normal file
59
test/functional/apps/management/_index_pattern_filter.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
|
||||
import expect from 'expect.js';
|
||||
|
||||
import {
|
||||
bdd,
|
||||
defaultTimeout,
|
||||
scenarioManager,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
import PageObjects from '../../../support/page_objects';
|
||||
|
||||
bdd.describe('index pattern filter', function describeIndexTests() {
|
||||
bdd.before(function () {
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
return esClient.deleteAndUpdateConfigDoc()
|
||||
.then(function () {
|
||||
return PageObjects.settings.navigateTo();
|
||||
})
|
||||
.then(function () {
|
||||
return PageObjects.settings.clickKibanaIndicies();
|
||||
});
|
||||
});
|
||||
|
||||
bdd.beforeEach(function () {
|
||||
return PageObjects.settings.createIndexPattern();
|
||||
});
|
||||
|
||||
bdd.afterEach(function () {
|
||||
return PageObjects.settings.removeIndexPattern();
|
||||
});
|
||||
|
||||
bdd.it('should filter indexed fields', async function () {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaIndicies();
|
||||
const fieldTypesBefore = await PageObjects.settings.getFieldTypes();
|
||||
|
||||
|
||||
await PageObjects.settings.setFieldTypeFilter('string');
|
||||
|
||||
await PageObjects.common.try(async function() {
|
||||
const fieldTypes = await PageObjects.settings.getFieldTypes();
|
||||
expect(fieldTypes.length).to.be.above(0);
|
||||
for (const fieldType of fieldTypes) {
|
||||
expect(fieldType).to.be('string');
|
||||
}
|
||||
});
|
||||
|
||||
await PageObjects.settings.setFieldTypeFilter('number');
|
||||
|
||||
await PageObjects.common.try(async function() {
|
||||
const fieldTypes = await PageObjects.settings.getFieldTypes();
|
||||
expect(fieldTypes.length).to.be.above(0);
|
||||
for (const fieldType of fieldTypes) {
|
||||
expect(fieldType).to.be('number');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
70
test/functional/apps/management/_scripted_fields_filter.js
Normal file
70
test/functional/apps/management/_scripted_fields_filter.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
import expect from 'expect.js';
|
||||
|
||||
import {
|
||||
bdd,
|
||||
scenarioManager,
|
||||
esClient
|
||||
} from '../../../support';
|
||||
|
||||
import PageObjects from '../../../support/page_objects';
|
||||
|
||||
|
||||
bdd.describe('filter scripted fields', function describeIndexTests() {
|
||||
|
||||
bdd.beforeEach(async function () {
|
||||
await PageObjects.remote.setWindowSize(1200,800);
|
||||
// delete .kibana index and then wait for Kibana to re-create it
|
||||
await esClient.deleteAndUpdateConfigDoc({ 'dateFormat:tz':'UTC' });
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaIndicies();
|
||||
await PageObjects.settings.createIndexPattern();
|
||||
await esClient.updateConfigDoc({ 'dateFormat:tz':'UTC' });
|
||||
});
|
||||
|
||||
const scriptedExpressionFieldName = 'ram_expr1';
|
||||
const scriptedPainlessFieldName = 'ram_pain1';
|
||||
|
||||
bdd.it('should filter scripted fields', async function () {
|
||||
await PageObjects.settings.navigateTo();
|
||||
await PageObjects.settings.clickKibanaIndicies();
|
||||
await PageObjects.settings.clickScriptedFieldsTab();
|
||||
const scriptedFieldLangsBefore = await PageObjects.settings.getScriptedFieldLangs();
|
||||
await PageObjects.common.debug('add scripted field');
|
||||
await PageObjects.settings
|
||||
.addScriptedField(scriptedExpressionFieldName,
|
||||
'expression', 'number', null, '1', 'doc[\'machine.ram\'].value / (1024 * 1024 * 1024)'
|
||||
);
|
||||
await PageObjects.settings
|
||||
.addScriptedField(scriptedPainlessFieldName,
|
||||
'painless', 'number', null, '1', 'doc[\'machine.ram\'].value / (1024 * 1024 * 1024)'
|
||||
);
|
||||
|
||||
// confirm two additional scripted fields were created
|
||||
await PageObjects.common.try(async function() {
|
||||
const scriptedFieldLangs = await PageObjects.settings.getScriptedFieldLangs();
|
||||
expect(scriptedFieldLangs.length).to.be(scriptedFieldLangsBefore.length + 2);
|
||||
});
|
||||
|
||||
await PageObjects.settings.setScriptedFieldLanguageFilter('painless');
|
||||
|
||||
await PageObjects.common.try(async function() {
|
||||
const scriptedFieldLangs = await PageObjects.settings.getScriptedFieldLangs();
|
||||
expect(scriptedFieldLangs.length).to.be.above(0);
|
||||
for (const lang of scriptedFieldLangs) {
|
||||
expect(lang).to.be('painless');
|
||||
}
|
||||
});
|
||||
|
||||
await PageObjects.settings.setScriptedFieldLanguageFilter('expression');
|
||||
|
||||
await PageObjects.common.try(async function() {
|
||||
const scriptedFieldLangs = await PageObjects.settings.getScriptedFieldLangs();
|
||||
expect(scriptedFieldLangs.length).to.be.above(0);
|
||||
for (const lang of scriptedFieldLangs) {
|
||||
expect(lang).to.be('expression');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -22,4 +22,6 @@ bdd.describe('settings app', function () {
|
|||
require('./_index_pattern_popularity');
|
||||
require('./_kibana_settings');
|
||||
require('./_scripted_fields');
|
||||
require('./_index_pattern_filter');
|
||||
require('./_scripted_fields_filter');
|
||||
});
|
||||
|
|
|
@ -202,6 +202,34 @@ export default class SettingsPage {
|
|||
});
|
||||
}
|
||||
|
||||
async getFieldTypes() {
|
||||
const fieldNameCells = await PageObjects.common.findAllTestSubjects('editIndexPattern indexedFieldType');
|
||||
return await mapAsync(fieldNameCells, async cell => {
|
||||
return (await cell.getVisibleText()).trim();
|
||||
});
|
||||
}
|
||||
|
||||
async getScriptedFieldLangs() {
|
||||
const fieldNameCells = await PageObjects.common.findAllTestSubjects('editIndexPattern scriptedFieldLang');
|
||||
return await mapAsync(fieldNameCells, async cell => {
|
||||
return (await cell.getVisibleText()).trim();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async setFieldTypeFilter(type) {
|
||||
await this.remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('select[data-test-subj="indexedFieldTypeFilterDropdown"] > option[label="' + type + '"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
|
||||
async setScriptedFieldLanguageFilter(language) {
|
||||
await this.remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('select[data-test-subj="scriptedFieldLanguageFilterDropdown"] > option[label="' + language + '"]')
|
||||
.click();
|
||||
}
|
||||
|
||||
async goToPage(pageNum) {
|
||||
await this.remote.setFindTimeout(defaultFindTimeout)
|
||||
.findByCssSelector('ul.pagination-other-pages-list.pagination-sm.ng-scope li.ng-scope:nth-child(' +
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue