Add negation support.

master
Sander Vocke 6 years ago
parent c6654566f3
commit bcbc51ba5d
  1. 9
      src/queries.js
  2. 87
      src/userquerywidget.js

@ -80,6 +80,7 @@ export class ResultFilter {
to_sql_where() { return "(1=1)"; }
is_true() { return false; }
is_false() { return false; }
is_negation() { return false; }
simplify() { return this; }
}
@ -105,17 +106,23 @@ export class NegationFilter extends ResultFilter {
body = new ConstFilter(this.return_type, false);
to_sql_where() {
return "NOT (" + this.body.to_sql_where + ")";
return "NOT (" + this.body.to_sql_where() + ")";
}
is_negation() { return true; }
simplify() {
var f = this.body.simplify();
console.log("NOT body:", f);
if (f.is_true()) {
return new ConstFilter(this.result_type, false);
}
if (f.is_false()) {
return new ConstFilter(this.result_type, true);
}
if (f.is_negation()) {
return f.body;
}
return this;
}
}

@ -203,20 +203,29 @@ export function EditFilterExpressionDialog(props) {
control = <EditMatchingFilterExpression {...subprops} />
}
// If this is a "leaf" filter, we will allow changing the filter type in the dialog.
// But if it is a combination filter (i.e. AND / OR / NOT), we won't allow it because
// That throws away all its children.
const allowTypeChange =
(filter instanceof ConstFilter) ||
(filter instanceof MatchingFilter);
return (
<Dialog aria-labelledby={id} open={open}>
<DialogTitle id={id}>Edit expression</DialogTitle>
<FormControl className={classes.margined}>
<Select
labelId={selectLabelId}
id={selectId}
value={getFilterType(filter)}
onChange={handleTypeChange}
>
<MenuItem value={FilterTypeEnum.CONST}>Constant</MenuItem>
<MenuItem value={FilterTypeEnum.MATCHING}>Matching</MenuItem>
</Select>
</FormControl>
{allowTypeChange &&
<FormControl className={classes.margined}>
<Select
labelId={selectLabelId}
id={selectId}
value={getFilterType(filter)}
onChange={handleTypeChange}
>
<MenuItem value={FilterTypeEnum.CONST}>Constant</MenuItem>
<MenuItem value={FilterTypeEnum.MATCHING}>Matching</MenuItem>
</Select>
</FormControl>
}
{control}
<Button onClick={handleClose} className={classes.margined}>
Done
@ -394,6 +403,40 @@ export function ConstFilterExpressionControl(props) {
);
}
export function NegationExpressionControl(props) {
const classes = useStyles();
const { expr, onClick, onChange } = props;
var _ = require('lodash');
function handleBodyChanged(body) {
var new_filter = _.cloneDeep(expr);
new_filter.body = body;
onChange(new_filter);
}
return (
<>
<Box className={classes.logic_op_outer}>
<Box className={classes.logic_op_sbs}>
<Box>
<Box className={classes.logic_op_subexpr}>
<FilterExpressionControl expr={expr.body} onChange={handleBodyChanged} isRoot={false} />
</Box>
</Box>
</Box>
<Button
variant="outlined"
className={classes.filterexpcontrol + " " + classes.logic_op_sbs}
aria-controls="simple-menu" aria-haspopup="true"
onClick={onClick}
>
NOT
</Button>
</Box>
</>
)
}
export function FilterExpressionControl(props) {
const { expr, onChange, isRoot } = props;
const [anchorEl, setAnchorEl] = React.useState(null);
@ -428,6 +471,12 @@ export function FilterExpressionControl(props) {
};
const handleRemove = () => {
// For negation filters, removal means replacing the negation node by its child.
// In all other cases, removal means complete deletion of the subtree.
if (expr instanceof NegationFilter) {
onChange(expr.body);
return;
}
onChange(null);
}
@ -458,6 +507,12 @@ export function FilterExpressionControl(props) {
setCombineDialogOpen(true);
}
const handleNegation = () => {
handleCloseMenu();
var new_filter = new NegationFilter(expr.result_type, expr);
onChange(new_filter.simplify());
}
var filter_elem = false;
if (expr instanceof ConstFilter) {
filter_elem = <ConstFilterExpressionControl {...props} onClick={handleClick} />
@ -465,10 +520,17 @@ export function FilterExpressionControl(props) {
filter_elem = <LogicalOperatorFilterExpressionControl {...props} onClick={handleClick} onChange={onChange} />
} else if (expr instanceof MatchingFilter) {
filter_elem = <MatchingFilterExpressionControl {...props} onClick={handleClick} />
} else if (expr instanceof NegationFilter) {
filter_elem = <NegationExpressionControl {...props} onClick={handleClick} onChange={onChange} />
} else {
throw new Error('Unsupported filter expression');
}
// If this is the root node, removing it is not allowed.
// Other nodes may be removed.
// The only exception is a negation node: removing that will replace it by its child.
var allowRemove = !isRoot || (expr instanceof NegationFilter);
return (
<>
{filter_elem}
@ -482,7 +544,8 @@ export function FilterExpressionControl(props) {
<MenuItem onClick={handleOpenEditDialog}>Edit...</MenuItem>
<MenuItem onClick={handleAnd}>And...</MenuItem>
<MenuItem onClick={handleOr}>Or...</MenuItem>
{!isRoot && <MenuItem onClick={handleRemove}>Remove</MenuItem>}
<MenuItem onClick={handleNegation}>Negate</MenuItem>
{allowRemove && <MenuItem onClick={handleRemove}>Remove</MenuItem>}
</Menu>
<EditFilterExpressionDialog
onClose={handleCloseEditFilterDialog}

Loading…
Cancel
Save