fix(filters): unify combinedFilter for queries and bulk delete (#13952)

Issue: https://github.com/twentyhq/twenty/issues/13913

Motivation/Problem:
Bulk delete and query paths were composing filters differently, causing
mismatches. In some cases this led to invalid or empty GraphQL filters
(e.g. {"and":[{}]}), breaking delete operations.

Fix:
Unify filter composition (combinedFilter) across queries and bulk
delete, ensuring consistent handling of base filters + soft-deleted
clause. Also adjusted record filter grouping logic to avoid dropping
filters when no groups exist.

Result:
Filtered queries and bulk deletes now behave consistently and reliably
without producing broken filters.

---------

Co-authored-by: root <root@DESKTOP-E2VOJGE>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Puranjay Mishra
2025-08-18 06:01:47 -04:00
committed by GitHub
parent 1598e5590a
commit 483c1e2214
16 changed files with 185 additions and 53 deletions
@@ -1,6 +1,7 @@
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
@@ -39,19 +40,24 @@ export const DeleteMultipleRecordsAction = () => {
contextStoreFiltersComponentState,
);
const contextStoreFilterGroups = useRecoilComponentValue(
contextStoreFilterGroupsComponentState,
);
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
contextStoreAnyFieldFilterValueComponentState,
);
const { filterValueDependencies } = useFilterValueDependencies();
const graphqlFilter = computeContextStoreFilters(
const graphqlFilter = computeContextStoreFilters({
contextStoreTargetedRecordsRule,
contextStoreFilters,
contextStoreFilterGroups,
objectMetadataItem,
filterValueDependencies,
contextStoreAnyFieldFilterValue,
);
});
const { fetchAllRecords: fetchAllRecordIds } = useLazyFetchAllRecords({
objectNameSingular: objectMetadataItem.nameSingular,
@@ -1,6 +1,7 @@
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
@@ -39,6 +40,10 @@ export const DestroyMultipleRecordsAction = () => {
contextStoreFiltersComponentState,
);
const contextStoreFilterGroups = useRecoilComponentValue(
contextStoreFilterGroupsComponentState,
);
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
contextStoreAnyFieldFilterValueComponentState,
);
@@ -49,13 +54,14 @@ export const DestroyMultipleRecordsAction = () => {
deletedAt: { is: 'NOT_NULL' },
};
const graphqlFilter = {
...computeContextStoreFilters(
...computeContextStoreFilters({
contextStoreTargetedRecordsRule,
contextStoreFilters,
contextStoreFilterGroups,
objectMetadataItem,
filterValueDependencies,
contextStoreAnyFieldFilterValue,
),
}),
...deletedAtFilter,
};
@@ -1,6 +1,7 @@
import { ActionModal } from '@/action-menu/actions/components/ActionModal';
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
@@ -39,6 +40,10 @@ export const RestoreMultipleRecordsAction = () => {
contextStoreFiltersComponentState,
);
const contextStoreFilterGroups = useRecoilComponentValue(
contextStoreFilterGroupsComponentState,
);
const contextStoreAnyFieldFilterValue = useRecoilComponentValue(
contextStoreAnyFieldFilterValueComponentState,
);
@@ -50,13 +55,14 @@ export const RestoreMultipleRecordsAction = () => {
};
const graphqlFilter = {
...computeContextStoreFilters(
...computeContextStoreFilters({
contextStoreTargetedRecordsRule,
contextStoreFilters,
contextStoreFilterGroups,
objectMetadataItem,
filterValueDependencies,
contextStoreAnyFieldFilterValue,
),
}),
...deletedAtFilter,
};