Allow filtering by multi-select fields. <img width="1053" alt="Screenshot 2024-11-11 at 11 54 45" src="https://github.com/user-attachments/assets/a79b2251-94e3-48f8-abda-e808103a6c39"> --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -13,7 +13,7 @@ import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metada
|
||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||
import { capitalize } from 'src/utils/capitalize';
|
||||
|
||||
const ARRAY_OPERATORS = ['in', 'contains', 'not_contains'];
|
||||
const ARRAY_OPERATORS = ['in', 'contains', 'notContains'];
|
||||
|
||||
export class GraphqlQueryFilterFieldParser {
|
||||
private fieldMetadataMapByName: FieldMetadataMap;
|
||||
|
||||
@ -19,6 +19,11 @@ export const computeWhereConditionParts = (
|
||||
const uuid = Math.random().toString(36).slice(2, 7);
|
||||
|
||||
switch (operator) {
|
||||
case 'isEmptyArray':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" = '{}'`,
|
||||
params: {},
|
||||
};
|
||||
case 'eq':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}" = :${key}${uuid}`,
|
||||
@ -84,10 +89,19 @@ export const computeWhereConditionParts = (
|
||||
sql: `"${objectNameSingular}"."${key}" @> ARRAY[:...${key}${uuid}]`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
|
||||
case 'not_contains':
|
||||
case 'notContains':
|
||||
return {
|
||||
sql: `NOT ("${objectNameSingular}"."${key}" && ARRAY[:...${key}${uuid}])`,
|
||||
sql: `NOT ("${objectNameSingular}"."${key}"::text[] && ARRAY[:...${key}${uuid}]::text[])`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'containsAny':
|
||||
return {
|
||||
sql: `"${objectNameSingular}"."${key}"::text[] && ARRAY[:...${key}${uuid}]::text[]`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
case 'containsIlike':
|
||||
return {
|
||||
sql: `EXISTS (SELECT 1 FROM unnest("${objectNameSingular}"."${key}") AS elem WHERE elem ILIKE :${key}${uuid})`,
|
||||
params: { [`${key}${uuid}`]: value },
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
|
||||
import { GraphQLInputObjectType, GraphQLInputType, GraphQLList } from 'graphql';
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLInputType,
|
||||
GraphQLList,
|
||||
} from 'graphql';
|
||||
|
||||
import { WorkspaceBuildSchemaOptions } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-build-schema-optionts.interface';
|
||||
|
||||
@ -103,7 +108,9 @@ export class InputTypeFactory {
|
||||
eq: { type: enumType },
|
||||
neq: { type: enumType },
|
||||
in: { type: new GraphQLList(enumType) },
|
||||
containsAny: { type: new GraphQLList(enumType) },
|
||||
is: { type: FilterIs },
|
||||
isEmptyArray: { type: GraphQLBoolean },
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLString } from 'graphql';
|
||||
import { GraphQLBoolean, GraphQLInputObjectType, GraphQLString } from 'graphql';
|
||||
|
||||
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const ArrayFilterType = new GraphQLInputObjectType({
|
||||
name: 'ArrayFilter',
|
||||
fields: {
|
||||
contains: { type: new GraphQLList(GraphQLString) },
|
||||
not_contains: { type: new GraphQLList(GraphQLString) },
|
||||
containsIlike: { type: GraphQLString },
|
||||
is: { type: FilterIs },
|
||||
isEmptyArray: { type: GraphQLBoolean },
|
||||
},
|
||||
});
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
import {
|
||||
GraphQLBoolean,
|
||||
GraphQLInputObjectType,
|
||||
GraphQLList,
|
||||
GraphQLString,
|
||||
} from 'graphql';
|
||||
|
||||
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const MultiSelectFilterType = new GraphQLInputObjectType({
|
||||
name: 'MultiSelectFilter',
|
||||
fields: {
|
||||
containsAny: { type: new GraphQLList(GraphQLString) },
|
||||
is: { type: FilterIs },
|
||||
isEmptyArray: { type: GraphQLBoolean },
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { GraphQLInputObjectType, GraphQLList, GraphQLString } from 'graphql';
|
||||
|
||||
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
|
||||
|
||||
export const SelectFilterType = new GraphQLInputObjectType({
|
||||
name: 'SelectFilter',
|
||||
fields: {
|
||||
in: { type: new GraphQLList(GraphQLString) },
|
||||
is: { type: FilterIs },
|
||||
},
|
||||
});
|
||||
@ -27,6 +27,8 @@ import {
|
||||
StringFilterType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input';
|
||||
import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type';
|
||||
import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type';
|
||||
import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type';
|
||||
import {
|
||||
BigFloatScalarType,
|
||||
UUIDScalarType,
|
||||
@ -115,6 +117,8 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.RAW_JSON, RawJsonFilterType],
|
||||
[FieldMetadataType.RICH_TEXT, StringFilterType],
|
||||
[FieldMetadataType.ARRAY, ArrayFilterType],
|
||||
[FieldMetadataType.MULTI_SELECT, MultiSelectFilterType],
|
||||
[FieldMetadataType.SELECT, SelectFilterType],
|
||||
[FieldMetadataType.TS_VECTOR, StringFilterType], // TODO: Add TSVectorFilterType
|
||||
]);
|
||||
|
||||
|
||||
@ -58,7 +58,9 @@ export const generateFields = <
|
||||
? {
|
||||
nullable: fieldMetadata.isNullable,
|
||||
defaultValue: fieldMetadata.defaultValue,
|
||||
isArray: fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
isArray:
|
||||
kind !== InputTypeDefinitionKind.Filter &&
|
||||
fieldMetadata.type === FieldMetadataType.MULTI_SELECT,
|
||||
settings: fieldMetadata.settings,
|
||||
isIdField: fieldMetadata.name === 'id',
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user