Reduce complexity on groupBy query (#16803)

fixes https://github.com/twentyhq/core-team-issues/issues/2009
This commit is contained in:
Etienne
2026-01-02 14:19:40 +01:00
committed by GitHub
parent 98a9ae2a0e
commit ecd41fc9cb
9 changed files with 95 additions and 15 deletions
@@ -6,6 +6,7 @@ import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdow
import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection';
import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility';
import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector';
import { visibleRecordGroupIdsComponentFamilySelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentFamilySelector';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
@@ -13,10 +14,12 @@ import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
import { useLingui } from '@lingui/react/macro';
import { useLocation } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { VIEW_GROUP_VISIBLE_OPTIONS_MAX } from 'twenty-shared/constants';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath } from 'twenty-shared/utils';
import { IconChevronLeft, IconSettings } from 'twenty-ui/display';
@@ -29,6 +32,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
objectMetadataItem,
onContentChange,
closeDropdown,
viewType,
} = useObjectOptionsDropdown();
const recordGroupFieldMetadata = useRecoilComponentValue(
@@ -39,6 +43,14 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
hiddenRecordGroupIdsComponentSelector,
);
const visibleRecordGroupIds = useRecoilComponentFamilyValue(
visibleRecordGroupIdsComponentFamilySelector,
viewType,
);
const isVisibleLimitReached =
visibleRecordGroupIds.length >= VIEW_GROUP_VISIBLE_OPTIONS_MAX;
const { objectNamePlural } = useObjectNamePluralFromSingular({
objectNameSingular: objectMetadataItem.nameSingular,
});
@@ -84,6 +96,7 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => {
isDraggable={false}
showSubheader={false}
showDragGrip={false}
isVisibleLimitReached={isVisibleLimitReached}
/>
<DropdownMenuSeparator />
<UndecoratedLink
@@ -3,8 +3,8 @@ import {
type RecordGroupDefinition,
RecordGroupDefinitionType,
} from '@/object-record/record-group/types/RecordGroupDefinition';
import { useRecoilValue } from 'recoil';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
import { isDefined } from 'twenty-shared/utils';
import { Tag } from 'twenty-ui/components';
import { IconEye, IconEyeOff } from 'twenty-ui/display';
@@ -15,6 +15,7 @@ type RecordGroupMenuItemDraggableProps = {
showDragGrip?: boolean;
isDraggable?: boolean;
onVisibilityChange: (recordGroup: RecordGroupDefinition) => void;
isVisibleLimitReached?: boolean;
};
export const RecordGroupMenuItemDraggable = ({
@@ -22,6 +23,7 @@ export const RecordGroupMenuItemDraggable = ({
showDragGrip,
isDraggable,
onVisibilityChange,
isVisibleLimitReached = false,
}: RecordGroupMenuItemDraggableProps) => {
const recordGroup = useRecoilValue(
recordGroupDefinitionFamilyState(recordGroupId),
@@ -35,6 +37,11 @@ export const RecordGroupMenuItemDraggable = ({
const getIconButtons = (recordGroup: RecordGroupDefinition) => {
const groupValue = recordGroup.value;
if (!recordGroup.isVisible && isVisibleLimitReached) {
return undefined;
}
const iconButtons = [
{
Icon: recordGroup.isVisible ? IconEyeOff : IconEye,
@@ -20,6 +20,7 @@ type RecordGroupsVisibilityDropdownSectionProps = {
title: string;
showSubheader?: boolean;
showDragGrip: boolean;
isVisibleLimitReached?: boolean;
};
export const RecordGroupsVisibilityDropdownSection = ({
@@ -30,6 +31,7 @@ export const RecordGroupsVisibilityDropdownSection = ({
title,
showSubheader = true,
showDragGrip,
isVisibleLimitReached = false,
}: RecordGroupsVisibilityDropdownSectionProps) => {
const handleOnDrag = (result: DropResult, provided: ResponderProvided) => {
onDragEnd?.(result, provided);
@@ -53,6 +55,7 @@ export const RecordGroupsVisibilityDropdownSection = ({
onVisibilityChange={onVisibilityChange}
showDragGrip={showDragGrip}
isDraggable={isDraggable}
isVisibleLimitReached={isVisibleLimitReached}
/>
))
) : (
@@ -71,6 +74,7 @@ export const RecordGroupsVisibilityDropdownSection = ({
onVisibilityChange={onVisibilityChange}
showDragGrip={showDragGrip}
isDraggable={isDraggable}
isVisibleLimitReached={isVisibleLimitReached}
/>
}
/>
@@ -4,6 +4,7 @@ import { currentRecordFilterGroupsComponentState } from '@/object-record/record-
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { recordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { recordIndexGroupFieldMetadataItemComponentState } from '@/object-record/record-index/states/recordIndexGroupFieldMetadataComponentState';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
@@ -11,6 +12,7 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
import {
combineFilters,
computeRecordGqlOperationFilter,
isDefined,
turnAnyFieldFilterIntoRecordGqlFilter,
} from 'twenty-shared/utils';
@@ -59,11 +61,38 @@ export const useRecordIndexGroupCommonQueryVariables = () => {
additionalFieldMetadataId: recordGroupFieldMetadata?.id,
});
const combinedFilters = combineFilters([anyFieldFilter, requestFilters]);
const recordGroupDefinitions = useRecoilComponentValue(
recordGroupDefinitionsComponentSelector,
);
const visibleRecordGroupDefinitions = recordGroupDefinitions.filter(
(recordGroupDefinition) => recordGroupDefinition.isVisible,
);
const recordGroupValues = visibleRecordGroupDefinitions.map(
(recordGroupDefinition) => recordGroupDefinition.value,
);
const recordGroupOptionsFilter = isDefined(recordGroupFieldMetadata)
? {
[recordGroupFieldMetadata.name]: {
in: recordGroupValues,
},
}
: {};
const combinedFilters = combineFilters([
anyFieldFilter,
requestFilters,
recordGroupOptionsFilter,
]);
const recordGroupsLimit = visibleRecordGroupDefinitions.length;
return {
combinedFilters,
recordGqlFields,
orderBy,
recordGroupsLimit,
};
};
@@ -31,7 +31,7 @@ export const useRecordIndexGroupsRecordsLazyGroupBy = ({
const { objectMetadataItems } = useObjectMetadataItems();
const apolloCoreClient = useApolloCoreClient();
const { combinedFilters, orderBy, recordGqlFields } =
const { combinedFilters, orderBy, recordGqlFields, recordGroupsLimit } =
useRecordIndexGroupCommonQueryVariables();
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
@@ -55,11 +55,12 @@ export const useRecordIndexGroupsRecordsLazyGroupBy = ({
recordIndexGroupsRecordGroupsGroupByQuery,
{
variables: {
filter: { ...combinedFilters },
filter: combinedFilters,
groupBy: {
...groupByGqlInput,
},
orderByForRecords: orderBy,
limit: recordGroupsLimit,
},
client: apolloCoreClient,
},