-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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
[ML] Add query bar to Anomaly Explorer for filtering of anomaly results for one or more influencers #31763
[ML] Add query bar to Anomaly Explorer for filtering of anomaly results for one or more influencers #31763
Conversation
Pinging @elastic/ml-ui |
💔 Build Failed |
💔 Build Failed |
ba2f052
to
e882a70
Compare
💔 Build Failed |
💔 Build Failed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this is great! Did a first round of review and added a few questions.
...ml/public/explorer/components/explorer_no_influencers_found/explorer_no_influencers_found.js
Outdated
Show resolved
Hide resolved
.html(label => { | ||
const showFilterContext = filterActive === true && label === 'Overall'; | ||
if (showFilterContext) { | ||
return `${label} (unfiltered)`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still need to use mlEscape
here? Like
return `${mlEscape(label)} (unfiltered)`;
Additionally, "(unfiltered)" needs to become a translated message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, i think so
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conversation was marked as resolved, but the suggested change hasn't been made.
is there a reason why we don't need to use mlEscape
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake - fixed with: a861b7d
const wrapper = shallow(<ClickOutside onClickOutside={() => {}} />); | ||
expect(wrapper).toMatchSnapshot(); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit suggestion: Would be great to have a test here that tests the actual click event.
💚 Build Succeeded |
💚 Build Succeeded |
@@ -666,14 +715,21 @@ export const Explorer = injectI18n(injectObservablesAsProps( | |||
{ viewByLoadedForTimeFormatted: formatHumanReadableDateTime(timerange.earliestMs) } | |||
); | |||
} else { | |||
let tempInfluencersFilterQuery = influencersFilterQuery; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, this could be const tempInfluencersFilterQuery = (maskAll === true) ? undefined : influencersFilterQuery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in: a861b7d
if (influencersFilterQuery.match_all && Object.keys(influencersFilterQuery.match_all).length === 0) { | ||
this.props.appStateHandler(APP_STATE_ACTION.CLEAR_INFLUENCER_FILTER_SETTINGS); | ||
const stateUpdate = { | ||
...{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this spread operator isn't necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Fixed here: a861b7d
.html(label => { | ||
const showFilterContext = filterActive === true && label === 'Overall'; | ||
if (showFilterContext) { | ||
return `${label} (unfiltered)`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conversation was marked as resolved, but the suggested change hasn't been made.
is there a reason why we don't need to use mlEscape
here?
return new Promise((resolve) => { | ||
const influencers = []; | ||
if (noInfluencersConfigured !== true) { | ||
ml.getMultipleJobs( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mlJobService
already has all the jobs loaded from the server so doesn't need to request them again.
the function selectedJobsHaveInfluencers
(in this file) uses this list and so a new function called getInfluencers
could be added here which is very similar to selectedJobsHaveInfluencers
.
This would remove the need for getMultipleJobs
in the ml
lib.
Also, noInfluencersConfigured
is already based on the results of selectedJobsHaveInfluencers
and so isn't needed at all here or in getIndexPattern
IMO, an even better change would be to remove selectedJobsHaveInfluencers
entirely from this file and add getInfluencers
to mlJobService
.
the places where selectedJobsHaveInfluencers
is set could then be based on mlJobService.getInfluencers(selectedJobs).length > 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really great thoughts here, @jgowdyelastic. I think removing getMultipleJobs
is the right call here since I can essentially access the already loaded jobs in this file via the mlJobService
as you mentioned.
I've gone ahead and created a getInfluencers
function in this file (explorer_utils.js
) that is similar to selectedJobsHaveInfluencers
but returns an array of influencer name strings. This way I can use it similarly to how you mentioned - by grabbing all the influencers I need in getIndexPattern
and also using noInfluencersConfigured: (getInfluencers(selectedJobs).length === 0)
in lieu of selectedJobsHaveInfluencers
Since that's the only context selectedJobsHaveInfluencers
is being used (and only in explorer.js
) I've removed that function since it's no longer needed. This solution also means I can remove loadInfluencerFields
from explorer_utils
. Let me know what you think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in: a861b7d
this.setState({ suggestions, isLoadingSuggestions: false }); | ||
} catch (e) { | ||
console.error('Error while fetching suggestions', e); | ||
this.setState({ isLoadingSuggestions: false, error: (e.message ? e.message : 'Error while fetching suggestions') }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should the errors displayed in the callout be translated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call - updated with localized default error messages. 👍
import { uniqueId } from 'lodash'; | ||
import { FilterBar } from './filter_bar'; | ||
import { EuiCallOut } from '@elastic/eui'; | ||
import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; // luceneStringToDsl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accidentally left in comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep - missed it! Removed in: a861b7d
try { | ||
const ast = fromKueryExpression(inputValue); | ||
const isAndOperator = (ast.function === 'and'); | ||
const query = convertKueryToEsQuery(inputValue, indexPattern); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
kibana's own buildEsQuery
function could be used here i believe:
const q = {
query: inputValue,
language: 'kuery',
};
const query = buildEsQuery(indexPattern, [q]);
also, indexPattern
is undefined
here as it should come from props
not state
, this made me realise that it's not needed at all and so null
or undefined
could be passed to convertKueryToEsQuery
or buildEsQuery
(if you decide to use it).
e.g.
const query = buildEsQuery(null, [q]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've decided to keep as is since this conversion function as been used in APM as well so I think it's best to keep things consistent.
} | ||
|
||
const suggestions = this.props.suggestions.map((suggestion, index) => { | ||
const key = suggestion + '_' + index; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, a template literal could be used here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! 😄
} | ||
|
||
render() { | ||
if (!this.props.show || isEmpty(this.props.suggestions)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, this.props.suggestions.length === 0
could be used instead here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call - updated 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx for the SASS change
@@ -27,6 +27,12 @@ export const ml = { | |||
}); | |||
}, | |||
|
|||
getMultipleJobs(jobIds) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this new function might not be needed if https://github.com/elastic/kibana/pull/31763/files#r260769619 can be implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in a861b7d
💚 Build Succeeded |
x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js
Outdated
Show resolved
Hide resolved
💚 Build Succeeded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two quick thoughts:
- the kibana query language is now being referred to as 'KQL' through the UI, so I wonder if the folder and filenames should be renamed at this stage from e.g.
kuery_filter_bar
tokql_filter_bar
. - is the query bar component generic enough to be used outside the Anomaly Explorer view? If so, it might be better to move it to the
x-pack/plugins/ml/public/components
folder. In the future, it would be great for example to use this in the Data Visualizer view.
2e091d6
to
a83bfc8
Compare
💚 Build Succeeded |
💚 Build Succeeded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested latest changes and LGTM. Happy for the additional work to go into follow-up PRs.
…ts for one or more influencers (elastic#31763) * wip: add autocomplete kueryFilterBar.update endpoints with query. * Create indexPattern on job change. Value is autocompleted * Mask unrelated swimlanes on filter apply * Only show relevant viewBy swimlane * reload viewBy swimlane when view By change is relevant to filter * handle various query types when building filteredFields list * remove deprecated code from old filter bar * Show error callout when invalid filter syntax * remove dependency on deprecated import var * remove all left over apm dependencies * persist filter on refresh * Create initial filter bar placeholder dynamically * add description text when filter active * switch to relevant viewBy fieldname on filter * recalculate placeholder on job change * Create tests for all components * View by dropdown only contains relevant fields for filter * fix localization message noInfluencers component * Move indexPattern to state and viewBy option filtering to utils * remove unnecessary setState when setting viewBy for filter * Use preloaded jobs from mlJobService to get influencers * Only filter viewByOptions if valid field * use kql and move to components dir * move filter bar to public/components/
…ts for one or more influencers (#31763) (#32518) * wip: add autocomplete kueryFilterBar.update endpoints with query. * Create indexPattern on job change. Value is autocompleted * Mask unrelated swimlanes on filter apply * Only show relevant viewBy swimlane * reload viewBy swimlane when view By change is relevant to filter * handle various query types when building filteredFields list * remove deprecated code from old filter bar * Show error callout when invalid filter syntax * remove dependency on deprecated import var * remove all left over apm dependencies * persist filter on refresh * Create initial filter bar placeholder dynamically * add description text when filter active * switch to relevant viewBy fieldname on filter * recalculate placeholder on job change * Create tests for all components * View by dropdown only contains relevant fields for filter * fix localization message noInfluencers component * Move indexPattern to state and viewBy option filtering to utils * remove unnecessary setState when setting viewBy for filter * Use preloaded jobs from mlJobService to get influencers * Only filter viewByOptions if valid field * use kql and move to components dir * move filter bar to public/components/
LGTM! |
LGTM |
Summary
Related issue: #18262
Adds a query bar component to the Anomaly Explorer which allows filtering of anomaly results for one or more influencers. The query bar autocomplete functionality suggests influencer field names and values based on the selected job's influencers.
Follow up PR:
Checklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.- [ ] Documentation was added for features that require explanation or tutorialsFor maintainers
- [ ] This was checked for breaking API changes and was labeled appropriately- [ ] This includes a feature addition or change that requires a release note and was labeled appropriately