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

[Explore] Adding Adhoc Filters #4909

Merged
merged 4 commits into from
May 10, 2018

Conversation

gabe-lyons
Copy link
Contributor

Here I'm following a similar design pattern as Adhoc Metrics (#4663 and #4736). Adhoc Filters introduces a new control that unifies the 4 previous filter controls (free text where, free text having, structured where filter, structured having filter). It allows you to select a column or any saved metric / adhoc metric currently being used in the metric field to filter by:

image

and then once a metric or column is selected lets you switch between writing a SQL filter or simple filter for that metric/column:

image

(for druid datsources, metric and column filters can only be edited in the simple view. for table datsources, column filters can be edited in either but metrics filters can only be edited in the sql view)

This PR also removes the legacy filter controls. When loading a chart that has legacy filters already attached to it, they will be cast to adhoc filters:
adhocfilters_castslegacyfilters

Here are some more demos of the feature in action,

creating and editing filters:
adhocfilters_creatingandediting

transitioning between simple tab and sql tab:
adhocfilters_simpletosql

selecting adhoc metrics to filter on:
adhocfilters_coversadhocmetrics

notes to reviewers:
The meat of the frontend lies in the three AdhocFilterEditPopover* files and the AdhocFilterControl file. The meat of the backend lies in viz.py where I break adhoc filters out into the four base types of filters for processing.

reviewers:
@michellethomas @williaster @graceguo-supercat @john-bodley @mistercrunch

@gabe-lyons gabe-lyons force-pushed the gabe_adding_adhoc_filters branch 3 times, most recently from fac4cf6 to c85c7f1 Compare April 30, 2018 19:34
@codecov-io
Copy link

codecov-io commented Apr 30, 2018

Codecov Report

Merging #4909 into master will increase coverage by 0.07%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4909      +/-   ##
==========================================
+ Coverage   77.07%   77.15%   +0.07%     
==========================================
  Files          44       44              
  Lines        8558     8587      +29     
==========================================
+ Hits         6596     6625      +29     
  Misses       1962     1962
Impacted Files Coverage Δ
superset/connectors/druid/models.py 81.11% <100%> (-0.06%) ⬇️
superset/viz.py 80.29% <100%> (+0.39%) ⬆️
superset/utils.py 87.9% <100%> (+0.05%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f21ba1a...e3ec913. Read the comment docs.

@gabe-lyons
Copy link
Contributor Author

Made a couple of aesthetic changes based on feedback from the team

Pills now appear darker and more clickable:
image

the text area is also wrapped in a thin border (and by wraps overflow text to new lines rather than hiding it):

image

I also introduced some ways to give feedback that simple filters cannot filter on metrics in table datasources:
image

image

Copy link
Contributor

@williaster williaster left a comment

Choose a reason for hiding this comment

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

this looks good to me overall! 💯 a couple small comments :)

comparator: '10',
clause: CLAUSES.WHERE,
});
expect(adhocFilter.filterOptionName.length).to.be.above(10);
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 because value > 10 has a length of 10? you could maybe make it more readable/obvious by doing 'value > 10'.length

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah the optionNames are just randomized ids in this case. we use them to differentiate options when updating (sort of like a react key). so I can't predict what its value is.


duplicateWith(nextFields) {
return new AdhocFilter({
...this,
Copy link
Contributor

Choose a reason for hiding this comment

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

not a blocker, but I think in general it's better for readability and bugginess to explicitly define which properties are copied (for ...this, I think ...nextFields is fine)

function translateToSql(adhocMetric, { useSimple } = {}) {
if (adhocMetric.expressionType === EXPRESSION_TYPES.SIMPLE || useSimple) {
const isMulti = MULTI_OPERATORS.indexOf(adhocMetric.operator) >= 0;
return `${adhocMetric.subject} ${OPERATORS_TO_SQL[adhocMetric.operator]} ${isMulti ? '(\'' : ''}${isMulti ? adhocMetric.comparator.join("','") : adhocMetric.comparator}${isMulti ? '\')' : ''}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

you could break this up into different pieces to make it more readable.

onChange: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onResize: PropTypes.func.isRequired,
options: PropTypes.arrayOf(PropTypes.oneOfType([
Copy link
Contributor

Choose a reason for hiding this comment

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

yay for well defined shapes! 🎉

width: startingWidth,
height: startingHeight,
};
document.addEventListener('mouseup', this.onMouseUp);
Copy link
Contributor

Choose a reason for hiding this comment

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

I've typically seen this in the componentDidMount lifecycle event but am not sure if it matters too much.

)),
value: adhocFilter.operator,
onChange: this.onOperatorChange,
optionRenderer: VirtualizedRendererWrap((
Copy link
Contributor

Choose a reason for hiding this comment

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

so are we just doing readable options for equals? any reason not to make them all human readable?

Copy link
Contributor

Choose a reason for hiding this comment

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

(to clarify, I think readable labels are good for the options dropdown, I think the mathematical representation >, != etc is still probably better for the label since it needs to be concise.) not set on this though, I would imagine most people would understand either. an alternative I think we discussed would be to include both representations in the dropdown eg != (not equal to)

onExited={this.onOverlayExited}
>
<Label className="adhoc-filter-option">
<div onMouseDownCapture={(e) => { e.stopPropagation(); }}>
Copy link
Contributor

Choose a reason for hiding this comment

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

could you define this as a class function so it's not initialized every render?

val: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
});

const propTypes = {
Copy link
Contributor

Choose a reason for hiding this comment

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

typically you should define default props for anything not required because it's less likely you'll throw an error.

@@ -43,6 +44,7 @@ const defaultProps = {
optionRenderer: opt => opt.label,
valueRenderer: opt => opt.label,
valueKey: 'value',
noResultsText: 'No results found',
Copy link
Contributor

Choose a reason for hiding this comment

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

should this be a t()?

}

.adhoc-filter-edit-tabs > .nav-tabs > li > a {
padding: 4px 4px 4px 4px;
Copy link
Contributor

Choose a reason for hiding this comment

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

you might know this but padding: 4px also works

@@ -0,0 +1,5 @@
import visTypes from './explore/visTypes';
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you mean to commit this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh geez

superset/viz.py Outdated
'op': adhoc_filter.get('operator'),
'val': adhoc_filter.get('comparator'),
})
if expression_type == 'SQL':
Copy link
Member

Choose a reason for hiding this comment

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

Nit. s/if/elif.

superset/viz.py Outdated
'op': adhoc_filter.get('operator'),
'val': adhoc_filter.get('comparator'),
})
if clause == 'HAVING':
Copy link
Member

Choose a reason for hiding this comment

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

Nit. s/if/elif.

superset/viz.py Outdated
if expression_type == 'SQL':
if clause == 'WHERE':
sql_where_filters.append(adhoc_filter.get('sqlExpression'))
if clause == 'HAVING':
Copy link
Member

Choose a reason for hiding this comment

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

Nit. s/if/elif.

superset/viz.py Outdated
sql_having_filters.append(adhoc_filter.get('sqlExpression'))
all_where_text = ''
all_having_text = ''
if len(sql_where_filters) == 1:
Copy link
Member

Choose a reason for hiding this comment

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

Why not simply,

all_where_text = ' AND '.join(['({})'.format(sql) for sql in sql_where_filters)])

Note list comprehensions are preferred over using map, filter etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah nice thats so much shorter 🎉

superset/viz.py Outdated
all_where_text = ' AND '.join(list(map(
lambda expression: '({})'.format(expression), sql_where_filters,
)))
if len(sql_having_filters) == 1:
Copy link
Member

Choose a reason for hiding this comment

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

See ^^.

@gabe-lyons
Copy link
Contributor Author

@williaster @michellethomas @john-bodley comments addressed, PTAL a when you have the chance!

@gabe-lyons gabe-lyons force-pushed the gabe_adding_adhoc_filters branch 4 times, most recently from 88e31b7 to e3ec913 Compare May 8, 2018 01:13
extras = {
'where': ' AND '.join(['({})'.format(sql) for sql in sql_where_filters]),
'having': ' AND '.join(
['({})'.format(sql) for sql in sql_having_filters],
Copy link
Member

Choose a reason for hiding this comment

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

Does this go over the 90 character limit without wrapping?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

by one character 😢

@graceguo-supercat graceguo-supercat merged commit a8514b2 into apache:master May 10, 2018
@graceguo-supercat
Copy link

🔥 🎆 🍾 🎊

michellethomas pushed a commit to michellethomas/panoramix that referenced this pull request May 24, 2018
* adding custom expressions to adhoc metrics

* adjusted transitions and made the box expandable

* adding adhoc filters

* adjusted based on feedback
timifasubaa pushed a commit to timifasubaa/incubator-superset that referenced this pull request May 31, 2018
* adding custom expressions to adhoc metrics

* adjusted transitions and made the box expandable

* adding adhoc filters

* adjusted based on feedback
@jasnovak
Copy link
Contributor

jasnovak commented Jun 13, 2018

@GabeLoins @john-bodley This is causing a bug with filters not being saved when visualizations are changed. specifically when switching to Table View: #5172

wenchma pushed a commit to wenchma/incubator-superset that referenced this pull request Nov 16, 2018
* adding custom expressions to adhoc metrics

* adjusted transitions and made the box expandable

* adding adhoc filters

* adjusted based on feedback
@mistercrunch mistercrunch added 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 0.26.0 labels Feb 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 0.26.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants