Issue 545 - Case (in)sensitive filtering#893
Conversation
| const match = query.match(next.regexp) ?? []; | ||
| const value = match[next.regexpMatch || 0]; | ||
| const flags = match[next.regexpFlags || -1]; | ||
| result.push({lexeme: next, flags, value}); |
There was a problem hiding this comment.
From a lexicon perspective treat i|s prefix to applicable relational operators as "flags" that can be used to determine the exact processing to do during evaluation. value is the "base" operator + flags (e.g. icontains)
| <FilterOptions | ||
| filterOptions={filterOptions} | ||
| toggleFilterOptions={toggleFilterOptions} | ||
| /> |
There was a problem hiding this comment.
New component displayed in the filter cell to toggle filtering options
| clearable?: boolean | boolean[] | 'first' | 'last'; | ||
| deletable?: boolean | boolean[] | 'first' | 'last'; | ||
| editable: boolean; | ||
| filter_options: FilterCase; |
There was a problem hiding this comment.
FilterCase vs. filter_options may look inconsistent but is intended to be that way. In the future you may also want to add locale-aware comparisons and leaving some space to do so at a future time.
| ) => { | ||
| const newColumn = toggleFilterOptions(column); | ||
|
|
||
| updateColumnFilter(map, newColumn, operator, value, setFilter); |
There was a problem hiding this comment.
Trigger a setProps and return the new column configuration, use the new column configuration to update the column's query (and trigger a refresh)
| ); | ||
|
|
||
| const toggleFilterOptions = memoizeOne( | ||
| (setProps: SetProps, columns: IColumn[]) => (column: IColumn) => { |
There was a problem hiding this comment.
Called by https://github.com/plotly/dash-table/pull/893/files#r622661715
Defined at the factory level so that the individual components remain unaware of columns for memoization/cache busting / performance reasons and setProps so they remain unaware of Dash's idiosyncracies.
|
|
||
| setProps({columns: newColumns}); | ||
|
|
||
| return newColumn; |
There was a problem hiding this comment.
update the table's columns in the shallowest way possible (minimize cache busting)
| ): boolean => | ||
| relOp[0] == 'i' | ||
| ? fn(lhs.toString().toUpperCase(), rhs.toString().toUpperCase()) | ||
| : fn(lhs, rhs); |
There was a problem hiding this comment.
Conditional evaluation of contains, =, !=, >=, >, <=, < based on the flags
| ), | ||
| subType: RelationalOperator.Contains, | ||
| regexp: /^((contains)(?=\s|$))/i, | ||
| regexp: /^((i|s)?contains)(?=\s|$)/i, |
There was a problem hiding this comment.
allow for optional flags
| subType: RelationalOperator.Contains, | ||
| regexp: /^((contains)(?=\s|$))/i, | ||
| regexp: /^((i|s)?contains)(?=\s|$)/i, | ||
| regexpFlags: 2, |
There was a problem hiding this comment.
regex match group for usage in https://github.com/plotly/dash-table/pull/893/files#r622660202
| ), | ||
| subType: RelationalOperator.Equal, | ||
| regexp: /^(=|(eq)(?=\s|$))/i, | ||
| regexp: /^((i|s)?(=|(eq)(?=\s|$)))/i, |
There was a problem hiding this comment.
same thing for all applicable relational operators
| query: `${c.hideOperand ? '' : '{a} '}seq ABC`, | ||
| target: {a: 'abc'}, | ||
| valid: true, | ||
| evaluate: false |
There was a problem hiding this comment.
Multiple new tests for the (in)sensitive versions of the operators -- as used by the table itself
| expect(tree.isValid).to.equal(true); | ||
| expect(tree.evaluate({a: 'abc'})).to.equal(true); | ||
| expect(tree.evaluate({a: 'ABC'})).to.equal(false); | ||
| }); |
There was a problem hiding this comment.
Test all new operators with the various query flavors
wbrgss
left a comment
There was a problem hiding this comment.
Looking nice so far! I'll probably do another more fine-grained pass of the code later. The functionality looks good.
If you have time @alexcjohnson I'd welcome a review from you as well
wbrgss
left a comment
There was a problem hiding this comment.
@Marc-Andre-Rivet Let's say I set a column to be case-insensitive using the toggle, or with column-level 'filter_options': 'insensitive'. I know I can force case-insensitive comparison using e.g. ilt, but I might also expect lt to behave the same as ilt, considering the case-insensitive toggle is on, and right now it doesn't on my end. Does that make sense?
|
@wbrgss Good point. Yes it makes total sense to have |
wbrgss
left a comment
There was a problem hiding this comment.
Very nice @Marc-Andre-Rivet
💃
src/dash-table/dash/DataTable.js
Outdated
| * If the column-level `filter_options` prop is set it overrides | ||
| * the table-level `filter_options` prop for that column. | ||
| */ | ||
| filter_options: PropTypes.oneOf(['sensitive', 'insensitive']), |
There was a problem hiding this comment.
After a bit of a slack convo with @Marc-Andre-Rivet I think we concluded we should change this to filter_options: {case: ('sensitive'|'insensitive')}. The reason he wrote it as filter_options instead of something more specific like filter_case was to allow extending to ignoring other distinctions, like accents and punctuation. But that means the current syntax is confusing as it doesn't tell you what you're controlling the sensitivity to, and to extend later we'll have to support both these strings and objects like {case: 'sensitive', accents: 'insensitive', punctuation: 'sensitive'}
The alternative would be to make a single specific prop now, like filter_case: 'sensitive', and then add more props later filter_accents: 'sensitive', filter_punctuation: 'sensitive'. That's a little easier for the most common use of enabling case-insensitive filters, but will ultimately create more props than it would need to, at both the table and column levels.
There was a problem hiding this comment.
Changing this to an object works for me. I knew @Marc-Andre-Rivet wrote this with extensibility in mind and I agree this more cleanly allows for various filter-option combinations
…tring to improve forward compatibility and consistency
- invert column and table filter_options priority (column takes precedence)
|
This is so awesome! 🎊 |

Closes #545. Uses as as a starting point and supersedes #609.
@wbrgss What follows is not necessary for the review itself but, if you want to dig a little bit into the table's query parser / AST implementation, here's the key things to look at:
dash-table/src/core/syntax-tree/lexicon.ts
Line 13 in 8172683
dash-table/src/core/syntax-tree/lexicon.ts
Line 27 in 8172683
dash-table/src/core/syntax-tree/lexer.ts
Line 16 in 8172683
dash-table/src/core/syntax-tree/syntaxer.ts
Line 76 in 8172683