mirror of
https://github.com/apache/superset.git
synced 2024-09-12 08:39:45 -04:00
[druid] fixing the having clause in the explore view (#2648)
* [druid] fixing the having clause in the explore view * Backend * Lint * Fixing tests
This commit is contained in:
parent
f10ee13901
commit
29780821e8
@ -46,11 +46,12 @@ export default class DisplayQueryButton extends React.PureComponent {
|
|||||||
language: data.language,
|
language: data.language,
|
||||||
query: data.query,
|
query: data.query,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
error: (data) => {
|
error: (data) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
error: data.error,
|
error: data.responseJSON ? data.responseJSON.error : 'Error...',
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -8,12 +8,12 @@ const $ = window.$ = require('jquery');
|
|||||||
const operatorsArr = [
|
const operatorsArr = [
|
||||||
{ val: 'in', type: 'array', useSelect: true, multi: true },
|
{ val: 'in', type: 'array', useSelect: true, multi: true },
|
||||||
{ val: 'not in', type: 'array', useSelect: true, multi: true },
|
{ val: 'not in', type: 'array', useSelect: true, multi: true },
|
||||||
{ val: '==', type: 'string', useSelect: true, multi: false },
|
{ val: '==', type: 'string', useSelect: true, multi: false, havingOnly: true },
|
||||||
{ val: '!=', type: 'string', useSelect: true, multi: false },
|
{ val: '!=', type: 'string', useSelect: true, multi: false, havingOnly: true },
|
||||||
{ val: '>=', type: 'string' },
|
{ val: '>=', type: 'string', havingOnly: true },
|
||||||
{ val: '<=', type: 'string' },
|
{ val: '<=', type: 'string', havingOnly: true },
|
||||||
{ val: '>', type: 'string' },
|
{ val: '>', type: 'string', havingOnly: true },
|
||||||
{ val: '<', type: 'string' },
|
{ val: '<', type: 'string', havingOnly: true },
|
||||||
{ val: 'regex', type: 'string', datasourceTypes: ['druid'] },
|
{ val: 'regex', type: 'string', datasourceTypes: ['druid'] },
|
||||||
{ val: 'LIKE', type: 'string', datasourceTypes: ['table'] },
|
{ val: 'LIKE', type: 'string', datasourceTypes: ['table'] },
|
||||||
];
|
];
|
||||||
@ -27,12 +27,14 @@ const propTypes = {
|
|||||||
removeFilter: PropTypes.func,
|
removeFilter: PropTypes.func,
|
||||||
filter: PropTypes.object.isRequired,
|
filter: PropTypes.object.isRequired,
|
||||||
datasource: PropTypes.object,
|
datasource: PropTypes.object,
|
||||||
|
having: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
changeFilter: () => {},
|
changeFilter: () => {},
|
||||||
removeFilter: () => {},
|
removeFilter: () => {},
|
||||||
datasource: null,
|
datasource: null,
|
||||||
|
having: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Filter extends React.Component {
|
export default class Filter extends React.Component {
|
||||||
@ -47,7 +49,7 @@ export default class Filter extends React.Component {
|
|||||||
}
|
}
|
||||||
fetchFilterValues(col) {
|
fetchFilterValues(col) {
|
||||||
const datasource = this.props.datasource;
|
const datasource = this.props.datasource;
|
||||||
if (col && this.props.datasource && this.props.datasource.filter_select) {
|
if (col && this.props.datasource && this.props.datasource.filter_select && !this.props.having) {
|
||||||
this.setState({ valuesLoading: true });
|
this.setState({ valuesLoading: true });
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
@ -90,7 +92,7 @@ export default class Filter extends React.Component {
|
|||||||
}
|
}
|
||||||
renderFilterFormControl(filter) {
|
renderFilterFormControl(filter) {
|
||||||
const operator = operators[filter.op];
|
const operator = operators[filter.op];
|
||||||
if (operator.useSelect) {
|
if (operator.useSelect && !this.props.having) {
|
||||||
return (
|
return (
|
||||||
<SelectControl
|
<SelectControl
|
||||||
multi={operator.multi}
|
multi={operator.multi}
|
||||||
@ -117,18 +119,28 @@ export default class Filter extends React.Component {
|
|||||||
const datasource = this.props.datasource;
|
const datasource = this.props.datasource;
|
||||||
const filter = this.props.filter;
|
const filter = this.props.filter;
|
||||||
const opsChoices = operatorsArr
|
const opsChoices = operatorsArr
|
||||||
.filter(o => !o.datasourceTypes || o.datasourceTypes.indexOf(datasource.type) >= 0)
|
.filter((o) => {
|
||||||
|
if (this.props.having) {
|
||||||
|
return !!o.havingOnly;
|
||||||
|
}
|
||||||
|
return (!o.datasourceTypes || o.datasourceTypes.indexOf(datasource.type) >= 0);
|
||||||
|
})
|
||||||
.map(o => ({ value: o.val, label: o.val }));
|
.map(o => ({ value: o.val, label: o.val }));
|
||||||
const colChoices = datasource ?
|
let colChoices;
|
||||||
datasource.filterable_cols.map(c => ({ value: c[0], label: c[1] })) :
|
if (datasource) {
|
||||||
null;
|
if (this.props.having) {
|
||||||
|
colChoices = datasource.metrics_combo.map(c => ({ value: c[0], label: c[1] }));
|
||||||
|
} else {
|
||||||
|
colChoices = datasource.filterable_cols.map(c => ({ value: c[0], label: c[1] }));
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Row className="space-1">
|
<Row className="space-1">
|
||||||
<Col md={12}>
|
<Col md={12}>
|
||||||
<Select
|
<Select
|
||||||
id="select-col"
|
id="select-col"
|
||||||
placeholder="Select column"
|
placeholder={this.props.having ? 'Select metric' : 'Select column'}
|
||||||
clearable={false}
|
clearable={false}
|
||||||
options={colChoices}
|
options={colChoices}
|
||||||
value={filter.col}
|
value={filter.col}
|
||||||
|
@ -22,6 +22,10 @@ const defaultProps = {
|
|||||||
type: 'qtable',
|
type: 'qtable',
|
||||||
filter_select: false,
|
filter_select: false,
|
||||||
filterable_cols: ['col1', 'col2'],
|
filterable_cols: ['col1', 'col2'],
|
||||||
|
metrics_combo: [
|
||||||
|
['m1', 'v1'],
|
||||||
|
['m2', 'v2'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,7 +50,7 @@ describe('Filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders five op choices for table datasource', () => {
|
it('renders five op choices for table datasource', () => {
|
||||||
const props = defaultProps;
|
const props = Object.assign({}, defaultProps);
|
||||||
props.datasource = {
|
props.datasource = {
|
||||||
id: 1,
|
id: 1,
|
||||||
type: 'druid',
|
type: 'druid',
|
||||||
@ -58,10 +62,10 @@ describe('Filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders six op choices for having filter', () => {
|
it('renders six op choices for having filter', () => {
|
||||||
const props = defaultProps;
|
const props = Object.assign({}, defaultProps);
|
||||||
props.having = true;
|
props.having = true;
|
||||||
const havingWrapper = shallow(<Filter {...props} />);
|
const havingWrapper = shallow(<Filter {...props} />);
|
||||||
expect(havingWrapper.find('#select-op').prop('options')).to.have.lengthOf(9);
|
expect(havingWrapper.find('#select-op').prop('options')).to.have.lengthOf(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls changeFilter when select is changed', () => {
|
it('calls changeFilter when select is changed', () => {
|
||||||
@ -75,7 +79,7 @@ describe('Filter', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders input for regex filters', () => {
|
it('renders input for regex filters', () => {
|
||||||
const props = defaultProps;
|
const props = Object.assign({}, defaultProps);
|
||||||
props.filter = {
|
props.filter = {
|
||||||
col: null,
|
col: null,
|
||||||
op: 'regex',
|
op: 'regex',
|
||||||
|
@ -835,7 +835,7 @@ class DruidDatasource(Model, BaseDatasource):
|
|||||||
qry['having'] = having_filters
|
qry['having'] = having_filters
|
||||||
|
|
||||||
orig_filters = filters
|
orig_filters = filters
|
||||||
if len(groupby) == 0:
|
if len(groupby) == 0 and not having_filters:
|
||||||
del qry['dimensions']
|
del qry['dimensions']
|
||||||
client.timeseries(**qry)
|
client.timeseries(**qry)
|
||||||
if not having_filters and len(groupby) == 1:
|
if not having_filters and len(groupby) == 1:
|
||||||
|
@ -29,7 +29,7 @@ def get_error_msg():
|
|||||||
|
|
||||||
|
|
||||||
def json_error_response(msg, status=None, stacktrace=None):
|
def json_error_response(msg, status=None, stacktrace=None):
|
||||||
data = {'error': msg}
|
data = {'error': str(msg)}
|
||||||
if stacktrace:
|
if stacktrace:
|
||||||
data['stacktrace'] = stacktrace
|
data['stacktrace'] = stacktrace
|
||||||
status = status if status else 500
|
status = status if status else 500
|
||||||
|
Loading…
Reference in New Issue
Block a user