forked from apache/superset
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Explore] Adding Adhoc Filters (apache#4909)
* adding custom expressions to adhoc metrics * adjusted transitions and made the box expandable * adding adhoc filters * adjusted based on feedback
- Loading branch information
Gabe Lyons
authored and
Grace Guo
committed
May 10, 2018
1 parent
96f61ef
commit 425f8df
Showing
32 changed files
with
2,094 additions
and
21 deletions.
There are no files selected for viewing
136 changes: 136 additions & 0 deletions
136
superset/assets/spec/javascripts/explore/AdhocFilter_spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { expect } from 'chai'; | ||
import { describe, it } from 'mocha'; | ||
|
||
import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../src/explore/AdhocFilter'; | ||
|
||
describe('AdhocFilter', () => { | ||
it('sets filterOptionName in constructor', () => { | ||
const adhocFilter = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
expect(adhocFilter.filterOptionName.length).to.be.above(10); | ||
expect(adhocFilter).to.deep.equal({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
filterOptionName: adhocFilter.filterOptionName, | ||
sqlExpression: null, | ||
fromFormData: false, | ||
}); | ||
}); | ||
|
||
it('can create altered duplicates', () => { | ||
const adhocFilter1 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
const adhocFilter2 = adhocFilter1.duplicateWith({ operator: '<' }); | ||
|
||
expect(adhocFilter1.subject).to.equal(adhocFilter2.subject); | ||
expect(adhocFilter1.comparator).to.equal(adhocFilter2.comparator); | ||
expect(adhocFilter1.clause).to.equal(adhocFilter2.clause); | ||
expect(adhocFilter1.expressionType).to.equal(adhocFilter2.expressionType); | ||
|
||
expect(adhocFilter1.operator).to.equal('>'); | ||
expect(adhocFilter2.operator).to.equal('<'); | ||
}); | ||
|
||
it('can verify equality', () => { | ||
const adhocFilter1 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
const adhocFilter2 = adhocFilter1.duplicateWith({}); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter1.equals(adhocFilter2)).to.be.true; | ||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter1 === adhocFilter2).to.be.false; | ||
}); | ||
|
||
it('can verify inequality', () => { | ||
const adhocFilter1 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
const adhocFilter2 = adhocFilter1.duplicateWith({ operator: '<' }); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter1.equals(adhocFilter2)).to.be.false; | ||
|
||
const adhocFilter3 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
sqlExpression: 'value > 10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
const adhocFilter4 = adhocFilter3.duplicateWith({ sqlExpression: 'value = 5' }); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter3.equals(adhocFilter4)).to.be.false; | ||
}); | ||
|
||
it('can determine if it is valid', () => { | ||
const adhocFilter1 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter1.isValid()).to.be.true; | ||
|
||
const adhocFilter2 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: null, | ||
clause: CLAUSES.WHERE, | ||
}); | ||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter2.isValid()).to.be.false; | ||
|
||
const adhocFilter3 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
sqlExpression: 'some expression', | ||
clause: null, | ||
}); | ||
// eslint-disable-next-line no-unused-expressions | ||
expect(adhocFilter3.isValid()).to.be.false; | ||
}); | ||
|
||
it('can translate from simple expressions to sql expressions', () => { | ||
const adhocFilter1 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '==', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
expect(adhocFilter1.translateToSql()).to.equal('value = 10'); | ||
|
||
const adhocFilter2 = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'SUM(value)', | ||
operator: '!=', | ||
comparator: '5', | ||
clause: CLAUSES.HAVING, | ||
}); | ||
expect(adhocFilter2.translateToSql()).to.equal('SUM(value) <> 5'); | ||
}); | ||
}); |
189 changes: 189 additions & 0 deletions
189
superset/assets/spec/javascripts/explore/components/AdhocFilterControl_spec.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
/* eslint-disable no-unused-expressions */ | ||
import React from 'react'; | ||
import sinon from 'sinon'; | ||
import { expect } from 'chai'; | ||
import { describe, it } from 'mocha'; | ||
import { shallow } from 'enzyme'; | ||
|
||
import AdhocFilter, { EXPRESSION_TYPES, CLAUSES } from '../../../../src/explore/AdhocFilter'; | ||
import AdhocFilterControl from '../../../../src/explore/components/controls/AdhocFilterControl'; | ||
import AdhocMetric from '../../../../src/explore/AdhocMetric'; | ||
import { AGGREGATES, OPERATORS } from '../../../../src/explore/constants'; | ||
import OnPasteSelect from '../../../../src/components/OnPasteSelect'; | ||
|
||
const simpleAdhocFilter = new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.WHERE, | ||
}); | ||
|
||
const sumValueAdhocMetric = new AdhocMetric({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
column: { type: 'VARCHAR(255)', column_name: 'source' }, | ||
aggregate: AGGREGATES.SUM, | ||
}); | ||
|
||
const savedMetric = { metric_name: 'sum__value', expression: 'SUM(value)' }; | ||
|
||
const columns = [ | ||
{ type: 'VARCHAR(255)', column_name: 'source' }, | ||
{ type: 'VARCHAR(255)', column_name: 'target' }, | ||
{ type: 'DOUBLE', column_name: 'value' }, | ||
]; | ||
|
||
const legacyFilter = { col: 'value', op: '>', val: '5' }; | ||
const legacyHavingFilter = { col: 'SUM(value)', op: '>', val: '10' }; | ||
const whereFilterText = 'target in (\'alpha\')'; | ||
const havingFilterText = 'SUM(value) < 20'; | ||
|
||
const formData = { | ||
filters: [legacyFilter], | ||
having: havingFilterText, | ||
having_filters: [legacyHavingFilter], | ||
metric: undefined, | ||
metrics: [sumValueAdhocMetric, savedMetric.saved_metric_name], | ||
where: whereFilterText, | ||
}; | ||
|
||
function setup(overrides) { | ||
const onChange = sinon.spy(); | ||
const props = { | ||
onChange, | ||
value: [simpleAdhocFilter], | ||
datasource: { type: 'table' }, | ||
columns, | ||
savedMetrics: [savedMetric], | ||
formData, | ||
...overrides, | ||
}; | ||
const wrapper = shallow(<AdhocFilterControl {...props} />); | ||
return { wrapper, onChange }; | ||
} | ||
|
||
describe('AdhocFilterControl', () => { | ||
it('renders an onPasteSelect', () => { | ||
const { wrapper } = setup(); | ||
expect(wrapper.find(OnPasteSelect)).to.have.lengthOf(1); | ||
}); | ||
|
||
it('will translate legacy filters into adhoc filters if no adhoc filters are present', () => { | ||
const { wrapper } = setup({ value: undefined }); | ||
expect(wrapper.state('values')).to.have.lengthOf(4); | ||
expect(wrapper.state('values')[0].equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'value', | ||
operator: '>', | ||
comparator: '5', | ||
clause: CLAUSES.WHERE, | ||
}) | ||
))).to.be.true; | ||
expect(wrapper.state('values')[1].equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: 'SUM(value)', | ||
operator: '>', | ||
comparator: '10', | ||
clause: CLAUSES.HAVING, | ||
}) | ||
))).to.be.true; | ||
expect(wrapper.state('values')[2].equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
sqlExpression: 'target in (\'alpha\')', | ||
clause: CLAUSES.WHERE, | ||
}) | ||
))).to.be.true; | ||
expect(wrapper.state('values')[3].equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
sqlExpression: 'SUM(value) < 20', | ||
clause: CLAUSES.HAVING, | ||
}) | ||
))).to.be.true; | ||
}); | ||
|
||
it('will ignore legacy filters if adhoc filters are present', () => { | ||
const { wrapper } = setup(); | ||
expect(wrapper.state('values')).to.have.lengthOf(1); | ||
expect(wrapper.state('values')[0]).to.equal(simpleAdhocFilter); | ||
}); | ||
|
||
it('handles saved metrics being selected to filter on', () => { | ||
const { wrapper, onChange } = setup({ value: [] }); | ||
const select = wrapper.find(OnPasteSelect); | ||
select.simulate('change', [{ saved_metric_name: 'sum__value' }]); | ||
|
||
const adhocFilter = onChange.lastCall.args[0][0]; | ||
expect(adhocFilter instanceof AdhocFilter).to.be.true; | ||
expect(adhocFilter.equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
subject: savedMetric.expression, | ||
operator: OPERATORS['>'], | ||
comparator: 0, | ||
clause: CLAUSES.HAVING, | ||
}) | ||
))).to.be.true; | ||
}); | ||
|
||
it('handles adhoc metrics being selected to filter on', () => { | ||
const { wrapper, onChange } = setup({ value: [] }); | ||
const select = wrapper.find(OnPasteSelect); | ||
select.simulate('change', [sumValueAdhocMetric]); | ||
|
||
const adhocFilter = onChange.lastCall.args[0][0]; | ||
expect(adhocFilter instanceof AdhocFilter).to.be.true; | ||
expect(adhocFilter.equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SQL, | ||
subject: sumValueAdhocMetric.label, | ||
operator: OPERATORS['>'], | ||
comparator: 0, | ||
clause: CLAUSES.HAVING, | ||
}) | ||
))).to.be.true; | ||
}); | ||
|
||
it('handles columns being selected to filter on', () => { | ||
const { wrapper, onChange } = setup({ value: [] }); | ||
const select = wrapper.find(OnPasteSelect); | ||
select.simulate('change', [columns[0]]); | ||
|
||
const adhocFilter = onChange.lastCall.args[0][0]; | ||
expect(adhocFilter instanceof AdhocFilter).to.be.true; | ||
expect(adhocFilter.equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: columns[0].column_name, | ||
operator: OPERATORS['=='], | ||
comparator: '', | ||
clause: CLAUSES.WHERE, | ||
}) | ||
))).to.be.true; | ||
}); | ||
|
||
it('persists existing filters even when new filters are added', () => { | ||
const { wrapper, onChange } = setup(); | ||
const select = wrapper.find(OnPasteSelect); | ||
select.simulate('change', [simpleAdhocFilter, columns[0]]); | ||
|
||
const existingAdhocFilter = onChange.lastCall.args[0][0]; | ||
expect(existingAdhocFilter instanceof AdhocFilter).to.be.true; | ||
expect(existingAdhocFilter.equals(simpleAdhocFilter)).to.be.true; | ||
|
||
const newAdhocFilter = onChange.lastCall.args[0][1]; | ||
expect(newAdhocFilter instanceof AdhocFilter).to.be.true; | ||
expect(newAdhocFilter.equals(( | ||
new AdhocFilter({ | ||
expressionType: EXPRESSION_TYPES.SIMPLE, | ||
subject: columns[0].column_name, | ||
operator: OPERATORS['=='], | ||
comparator: '', | ||
clause: CLAUSES.WHERE, | ||
}) | ||
))).to.be.true; | ||
}); | ||
}); |
Oops, something went wrong.