Add filter on array and jsonb field types (#7839)

This PR was created by [GitStart](https://gitstart.com/) to address the
requirements from this ticket:
[TWNTY-6784](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-6784).
This ticket was imported from:
[TWNTY-6784](https://github.com/twentyhq/twenty/issues/6784)

 --- 

### Description

- Add filter on array and jsonb field types
- We did not implement the contains any filter for arrays on the
frontend because we would need to change the UI design since this should
be an array of values, and now we have only one input

### Demo


<https://www.loom.com/share/0facf752b63f4120b5d4ea4ee9772d35?sid=d7bde469-e6a9-4298-a637-d81d40695a86>

Fixes #6784

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com>
Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
gitstart-app[bot]
2024-10-21 18:11:02 +02:00
committed by GitHub
parent 3f2751ef6c
commit 7b10bfa7d2
19 changed files with 277 additions and 17 deletions

View File

@ -13,6 +13,8 @@ import { FieldMetadataMap } from 'src/engine/metadata-modules/utils/generate-obj
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'];
export class GraphqlQueryFilterFieldParser {
private fieldMetadataMap: FieldMetadataMap;
@ -44,13 +46,14 @@ export class GraphqlQueryFilterFieldParser {
}
const [[operator, value]] = Object.entries(filterValue);
if (operator === 'in') {
if (!Array.isArray(value) || value.length === 0) {
throw new GraphqlQueryRunnerException(
`Invalid filter value for field ${key}. Expected non-empty array`,
GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
);
}
if (
ARRAY_OPERATORS.includes(operator) &&
(!Array.isArray(value) || value.length === 0)
) {
throw new GraphqlQueryRunnerException(
`Invalid filter value for field ${key}. Expected non-empty array`,
GraphqlQueryRunnerExceptionCode.INVALID_QUERY_INPUT,
);
}
const { sql, params } = computeWhereConditionParts(

View File

@ -61,24 +61,36 @@ export const computeWhereConditionParts = (
};
case 'like':
return {
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}"::text LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'ilike':
return {
sql: `"${objectNameSingular}"."${key}" ILIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}"::text ILIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'startsWith':
return {
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}"::text LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'endsWith':
return {
sql: `"${objectNameSingular}"."${key}" LIKE :${key}${uuid}`,
sql: `"${objectNameSingular}"."${key}"::text LIKE :${key}${uuid}`,
params: { [`${key}${uuid}`]: `${value}` },
};
case 'contains':
return {
sql: `"${objectNameSingular}"."${key}" @> ARRAY[:...${key}${uuid}]`,
params: { [`${key}${uuid}`]: value },
};
case 'not_contains':
return {
sql: `NOT ("${objectNameSingular}"."${key}" && ARRAY[:...${key}${uuid}])`,
params: { [`${key}${uuid}`]: value },
};
default:
throw new GraphqlQueryRunnerException(
`Operator "${operator}" is not supported`,

View File

@ -1,10 +1,12 @@
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 ArrayFilterType = new GraphQLInputObjectType({
name: 'ArrayFilter',
fields: {
contains: { type: new GraphQLList(GraphQLString) },
contains_any: { type: new GraphQLList(GraphQLString) },
not_contains: { type: new GraphQLList(GraphQLString) },
is: { type: FilterIs },
},
});

View File

@ -1,4 +1,4 @@
import { GraphQLInputObjectType } from 'graphql';
import { GraphQLInputObjectType, GraphQLString } from 'graphql';
import { FilterIs } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/filter-is.input-type';
@ -6,5 +6,6 @@ export const RawJsonFilterType = new GraphQLInputObjectType({
name: 'RawJsonFilter',
fields: {
is: { type: FilterIs },
like: { type: GraphQLString },
},
});