Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.

Issue 545 - Case (in)sensitive filtering#893

Merged
Marc-Andre-Rivet merged 17 commits into
devfrom
case-insensitive-redux
May 11, 2021
Merged

Issue 545 - Case (in)sensitive filtering#893
Marc-Andre-Rivet merged 17 commits into
devfrom
case-insensitive-redux

Conversation

@Marc-Andre-Rivet

@Marc-Andre-Rivet Marc-Andre-Rivet commented Apr 29, 2021

Copy link
Copy Markdown
Contributor

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:

@Marc-Andre-Rivet
Marc-Andre-Rivet requested a review from wbrgss April 29, 2021 00:36
Comment thread .eslintrc.js
const match = query.match(next.regexp) ?? [];
const value = match[next.regexpMatch || 0];
const flags = match[next.regexpFlags || -1];
result.push({lexeme: next, flags, value});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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}
/>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

New component displayed in the filter cell to toggle filtering options

Comment thread src/dash-table/components/FilterFactory.tsx
clearable?: boolean | boolean[] | 'first' | 'last';
deletable?: boolean | boolean[] | 'first' | 'last';
editable: boolean;
filter_options: FilterCase;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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) => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

update the table's columns in the shallowest way possible (minimize cache busting)

Comment thread src/dash-table/syntax-tree/SingleColumnSyntaxTree.ts
): boolean =>
relOp[0] == 'i'
? fn(lhs.toString().toUpperCase(), rhs.toString().toUpperCase())
: fn(lhs, rhs);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Conditional evaluation of contains, =, !=, >=, >, <=, < based on the flags

),
subType: RelationalOperator.Contains,
regexp: /^((contains)(?=\s|$))/i,
regexp: /^((i|s)?contains)(?=\s|$)/i,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

allow for optional flags

subType: RelationalOperator.Contains,
regexp: /^((contains)(?=\s|$))/i,
regexp: /^((i|s)?contains)(?=\s|$)/i,
regexpFlags: 2,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

),
subType: RelationalOperator.Equal,
regexp: /^(=|(eq)(?=\s|$))/i,
regexp: /^((i|s)?(=|(eq)(?=\s|$)))/i,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

same thing for all applicable relational operators

query: `${c.hideOperand ? '' : '{a} '}seq ABC`,
target: {a: 'abc'},
valid: true,
evaluate: false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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);
});

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Test all new operators with the various query flavors

@wbrgss wbrgss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Comment thread src/dash-table/components/FilterFactory.tsx
Comment thread src/dash-table/syntax-tree/SingleColumnSyntaxTree.ts
Comment thread src/dash-table/syntax-tree/SingleColumnSyntaxTree.ts
Comment thread src/dash-table/syntax-tree/SingleColumnSyntaxTree.ts
Comment thread tests/selenium/test_filter.py Outdated
Comment thread src/dash-table/components/Filter/FilterOptions.tsx
@Marc-Andre-Rivet
Marc-Andre-Rivet marked this pull request as ready for review April 30, 2021 23:47

@wbrgss wbrgss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@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?

filtering

@Marc-Andre-Rivet

Copy link
Copy Markdown
Contributor Author

@wbrgss Good point. Yes it makes total sense to have lt like ilt if the toggle is turned on. I'll see about adding that in.

@wbrgss wbrgss left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Very nice @Marc-Andre-Rivet

💃

Comment thread 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']),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

@Marc-Andre-Rivet Marc-Andre-Rivet May 11, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ada8456, 6f012c3, 4697136 (mistakes were made..)

Marc-André Rivet added 3 commits May 11, 2021 12:07
…tring to improve forward compatibility and consistency
- invert column and table filter_options priority (column takes precedence)

@alexcjohnson alexcjohnson left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

💃

@Marc-Andre-Rivet
Marc-Andre-Rivet merged commit 776484f into dev May 11, 2021
@Marc-Andre-Rivet
Marc-Andre-Rivet deleted the case-insensitive-redux branch May 11, 2021 18:24
@AnnMarieW

Copy link
Copy Markdown
Contributor

This is so awesome! 🎊

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

case insensitive filtering

4 participants