Add optional limit variable to groupBy queries (#15885)
Closes https://github.com/twentyhq/core-team-issues/issues/1600. Two remarks - This `limit` variable does not reduce postgre's work at it still needs to scan the whole table. It did not seem possible to me to optimize this as we cannot foresee which dimensions will be used by the user, and an optimization could only result from an index on the dimension(s) (e.g.: group companies by addressCity limit 50 can be optimized if we have an index on companies.addressCity + we had a default orderBy on adressCity). But this will still optimize the FE which at the moment receives all groups and truncates the result. - I have not done the work on the FE as the addition of limit is a breaking change, and will break until the workspaces' schema is rebuilt, so we need to flush the cache. I think this could be acceptable as the feature is in the lab but I preferred not doing it yet as it would have no impact since in the BE I added a default limit to 50 groups, and I expect more FE work will be done to allow the user to choose their own limit
This commit is contained in:
+9
@@ -37,6 +37,7 @@ import { isGroupByDateField } from 'src/engine/api/graphql/graphql-query-runner/
|
||||
import { parseGroupByArgs } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/parse-group-by-args.util';
|
||||
import { removeQuotes } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/remove-quote.util';
|
||||
import { GroupByWithRecordsService } from 'src/engine/api/graphql/graphql-query-runner/group-by/services/group-by-with-records.service';
|
||||
import { getGroupLimit } from 'src/engine/api/graphql/graphql-query-runner/group-by/utils/get-group-limit.util';
|
||||
import { ProcessAggregateHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-aggregate.helper';
|
||||
import { isFieldMetadataRelationOrMorphRelation } from 'src/engine/api/graphql/workspace-schema-builder/utils/is-field-metadata-relation-or-morph-relation.utils';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
@@ -175,6 +176,7 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
selectedFieldsResult: args.selectedFieldsResult,
|
||||
queryRunnerContext,
|
||||
orderByForRecords: args.orderByForRecords ?? [],
|
||||
groupLimit: args.limit,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -182,6 +184,7 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
queryBuilder,
|
||||
groupByDefinitions,
|
||||
selectedFieldsResult: args.selectedFieldsResult,
|
||||
groupLimit: args.limit,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,11 +323,17 @@ export class CommonGroupByQueryRunnerService extends CommonBaseQueryRunnerServic
|
||||
queryBuilder,
|
||||
groupByDefinitions,
|
||||
selectedFieldsResult,
|
||||
groupLimit,
|
||||
}: {
|
||||
queryBuilder: WorkspaceSelectQueryBuilder<ObjectLiteral>;
|
||||
groupByDefinitions: GroupByDefinition[];
|
||||
selectedFieldsResult: GraphqlQuerySelectedFieldsResult;
|
||||
groupLimit?: number;
|
||||
}): Promise<CommonGroupByOutputItem[]> {
|
||||
const effectiveGroupLimit = getGroupLimit(groupLimit);
|
||||
|
||||
queryBuilder.limit(effectiveGroupLimit);
|
||||
|
||||
const result = await queryBuilder.getRawMany();
|
||||
|
||||
return formatResultWithGroupByDimensionValues({
|
||||
|
||||
@@ -70,6 +70,7 @@ export interface GroupByQueryArgs {
|
||||
viewId?: string;
|
||||
includeRecords?: boolean;
|
||||
selectedFields: CommonSelectedFields;
|
||||
limit?: number;
|
||||
}
|
||||
export interface DestroyOneQueryArgs {
|
||||
id: string;
|
||||
|
||||
+6
-2
@@ -14,6 +14,7 @@ import { type GraphqlQuerySelectedFieldsResult } from 'src/engine/api/graphql/gr
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { type GroupByDefinition } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/types/group-by-definition.types';
|
||||
import { formatResultWithGroupByDimensionValues } from 'src/engine/api/graphql/graphql-query-runner/group-by/resolvers/utils/format-result-with-group-by-dimension-values.util';
|
||||
import { getGroupLimit } from 'src/engine/api/graphql/graphql-query-runner/group-by/utils/get-group-limit.util';
|
||||
import { ProcessNestedRelationsHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/process-nested-relations.helper';
|
||||
import { buildColumnsToSelect } from 'src/engine/api/graphql/graphql-query-runner/utils/build-columns-to-select';
|
||||
import { ObjectMetadataItemWithFieldMaps } from 'src/engine/metadata-modules/types/object-metadata-item-with-field-maps';
|
||||
@@ -21,7 +22,6 @@ import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-met
|
||||
import { type WorkspaceSelectQueryBuilder } from 'src/engine/twenty-orm/repository/workspace-select-query-builder';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
|
||||
const GROUPS_LIMIT = 50;
|
||||
const RECORDS_PER_GROUP_LIMIT = 10;
|
||||
const RELATIONS_PER_RECORD_LIMIT = 5;
|
||||
const SUB_QUERY_PREFIX = 'sub_query_';
|
||||
@@ -41,6 +41,7 @@ export class GroupByWithRecordsService {
|
||||
selectedFieldsResult,
|
||||
queryRunnerContext,
|
||||
orderByForRecords,
|
||||
groupLimit,
|
||||
}: {
|
||||
queryBuilderWithGroupBy: WorkspaceSelectQueryBuilder<ObjectLiteral>;
|
||||
queryBuilderWithFiltersAndWithoutGroupBy: WorkspaceSelectQueryBuilder<ObjectLiteral>;
|
||||
@@ -48,9 +49,12 @@ export class GroupByWithRecordsService {
|
||||
selectedFieldsResult: GraphqlQuerySelectedFieldsResult;
|
||||
queryRunnerContext: CommonExtendedQueryRunnerContext;
|
||||
orderByForRecords: ObjectRecordOrderBy;
|
||||
groupLimit?: number;
|
||||
}): Promise<CommonGroupByOutputItem[]> {
|
||||
const effectiveGroupLimit = getGroupLimit(groupLimit);
|
||||
|
||||
const groupsResult = await queryBuilderWithGroupBy
|
||||
.limit(GROUPS_LIMIT)
|
||||
.limit(effectiveGroupLimit)
|
||||
.getRawMany();
|
||||
|
||||
if (groupsResult.length === 0) {
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { DEFAULT_NUMBER_OF_GROUPS_LIMIT } from 'twenty-shared/constants';
|
||||
|
||||
export const getGroupLimit = (limit?: number): number => {
|
||||
if (
|
||||
typeof limit === 'number' &&
|
||||
Number.isFinite(limit) &&
|
||||
limit > 0 &&
|
||||
Number.isInteger(limit)
|
||||
) {
|
||||
return limit;
|
||||
}
|
||||
|
||||
return DEFAULT_NUMBER_OF_GROUPS_LIMIT;
|
||||
};
|
||||
+4
@@ -186,6 +186,10 @@ export const getResolverArgs = (
|
||||
type: UUIDScalarType,
|
||||
isNullable: true,
|
||||
},
|
||||
limit: {
|
||||
type: GraphQLInt,
|
||||
isNullable: true,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(`Unknown resolver type: ${type}`);
|
||||
|
||||
Reference in New Issue
Block a user