Skip to content

Commit

Permalink
Filter editors (#11375) (#12184)
Browse files Browse the repository at this point in the history
* Add terms filter

* First stab at inline filter editors

* Cleanup

* Move endpoint to server

* Add delete button to filter editor

* Add sort prefix first utility

* Sort ui-select items by prefix first

* Add sortPrefixFirst tests

* Use small text input instead of styles

* More keyboard-driven filter creation

* Add custom dsl edit support

* Add more actions

* Add alias support

* Simplify label

* Allow freeform entries

* Fix visualize

* Support for dashboard

* Move styles to less and fix visual issues

* Rename map_match to map_phrase

* Detect scripted filters for filter editors

* Simplify range map

* Use match_phrase instead of terms

* Fix scripted exists

* Support scripted phrases

* Don't show DSL editor for new filters

* Disable save unless valid

* Fix error when searching for numbers

* Return correct error and dont suggest values for numbers

* Only suggest filterable fields

* Clear params on field change

* Clear suggestions on field change

* Cache response

* Preserve selected params

* Fix multiple selection

* Don't overflow on multiselect

* Show tooltip on long values

* Use different input for different types

* Show all actions again

* Move dsl editor to directive

* Update filter query dsl editor

* Clean up code

* Move filter field select into own directive

* Move filter operator select into own directive

* Move utils to separate file

* Move field options to field directive

* Move operator options to operator directive

* Move params under operator type

* Move params editor into own directive

* Move param editors to own directives

* Don't highlight values

* Don't use unnecessary onChange

* Use text input if no suggestions

* Remove unused updateFilter

* Enhance params editors
Move params editors to separate file and present different editors based on field type

* Update save filter api

* Update save filter api

* Fix a couple tests

* Fix more tests

* Fix map phrase test

* test cleanup

* Hide editor on esc key

* Use first index pattern if none specified

* FilterEditorUtils test and fix range error

* fix date phrase

* Date format should allow date math values

* Only show add filter button if there are index patterns

* Look up field from indexPatterns service rather than passed index patterns

* Use basepath instead of relative path

* Feedback from bargs

* Don't show filter bar when ya shouldnt

* don't reload panel saved objects

* Show date math instead of formatted date

* Focus on params even when not ui-select

* Hide editor on delete

* Update styles to BEM format

* Update styles

* Add more tests

* Don't show pin/disable

* Fix bug where saving a filter edit without making changes messed up pinning

* Update styles to camelcase

* Fix cutoff from top of screen for ui-select

* Add accessibility

* Align to top

* Align inputs and add placeholders

* Dynamic input sizing

* Fix dashboard panel test
  • Loading branch information
cjcenizal committed Jun 5, 2017
1 parent d9c1df9 commit fe67bfd
Show file tree
Hide file tree
Showing 82 changed files with 1,731 additions and 368 deletions.
2 changes: 2 additions & 0 deletions src/core_plugins/kibana/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import manageUuid from './server/lib/manage_uuid';
import search from './server/routes/api/search';
import settings from './server/routes/api/settings';
import scripts from './server/routes/api/scripts';
import { registerSuggestionsApi } from './server/routes/api/suggestions';
import * as systemApi from './server/lib/system_api';
import mappings from './mappings.json';

Expand Down Expand Up @@ -149,6 +150,7 @@ module.exports = function (kibana) {
search(server);
settings(server);
scripts(server);
registerSuggestionsApi(server);
server.expose('systemApi', systemApi);
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/core_plugins/kibana/public/context/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</div>
</div>

<filter-bar></filter-bar>
<filter-bar index-patterns="[contextApp.indexPattern]"></filter-bar>

<!-- Error feedback -->
<div
Expand Down
2 changes: 2 additions & 0 deletions src/core_plugins/kibana/public/dashboard/__tests__/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('dashboard panel', function () {
parentScope.createChildUiState = sinon.stub().returns(mockUiState);
parentScope.getVisClickHandler = sinon.stub();
parentScope.getVisBrushHandler = sinon.stub();
parentScope.registerPanelIndexPattern = sinon.stub();
parentScope.panel = {
col: 3,
id: 'foo1',
Expand All @@ -45,6 +46,7 @@ describe('dashboard panel', function () {
get-vis-click-handler="getVisClickHandler"
get-vis-brush-handler="getVisBrushHandler"
save-state="saveState"
register-panel-index-pattern="registerPanelIndexPattern"
create-child-ui-state="createChildUiState">
</dashboard-panel>`)(parentScope);
$scope = $el.isolateScope();
Expand Down
7 changes: 6 additions & 1 deletion src/core_plugins/kibana/public/dashboard/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@
</kbn-top-nav>

<!-- Filters. -->
<filter-bar state="state"></filter-bar>
<filter-bar
state="state"
index-patterns="indexPatterns"
></filter-bar>

<div
ng-show="getShouldShowEditHelp()"
Expand Down Expand Up @@ -106,6 +109,7 @@ <h2 class="kuiTitle kuiVerticalRhythm">
toggle-expand="toggleExpandPanel"
create-child-ui-state="createChildUiState"
toggle-expand="toggleExpandPanel"
register-panel-index-pattern="registerPanelIndexPattern"
data-shared-items-count="{{panels.length}}"
></dashboard-grid>

Expand All @@ -118,6 +122,7 @@ <h2 class="kuiTitle kuiVerticalRhythm">
get-vis-click-handler="getFilterBarClickHandler"
get-vis-brush-handler="getBrushEvent"
save-state="saveState"
register-panel-index-pattern="registerPanelIndexPattern"
create-child-ui-state="createChildUiState"
toggle-expand="toggleExpandPanel(expandedPanel.panelIndex)"
></dashboard-panel>
Expand Down
14 changes: 12 additions & 2 deletions src/core_plugins/kibana/public/dashboard/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { UtilsBrushEventProvider } from 'ui/utils/brush_event';
import { FilterBarClickHandlerProvider } from 'ui/filter_bar/filter_bar_click_handler';
import { DashboardState } from './dashboard_state';
import { notify } from 'ui/notify';
import './panel/get_object_loaders_for_dashboard';
import { documentationLinks } from 'ui/documentation_links/documentation_links';
import { showCloneModal } from './top_nav/show_clone_modal';

Expand Down Expand Up @@ -199,15 +200,24 @@ app.directive('dashboardApp', function ($injector) {
return dashboardState.uiState.createChild(path, uiState, true);
};

$scope.onPanelRemoved = (panelIndex) => dashboardState.removePanel(panelIndex);

$scope.$watch('model.darkTheme', () => {
dashboardState.setDarkTheme($scope.model.darkTheme);
updateTheme();
});
$scope.$watch('model.description', () => dashboardState.setDescription($scope.model.description));
$scope.$watch('model.title', () => dashboardState.setTitle($scope.model.title));
$scope.$watch('model.timeRestore', () => dashboardState.setTimeRestore($scope.model.timeRestore));
$scope.indexPatterns = [];

$scope.registerPanelIndexPattern = (panelIndex, pattern) => {
dashboardState.registerPanelIndexPatternMap(panelIndex, pattern);
$scope.indexPatterns = dashboardState.getPanelIndexPatterns();
};

$scope.onPanelRemoved = (panelIndex) => {
dashboardState.removePanel(panelIndex);
$scope.indexPatterns = dashboardState.getPanelIndexPatterns();
};

$scope.$listen(timefilter, 'fetch', $scope.refresh);

Expand Down
12 changes: 12 additions & 0 deletions src/core_plugins/kibana/public/dashboard/dashboard_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,21 @@ export class DashboardState {
//in the 'lose changes' warning message.
this.lastSavedDashboardFilters = this.getFilterState();

// A mapping of panel index to the index pattern it uses.
this.panelIndexPatternMapping = {};

PanelUtils.initPanelIndexes(this.getPanels());
this.createStateMonitor();
}

registerPanelIndexPatternMap(panelIndex, indexPattern) {
this.panelIndexPatternMapping[panelIndex] = indexPattern;
}

getPanelIndexPatterns() {
return _.uniq(Object.values(this.panelIndexPatternMapping));
}

/**
* Resets the state back to the last saved version of the dashboard.
*/
Expand Down Expand Up @@ -269,6 +280,7 @@ export class DashboardState {
_.remove(this.getPanels(), (panel) => {
if (panel.panelIndex === panelIndex) {
this.uiState.removeChild(getPersistedStateId(panel));
delete this.panelIndexPatternMapping[panelIndex];
return true;
} else {
return false;
Expand Down
7 changes: 7 additions & 0 deletions src/core_plugins/kibana/public/dashboard/grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
* @type {function} - Returns a {PersistedState} child uiState for this scope.
*/
createChildUiState: '=',
/**
* Registers an index pattern with the dashboard app used by each panel. The index patterns are used by the
* filter bar for generating field suggestions.
* @type {function(IndexPattern)}
*/
registerPanelIndexPattern: '=',
/**
* Trigger after a panel has been removed from the grid.
*/
Expand Down Expand Up @@ -212,6 +218,7 @@ app.directive('dashboardGrid', function ($compile, Notifier) {
get-vis-click-handler="getVisClickHandler"
get-vis-brush-handler="getVisBrushHandler"
save-state="saveState"
register-panel-index-pattern="registerPanelIndexPattern"
toggle-expand="toggleExpand(${panel.panelIndex})"
create-child-ui-state="createChildUiState">
</li>`;
Expand Down
10 changes: 10 additions & 0 deletions src/core_plugins/kibana/public/dashboard/panel/panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ uiModules
* @type {function} - Returns a {PersistedState} child uiState for this scope.
*/
createChildUiState: '=',
/**
* Registers an index pattern with the dashboard app used by this panel. Used by the filter bar for
* generating field suggestions.
* @type {function(IndexPattern)}
*/
registerPanelIndexPattern: '=',
/**
* Contains information about this panel.
* @type {PanelState}
Expand Down Expand Up @@ -103,7 +109,11 @@ uiModules
$scope.savedObj.vis.setUiState($scope.uiState);
$scope.savedObj.vis.listeners.click = $scope.getVisClickHandler();
$scope.savedObj.vis.listeners.brush = $scope.getVisBrushHandler();
$scope.registerPanelIndexPattern($scope.panel.panelIndex, $scope.savedObj.vis.indexPattern);
} else if ($scope.panel.type === savedSearches.type) {
if ($scope.savedObj.searchSource) {
$scope.registerPanelIndexPattern($scope.panel.panelIndex, $scope.savedObj.searchSource.get('index'));
}
// This causes changes to a saved search to be hidden, but also allows
// the user to locally modify and save changes to a saved search only in a dashboard.
// See https://github.com/elastic/kibana/issues/9523 for more details.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from 'plugins/kibana/dashboard/panel/panel_state';

import _ from 'lodash';

export class PanelUtils {
Expand Down
5 changes: 4 additions & 1 deletion src/core_plugins/kibana/public/discover/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@

<div class="container-fluid" role="main">
<div class="row">
<filter-bar state="state"></filter-bar>
<filter-bar
state="state"
index-patterns="[indexPattern]"
></filter-bar>
</div>
<div class="row">
<div class="col-md-2 sidebar-container collapsible-sidebar">
Expand Down
5 changes: 4 additions & 1 deletion src/core_plugins/kibana/public/visualize/editor/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@
</kbn-top-nav>

<!-- Filters. -->
<filter-bar state="state"></filter-bar>
<filter-bar
state="state"
index-patterns="[indexPattern]"
></filter-bar>

<!-- Custom, full-screen editing UI. -->
<div
Expand Down
1 change: 1 addition & 0 deletions src/core_plugins/kibana/public/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ function VisEditor($rootScope, $scope, $route, timefilter, AppState, $window, kb
$scope.editableVis = editableVis;
$scope.state = $state;
$scope.queryDocLinks = documentationLinks.query;
$scope.dateDocLinks = documentationLinks.date;

// Create a PersistedState instance.
$scope.uiState = $state.makeStateful('uiState');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { registerValueSuggestions } from './register_value_suggestions';

export function registerSuggestionsApi(server) {
registerValueSuggestions(server);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import handleESError from '../../../lib/handle_es_error';

export function registerValueSuggestions(server) {
server.route({
path: '/api/kibana/suggestions/values/{index}',
method: ['POST'],
handler: function (req, reply) {
const { index } = req.params;
const { field, query } = req.payload;

const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
const include = query ? `.*${query}.*` : undefined;
const body = getBody({ field, include });

return callWithRequest(req, 'search', { index, body })
.then((res) => {
const suggestions = res.aggregations.suggestions.buckets.map(bucket => bucket.key);
return reply(suggestions);
})
.catch(error => reply(handleESError(error)));
}
});
}

function getBody(terms) {
return {
aggs: {
suggestions: { terms }
}
};
}
16 changes: 16 additions & 0 deletions src/fixtures/filters/exists_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const existsFilter = {
'meta': {
'index': 'logstash-*',
'negate': false,
'disabled': false,
'type': 'exists',
'key': 'machine.os',
'value': 'exists'
},
'exists': {
'field': 'machine.os'
},
'$state': {
'store': 'appState'
}
};
5 changes: 5 additions & 0 deletions src/fixtures/filters/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { phraseFilter } from './phrase_filter';
export { scriptedPhraseFilter } from './scripted_phrase_filter';
export { phrasesFilter } from './phrases_filter';
export { rangeFilter } from './range_filter';
export { existsFilter } from './exists_filter';
21 changes: 21 additions & 0 deletions src/fixtures/filters/phrase_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const phraseFilter = {
meta: {
negate: false,
index: 'logstash-*',
type: 'phrase',
key: 'machine.os',
value: 'ios',
disabled: false
},
query: {
match: {
'machine.os': {
query: 'ios',
type: 'phrase'
}
}
},
$state: {
store: 'appState'
}
};
34 changes: 34 additions & 0 deletions src/fixtures/filters/phrases_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export const phrasesFilter = {
meta: {
index: 'logstash-*',
type: 'phrases',
key: 'machine.os.raw',
value: 'win xp, osx',
params: [
'win xp',
'osx'
],
negate: false,
disabled: false
},
query: {
bool: {
should: [
{
match_phrase: {
'machine.os.raw': 'win xp'
}
},
{
match_phrase: {
'machine.os.raw': 'osx'
}
}
],
minimum_should_match: 1
}
},
$state: {
store: 'appState'
}
};
20 changes: 20 additions & 0 deletions src/fixtures/filters/range_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const rangeFilter = {
'meta': {
'index': 'logstash-*',
'negate': false,
'disabled': false,
'alias': null,
'type': 'range',
'key': 'bytes',
'value': '0 to 10'
},
'range': {
'bytes': {
'gte': 0,
'lt': 10
}
},
'$state': {
'store': 'appState'
}
};
23 changes: 23 additions & 0 deletions src/fixtures/filters/scripted_phrase_filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const scriptedPhraseFilter = {
'meta': {
'negate': false,
'index': 'logstash-*',
'field': 'script string',
'type': 'phrase',
'key': 'script string',
'value': 'i am a string',
'disabled': false
},
'script': {
'script': {
'inline': 'boolean compare(Supplier s, def v) {return s.get() == v;}compare(() -> { \'i am a string\' }, params.value);',
'lang': 'painless',
'params': {
'value': 'i am a string'
}
}
},
'$state': {
'store': 'appState'
}
};
8 changes: 7 additions & 1 deletion src/ui/public/agg_types/controls/date_ranges.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@
<tr>
<td colspan="3">
<small>
<a target="_window" href="http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html#date-math">Accepted Date Formats <i class="fa-link fa"></i></a>
<a
class="kuiLink"
ng-href="{{dateDocLinks.dateMath}}"
target="_blank"
>
Accepted date formats
</a>
</small>
</td>
</tr>
Expand Down
Loading

0 comments on commit fe67bfd

Please sign in to comment.