Skip to content

Commit

Permalink
Stateful search bar default behaviors (#56160)
Browse files Browse the repository at this point in the history
* Clean up discover

* Clean up visualize

* Clean up dashboard

* use-default-behaviors

* ts

* Discover interval changing

* timerange for interval definition in editor

* ts

* Revert most of changes to dashboard app because of changes in #55443

* Fix spaces

* Revert editor to scope PR!

* typo

* keep savedQuery state in create search bar

* update saved query to save it with the state

* revert all dashboard changes

* saved queries

* @kertal code review

* fix applying filters from histogram

* @Dosant code review

* Merge changes from #56643 to handle saved query errors

Fix bug where saved query clean does not reset query

* change string path

* if

* Extract useFilterManager and useTimefilter

* Split useSavedQuery and restore capability of changing saved query in URL

* Added some tests

* context view

* Remove useMemo

* spaces

* Support filter intial values
Improve useSavedQuery hook in terface

Co-authored-by: Elastic Machine <[email protected]>
  • Loading branch information
Liza Katz and elasticmachine authored Feb 6, 2020
1 parent ed2bac3 commit 1fa01a7
Show file tree
Hide file tree
Showing 17 changed files with 657 additions and 248 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
app-name="'context'"
show-search-bar="true"
show-filter-bar="true"
show-query-bar="false"
show-save-query="false"
show-date-picker="false"

filters="contextApp.state.queryParameters.filters"
on-filters-updated="contextApp.actions.updateFilters"
index-patterns="[contextApp.indexPattern]"
use-default-behaviors="true"
>
</kbn-top-nav>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/

import _ from 'lodash';
import { getServices, callAfterBindingsWorkaround, getAngularModule } from '../../kibana_services';
import { callAfterBindingsWorkaround, getAngularModule } from '../../kibana_services';
import contextAppTemplate from './context_app.html';
import './context/components/action_bar';
import { getFirstSortableField } from './context/api/utils/sorting';
Expand All @@ -34,8 +34,6 @@ import {
QueryActionsProvider,
} from './context/query';

const { timefilter } = getServices();

const module = getAngularModule();

module.directive('contextApp', function ContextApp() {
Expand All @@ -61,10 +59,6 @@ module.directive('contextApp', function ContextApp() {
function ContextAppController($scope, config, Private) {
const queryParameterActions = getQueryParameterActions();
const queryActions = Private(QueryActionsProvider);

timefilter.disableAutoRefreshSelector();
timefilter.disableTimeRangeSelector();

this.state = createInitialState(
parseInt(config.get('context:step'), 10),
getFirstSortableField(this.indexPattern, config.get('context:tieBreakerFields')),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,19 @@ <h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
<kbn-top-nav
app-name="'discover'"
config="topNavMenu"
screen-title="screenTitle"
show-search-bar="true"
show-date-picker="enableTimeRangeSelector"
show-save-query="showSaveQuery"
query="state.query"
saved-query="savedQuery"
screen-title="screenTitle"
on-query-submit="updateQueryAndFetch"
index-patterns="[indexPattern]"
filters="filters"
on-filters-updated="onFiltersUpdated"
date-range-from="time.from"
date-range-to="time.to"
is-refresh-paused="refreshInterval.pause"
refresh-interval="refreshInterval.value"
on-refresh-change="onRefreshChange"
on-saved="onQuerySaved"
on-saved-query-updated="onSavedQueryUpdated"
on-clear-saved-query="onClearSavedQuery"

query="state.query"
on-query-submit="updateQuery"

show-save-query="showSaveQuery"
saved-query-id="state.savedQuery"
on-saved-query-id-change="updateSavedQueryId"

use-default-behaviors="true"
>
</kbn-top-nav>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

import _ from 'lodash';
import React from 'react';
import { Subscription } from 'rxjs';
import { Subscription, Subject, merge } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import moment from 'moment';
import dateMath from '@elastic/datemath';
import { i18n } from '@kbn/i18n';
Expand Down Expand Up @@ -62,7 +63,6 @@ import { Vis } from '../../../../../visualizations/public';
const {
core,
chrome,
data,
docTitle,
filterManager,
share,
Expand All @@ -79,8 +79,6 @@ import {
import { getIndexPatternId } from '../helpers/get_index_pattern_id';
import { FilterStateManager } from '../../../../../data/public';

const { getSavedQuery } = data.query.savedQueries;

const fetchStatuses = {
UNINITIALIZED: 'uninitialized',
LOADING: 'loading',
Expand Down Expand Up @@ -205,8 +203,6 @@ function discoverController(

const subscriptions = new Subscription();

timefilter.disableTimeRangeSelector();
timefilter.disableAutoRefreshSelector();
$scope.timefilterUpdateHandler = ranges => {
timefilter.setTime({
from: moment(ranges.from).toISOString(),
Expand All @@ -218,7 +214,6 @@ function discoverController(
$scope.showInterval = false;
$scope.minimumVisibleRows = 50;
$scope.fetchStatus = fetchStatuses.UNINITIALIZED;
$scope.refreshInterval = timefilter.getRefreshInterval();
$scope.showSaveQuery = uiCapabilities.discover.saveQuery;

$scope.$watch(
Expand Down Expand Up @@ -436,15 +431,10 @@ function discoverController(
let stateMonitor;

const $state = ($scope.state = new AppState(getStateDefaults()));
const $fetchObservable = new Subject();

$scope.filters = filterManager.getFilters();
$scope.screenTitle = savedSearch.title;

$scope.onFiltersUpdated = filters => {
// The filters will automatically be set when the filterManager emits an update event (see below)
filterManager.setFilters(filters);
};

const getFieldCounts = async () => {
// the field counts aren't set until we have the data back,
// so we wait for the fetch to be done before proceeding
Expand Down Expand Up @@ -571,17 +561,12 @@ function discoverController(
};

const shouldSearchOnPageLoad = () => {
// If a saved query is referenced in the app state, omit the initial load because the saved query will
// be fetched separately and trigger a reload
if ($scope.state.savedQuery) {
return false;
}
// A saved search is created on every page load, so we check the ID to see if we're loading a
// previously saved search or if it is just transient
return (
config.get('discover:searchOnPageLoad') ||
savedSearch.id !== undefined ||
_.get($scope, 'refreshInterval.pause') === false
timefilter.getRefreshInterval().pause === false
);
};

Expand All @@ -593,25 +578,23 @@ function discoverController(
$scope.$on('$destroy', () => stateMonitor.destroy());

$scope.updateDataSource().then(function() {
subscriptions.add(
subscribeWithScope($scope, timefilter.getAutoRefreshFetch$(), {
next: $scope.fetch,
})
);
const searchBarChanges = merge(
timefilter.getAutoRefreshFetch$(),
timefilter.getFetch$(),
filterManager.getFetches$(),
$fetchObservable
).pipe(debounceTime(100));

subscriptions.add(
subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), {
next: $scope.updateRefreshInterval,
subscribeWithScope($scope, searchBarChanges, {
next: $scope.fetch,
})
);
subscriptions.add(
subscribeWithScope($scope, timefilter.getTimeUpdate$(), {
next: $scope.updateTime,
})
);
subscriptions.add(
subscribeWithScope($scope, timefilter.getFetch$(), {
next: $scope.fetch,
next: () => {
$scope.updateTime();
},
})
);

Expand All @@ -622,31 +605,24 @@ function discoverController(
const currentSort = getSortArray($scope.searchSource.getField('sort'), $scope.indexPattern);

// if the searchSource doesn't know, tell it so
if (!angular.equals(sort, currentSort)) $scope.fetch();
if (!angular.equals(sort, currentSort)) $fetchObservable.next();
});

// update data source when filters update

subscriptions.add(
subscribeWithScope($scope, filterManager.getUpdates$(), {
next: () => {
$scope.filters = filterManager.getFilters();
$scope.updateDataSource().then(function() {
$state.save();
});
},
})
);

// fetch data when filters fire fetch event
subscriptions.add(
subscribeWithScope($scope, filterManager.getFetches$(), {
next: $scope.fetch,
})
);

// update data source when hitting forward/back and the query changes
$scope.$listen($state, 'fetch_with_changes', function(diff) {
if (diff.indexOf('query') >= 0) $scope.fetch();
if (diff.indexOf('query') >= 0) $fetchObservable.next();
});

$scope.$watch('opts.timefield', function(timefield) {
Expand All @@ -655,7 +631,7 @@ function discoverController(

$scope.$watch('state.interval', function(newInterval, oldInterval) {
if (newInterval !== oldInterval) {
$scope.fetch();
$fetchObservable.next();
}
});

Expand All @@ -674,7 +650,7 @@ function discoverController(
if (!_.isEqual(newQuery, oldQuery)) {
const query = migrateLegacyQuery(newQuery);
if (!_.isEqual(query, newQuery)) {
$scope.updateQueryAndFetch({ query });
$scope.updateQuery({ query });
}
}
});
Expand Down Expand Up @@ -734,7 +710,7 @@ function discoverController(
$state.replace();

if (shouldSearchOnPageLoad()) {
$scope.fetch();
$fetchObservable.next();
}
});
});
Expand Down Expand Up @@ -827,15 +803,9 @@ function discoverController(
});
};

$scope.updateQueryAndFetch = function({ query, dateRange }) {
const oldDateRange = timefilter.getTime();
timefilter.setTime(dateRange);
$scope.updateQuery = function({ query }) {
$state.query = query;
// storing the updated timerange in the state will trigger a fetch
// call automatically, so only trigger fetch in case this is a refresh call (no changes in parameters).
if (_.isEqual(oldDateRange, dateRange)) {
$scope.fetch();
}
$fetchObservable.next();
};

function onResults(resp) {
Expand Down Expand Up @@ -896,32 +866,12 @@ function discoverController(
from: dateMath.parse(timefilter.getTime().from),
to: dateMath.parse(timefilter.getTime().to, { roundUp: true }),
};
$scope.time = timefilter.getTime();
};

$scope.toMoment = function(datetime) {
return moment(datetime).format(config.get('dateFormat'));
};

$scope.updateRefreshInterval = function() {
const newInterval = timefilter.getRefreshInterval();
const shouldFetch =
_.get($scope, 'refreshInterval.pause') === true && newInterval.pause === false;

$scope.refreshInterval = newInterval;

if (shouldFetch) {
$scope.fetch();
}
};

$scope.onRefreshChange = function({ isPaused, refreshInterval }) {
timefilter.setRefreshInterval({
pause: isPaused,
value: refreshInterval ? refreshInterval : $scope.refreshInterval.value,
});
};

$scope.resetQuery = function() {
kbnUrl.change('/discover/{{id}}', { id: $route.current.params.id });
};
Expand Down Expand Up @@ -988,69 +938,14 @@ function discoverController(
$scope.minimumVisibleRows = $scope.hits;
};

$scope.onQuerySaved = savedQuery => {
$scope.savedQuery = savedQuery;
};

$scope.onSavedQueryUpdated = savedQuery => {
$scope.savedQuery = { ...savedQuery };
};

$scope.onClearSavedQuery = () => {
delete $scope.savedQuery;
delete $state.savedQuery;
$state.query = {
query: '',
language: localStorage.get('kibana.userQueryLanguage') || config.get('search:queryLanguage'),
};
filterManager.setFilters(filterManager.getGlobalFilters());
$state.save();
$scope.fetch();
};

const updateStateFromSavedQuery = savedQuery => {
$state.query = savedQuery.attributes.query;
$state.save();
const savedQueryFilters = savedQuery.attributes.filters || [];
const globalFilters = filterManager.getGlobalFilters();
filterManager.setFilters([...globalFilters, ...savedQueryFilters]);

if (savedQuery.attributes.timefilter) {
timefilter.setTime({
from: savedQuery.attributes.timefilter.from,
to: savedQuery.attributes.timefilter.to,
});
if (savedQuery.attributes.timefilter.refreshInterval) {
timefilter.setRefreshInterval(savedQuery.attributes.timefilter.refreshInterval);
}
$scope.updateSavedQueryId = newSavedQueryId => {
if (newSavedQueryId) {
$state.savedQuery = newSavedQueryId;
} else {
delete $state.savedQuery;
}

$scope.fetch();
};

$scope.$watch('savedQuery', newSavedQuery => {
if (!newSavedQuery) return;

$state.savedQuery = newSavedQuery.id;
$state.save();

updateStateFromSavedQuery(newSavedQuery);
});

$scope.$watch('state.savedQuery', newSavedQueryId => {
if (!newSavedQueryId) {
$scope.savedQuery = undefined;
return;
}
if (!$scope.savedQuery || newSavedQueryId !== $scope.savedQuery.id) {
getSavedQuery(newSavedQueryId).then(savedQuery => {
$scope.$evalAsync(() => {
$scope.savedQuery = savedQuery;
updateStateFromSavedQuery(savedQuery);
});
});
}
});
};

async function setupVisualization() {
// If no timefield has been specified we don't create a histogram of messages
Expand Down
Loading

0 comments on commit 1fa01a7

Please sign in to comment.