Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix multi select filtering (#5358) #8452

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

ad-elias
Copy link
Contributor

@ad-elias ad-elias commented Nov 11, 2024

Allow filtering by multi-select fields.

Screenshot 2024-11-11 at 11 54 45

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This PR implements comprehensive multi-select filtering functionality across the frontend and backend, including GraphQL schema updates and PostgreSQL array operations.

  • Added containsAny operator in /packages/twenty-server/src/engine/api/graphql/graphql-query-runner/utils/compute-where-condition-parts.ts for PostgreSQL array overlap checks
  • Modified array handling in /packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts to support containsAny filtering
  • Created /packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/SelectFilterTypes.ts to unify SELECT and MULTI_SELECT filter handling
  • Updated enum filter types in /packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/factories/input-type.factory.ts to support containsAny operation
  • Changed multi-select field array handling in schema generation via /packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/generate-fields.utils.ts

10 file(s) reviewed, 6 comment(s)
Edit PR Review Bot Settings | Greptile

@twentyhq twentyhq deleted a comment from greptile-apps bot Nov 11, 2024
@twentyhq twentyhq deleted a comment from greptile-apps bot Nov 11, 2024
@@ -63,6 +63,9 @@ export const isMatchingStringFilter = ({
case stringFilter.startsWith !== undefined: {
return value.startsWith(stringFilter.startsWith);
}
case stringFilter.containsAny !== undefined: {
return stringFilter.containsAny.some((item) => value.includes(item));
Copy link
Member

@FelixMalfait FelixMalfait Nov 11, 2024

Choose a reason for hiding this comment

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

Getting an error when triple clicking on one of the 9Lol cell
Screenshot 2024-11-11 at 17 16 13

Choose a reason for hiding this comment

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

I'm not able to reproduce this. I could add a nullish check like so

return stringFilter.containsAny.some((item) => value?.includes(item));

but would be better to know why null is passed to this function in the first place. Quick screenshare call?

@FelixMalfait
Copy link
Member

Could you please also take care of the Array field? I just noticed it's similar because I saw not_contains. Also could you please rename it to notContains? Since there was no filtered view I don't think there's any risk to rolling out a rename for everyone (unless you see one?).

Also I saw the "not contains" filters out null which we probably don't really want.

On the UI front, maybe display "Contains" / "Does not contain" instead of "Is" / "Is not"?

@FelixMalfait FelixMalfait self-assigned this Nov 12, 2024
@@ -19,6 +19,11 @@ export const computeWhereConditionParts = (
const uuid = Math.random().toString(36).slice(2, 7);

switch (operator) {
case 'isEmptyArray':
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The value of an empty multi-select can either be NULL or an empty array.

Instead of adding isEmptyArray operator, we could change the eq operator, but as far as I understand GraphQL does not support union types of inputs.

In other words I think it's not possible to set the value of an eq filter to either be an empty array or an enum like this:

{
  eq: []
}
# or
{
  eq: 'SOME_ENUM_VALUE'
}

To get around that, we could add a subfield under eq like this:

{
  eq: { value: 'SOME_ENUM_VALUE' }
}
# or
{
  eq: { emptyList: true }
}

But I think it's cleaner to just add the isEmptyArray operator and keep eq as is.

@FelixMalfait What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

I guess all variations of existing syntax like eq: "'{}'" etc didn't work?

Choose a reason for hiding this comment

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

No, couldn't get any of them to work

filter.definition,
);
}
const stringifiedSelectValues = filter.value;
Copy link
Member

Choose a reason for hiding this comment

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

why do we manipulated stringified values here? I feel they should be parsed as as regular values way earlier

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved parsing the string to resolveFilterValue function, it's a bit cleaner now.

Could be good to first do a bigger refactoring of filtering, especially remove the Filter type and use ViewFilter everywhere instead, and after that move parsing earlier. What do you think?

emptyRecordFilter = {
or: [
{
[correspondingField.name]: { is: 'NULL' } as ArrayFilter,
Copy link
Member

Choose a reason for hiding this comment

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

fast folllow, we should have ArrayFilter, MultiSelectFilter, etc.

case FieldMetadataType.Text: {
return isMatchingStringFilter({
stringFilter: filterValue as StringFilter,
value: record[filterKey],
});
}
case FieldMetadataType.Select:
case FieldMetadataType.MultiSelect:
case FieldMetadataType.Array: {
return isMatchingArrayFilter({
arrayFilter: filterValue as ArrayFilter,
Copy link
Member

Choose a reason for hiding this comment

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

this looks wrong to me (see comment above) why would Select checking isMatchingArrayFilter. MultiSelect is already borderline to me (as constainsILike does not make sense), Select seems wrong

Copy link

github-actions bot commented Nov 15, 2024

TODOs/FIXMEs:

  • // TODO: Why doesn't `%${filter.value}%` here? %-signs only work when they are in compute-where-condition-parts.ts.: packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts

Generated by 🚫 dangerJS against 51874e5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants