Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

explicitly passing filters and query to visualize #19172

Merged
merged 25 commits into from
Jun 13, 2018

Conversation

ppisljar
Copy link
Member

@ppisljar ppisljar commented May 17, 2018

filters and query are explicitly passed down to dashboard embeddables (visualize and saved search)

  • rootSearchSource is removed, timeFilter needs to be passed explicitly in to each embeddable or set on each searchSource
  • appSearchSource is removed, query and filters need to be passed explicitly to each embeddable
  • visualize no longer needs to watch appState for changes
  • visualization searchSource no longer inherits from rootSearchSource (however it still uses inheritance for saved searches or filters/query saved with visualization.

resolves #16641, resolves #16595

@ppisljar ppisljar added WIP Work in progress Feature:Visualizations Generic visualization features (in case no more specific feature label is available) labels May 17, 2018
@ppisljar ppisljar force-pushed the fix/explicitPassing branch 3 times, most recently from 863c153 to c5473b9 Compare May 21, 2018 15:44
@elastic elastic deleted a comment from elasticmachine May 21, 2018
@elastic elastic deleted a comment from elasticmachine May 21, 2018
@elastic elastic deleted a comment from elasticmachine May 21, 2018
@elastic elastic deleted a comment from elasticmachine May 21, 2018
@ppisljar ppisljar force-pushed the fix/explicitPassing branch 2 times, most recently from a0e0dec to 90e4013 Compare May 21, 2018 17:17
@@ -177,6 +181,14 @@ export class DashboardStateManager {
if (getDescription(state) !== this.getDescription()) {
store.dispatch(updateDescription(this.getDescription()));
}

if (getFilters(state) !== this.getFilterState().filterBars) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always return not equal:

screen shot 2018-05-21 at 1 21 00 pm

screen shot 2018-05-21 at 1 21 33 pm

See getFilterBarChanged function in this class for a better way to compare them. (Unfortunately I'm not sure there are any tests to make sure this isn't overly triggered...)

this.searchScope.columns = this.customization.columns || this.savedSearch.columns;
this.searchScope.sort = this.customization.sort || this.savedSearch.sort;
this.searchScope.sharedItemTitle = this.panelTitle;

this.searchScope.searchSource.getParent().set('filter', this.filters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are losing the time range filter. Unfortunately the passed down time range is not being picked up by saved searches yet. Was something I was planning to get to at some point... looks like you are going to beat me to it though!

screen shot 2018-05-21 at 1 26 51 pm

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why setting it on the parent and not this.searchScope.searchSource? Could you clarify a bit what search source is which in this case?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we created a searchsource 'stack' (check below) where timeRangeSearchSource is the root and filterSearchSouce inherits from it and our saved searchSource inherits from the filterSearchSource

  • savedSearchSource will have filters and query saved with the search
  • filterSearchSource will get dashboard filters and query
  • timeRangeSearchSource will get the time range filter

we do this as we don't want to handle merging of filters and queries (if we would have only one level) and we let the searchsource handle it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't we change the inheritance order of the search sources to be: timeRangeSearchSourcesavedSearchSourcefilterSearchSource (i.e. put the saved search source in the middle).

That way we could safely use this.searchScope.searchSource instead of needing this kind of implementation detail based call to getParent()? Also for the actual request, the inheritance order of those three shouldn't matter?

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💔 Build Failed

Copy link
Contributor

@timroes timroes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the way itself look good to go, though we should carefully also test every of this scenarios. I would also be interested in @spalger overall thoughts about that, and we should at least when we are done with it (maybe already before for feedback) get a review by @stacey-gammon and @Bargs

@@ -182,6 +182,17 @@ function discoverController(
.highlightAll(true)
.version(true);

// searchSource which applies time range
const timeRangeSearchSource = savedSearch.searchSource.clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually want to clone here, and not just creating a new instance? I've got the feeling except the filter (and maybe the index) we don't want to have anything from that savedSearch.searchSource? Wouldn't it be easier to create a new one, and copy this two things over rather than cloning and resetting some things (like query) below, but might forget to reset some important things?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the things you wouldn't want ? when i was thinking about it it was either:

  • create a new one and make sure the parameters are set correctly (index ... ? not sure if anything else is needed ?)
  • clone, and make sure the parameters are set correctly <-- sounded safer, as i don't need to worry about all parameters + i don't need to create a new SearchSource which means i don't need access to Private

Copy link
Contributor

@spalger spalger May 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @timroes and think we should be explicit about what we want to copy over, rather than cloning and unsetting things.

How do you feel about these options?

// create a new searchSource, without needing Private
const timeRangeSearchSource = new savedSearch.searchSource.constructor()

// or a little less nasty, add a `.new()` method
const timeRangeSearchSource = savedSearch.searchSource.new()

// or add a `.reset()` method so we don't have to know what to unset
const timeRangeSearchSource = savedSearch.searchSource.clone().reset()

this.searchScope.columns = this.customization.columns || this.savedSearch.columns;
this.searchScope.sort = this.customization.sort || this.savedSearch.sort;
this.searchScope.sharedItemTitle = this.panelTitle;

this.searchScope.searchSource.getParent().set('filter', this.filters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why setting it on the parent and not this.searchScope.searchSource? Could you clarify a bit what search source is which in this case?

this.searchScope.description = this.savedSearch.description;
this.searchScope.searchSource = this.savedSearch.searchSource;

const timeRangeSearchSource = this.searchScope.searchSource.clone();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question as above: cloning vs creating new one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same answer as above :)

});

const filtersSearchSource = this.searchScope.searchSource.clone();
filtersSearchSource.inherits(timeRangeSearchSource);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should still get this.filters and this.query in it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this one will receive filters and query

@@ -220,6 +220,7 @@ function VisEditor($scope, $route, timefilter, AppState, $window, kbnUrl, courie

const updateTimeRange = () => {
$scope.timeRange = timefilter.time;
$scope.vis.forceReload();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed we should instead listen in visualize on the changes of filter/queries/timerange

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the $watch is there now .... however removing forceReload doesn't work (watch is never triggered) .... i didn't dig into what exactly is preventing it to work that way .... any ideas ?

import _ from 'lodash';
import dateMath from '@kbn/datemath';

export function calculateBounds(timeRange, forceNow) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be more explicit here for better readability and make forceNow = false

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forceNow should not be false, but undefined or a date ?

Copy link
Contributor

@spalger spalger May 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally find unnamed boolean arguments hard to understand, what if we just allowed dateMathOptions:

export function calculateBounds(timeRange, dateMathOptions = {}) {
  return {
    min: dateMath.parse(timeRange.from, { ...dateMathOptions }),
    max: dateMath.parse(timeRange.to, { roundUp: true, ...dateMathOptions })
  };
}

// ... somewhere else ...

calculateBounds(timeRange, {
  forceNow: true
})
// vs
calculateBounds(timeRange, true)
``

// in editor mode we want to set filter and query on searchSource so they get
// stored with visualization, however on dashboard we set them on requestSearchSource
// so we don't override the stored ones
if (vis.editorMode) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay for now, but we should still figure out, if we can find a better way than that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the editor just shouldn't pass in the filters or query to <visualize>, instead the editor will set them on the vis searchSource and <visualize> will default to no extra filters or queries.

@@ -133,9 +135,8 @@ uiModules
if ($scope.editorMode) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this appState.vis in there? Shouldn't that rather be something that the visualize editor directive now takes care about (and rather expose a callback we'll trigger every time we start a fetch?

Also why do we in general need to save the app state when we do a fetch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to propagate changes to the URL ...

i would prefer if visualize wouldn't know about appState but would rather receive a callback which it should call on props updates, and then visualize app can figure out if it wants to add it to url or not.

however i would suggest in a separate PR ?

stateMonitor.destroy();
});
}
$scope.$watchGroup(['filters', 'query', 'timeRange'], fetch);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be $scope.fetch?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh .... i guess thats the reason watch is not triggering :D lol, thanks!

@elasticmachine
Copy link
Contributor

💔 Build Failed

@ppisljar ppisljar added review v6.4.0 and removed WIP Work in progress labels May 24, 2018
@ppisljar ppisljar changed the title WIP: explicitly passing filters and query to visualize explicitly passing filters and query to visualize May 24, 2018
@ppisljar ppisljar requested review from spalger and Bargs May 24, 2018 18:27
@elasticmachine
Copy link
Contributor

💔 Build Failed

@ppisljar ppisljar force-pushed the fix/explicitPassing branch from 024b516 to c8822cd Compare June 8, 2018 17:38
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@stacey-gammon
Copy link
Contributor

hmm, synced with the latest and also cannot repro that bug anymore @ppisljar.

Copy link
Contributor

@spalger spalger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm super happy to see this working with so few lines changed (by some standards). Couple nits/questions but I pulled the code and things seem to work great!

if (_.has(agg, 'type.postFlightRequest')) {
const nestedSearchSource = new SearchSource().inherits(requestSearchSource);
resp = await agg.type.postFlightRequest(resp, vis.aggs, agg, nestedSearchSource);
return new Promise(async (resolve, reject) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for async here

searchSource.set('filter', queryFilter.getFilters());
searchSource.set('query', appState.query);
}
requestSearchSource.set('filter', filters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this resolved? Best I can tell this is working.


export function getTime(indexPattern, timeRange, forceNow) {
if (!indexPattern) {
//in CI, we sometimes seem to fail here.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😬 Can you justify this a little better please? Not sure what this means by "sometimes seem to fail", but if you mean that some tests aren't properly stubbing indexPattern and that's causing issues I think we should update the indexPattern stubs, or stub out this method in tests, or something other than return undefined if an index patter isn't passed. This type of silent failing has a tendency to bite back in weird ways down the road.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea, just extracted this to its own file, i didn't write this code :)

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@ppisljar ppisljar mentioned this pull request Jun 13, 2018
@timroes timroes dismissed their stale review June 13, 2018 08:19

All changes implemented

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@ppisljar ppisljar merged commit 88bb012 into elastic:master Jun 13, 2018
ppisljar added a commit to ppisljar/kibana that referenced this pull request Jun 13, 2018
@ppisljar ppisljar deleted the fix/explicitPassing branch June 13, 2018 09:40
maryia-lapata pushed a commit to maryia-lapata/kibana that referenced this pull request Jun 25, 2018
@ppisljar ppisljar restored the fix/explicitPassing branch September 26, 2018 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:Visualizations Generic visualization features (in case no more specific feature label is available) review v6.4.0
Projects
None yet
5 participants