feat: Add TS vector field filters support (#12376)
# Implementation Details - Added support for 5 operators: `contains`, `containsAny`, `containsAll`, `matches`, and `fuzzy` - Works on any field of type `TS_VECTOR` - Added PostgreSQL `pg_trgm` extension for fuzzy search functionality. The extension provides the `similarity()` function needed for text similarity searches. - Not implemented in GraphQL ## Tradeoffs & Decisions 1. **Fuzzy Search Performance**: Using `pg_trgm` for fuzzy search is more accurate but slower than simple text matching. We might want to add a similarity threshold parameter in the future to control the tradeoff between accuracy and performance. 2. **Operator Naming**: Chose `contains`/`containsAny`/`containsAll` to be consistent with existing filter operators, though they might be less intuitive than `search`/`searchAny`/`searchAll`. ## Demo https://github.com/user-attachments/assets/790fc3ed-a188-4b49-864f-996a37481d99 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
+8
@@ -0,0 +1,8 @@
|
||||
import { GraphQLInputObjectType, GraphQLString } from 'graphql';
|
||||
|
||||
export const TSVectorFilterType = new GraphQLInputObjectType({
|
||||
name: 'TSVectorFilter',
|
||||
fields: {
|
||||
search: { type: GraphQLString },
|
||||
},
|
||||
});
|
||||
+5
@@ -5,13 +5,17 @@ import { DateScalarType } from './date.scalar';
|
||||
import { PositionScalarType } from './position.scalar';
|
||||
import { RawJSONScalar } from './raw-json.scalar';
|
||||
import { TimeScalarType } from './time.scalar';
|
||||
import { TSVectorScalarType } from './ts-vector.scalar';
|
||||
import { UUIDScalarType } from './uuid.scalar';
|
||||
|
||||
export * from './big-float.scalar';
|
||||
export * from './big-int.scalar';
|
||||
export * from './cursor.scalar';
|
||||
export * from './date.scalar';
|
||||
export * from './position.scalar';
|
||||
export * from './raw-json.scalar';
|
||||
export * from './time.scalar';
|
||||
export * from './ts-vector.scalar';
|
||||
export * from './uuid.scalar';
|
||||
|
||||
export const scalars = [
|
||||
@@ -23,4 +27,5 @@ export const scalars = [
|
||||
CursorScalarType,
|
||||
PositionScalarType,
|
||||
RawJSONScalar,
|
||||
TSVectorScalarType,
|
||||
];
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { GraphQLScalarType } from 'graphql';
|
||||
|
||||
export const TSVectorScalarType = new GraphQLScalarType({
|
||||
name: 'TSVector',
|
||||
description: 'A custom scalar type for PostgreSQL tsvector fields',
|
||||
serialize(value: string): string {
|
||||
return value;
|
||||
},
|
||||
parseValue(value: string): string {
|
||||
return value;
|
||||
},
|
||||
parseLiteral(ast): string | null {
|
||||
if (ast.kind === 'StringValue') {
|
||||
return ast.value;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
+4
-2
@@ -30,9 +30,11 @@ import {
|
||||
import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type';
|
||||
import { RichTextV2FilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type';
|
||||
import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type';
|
||||
import { TSVectorFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/ts-vector-filter.input-type';
|
||||
import { UUIDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/uuid-filter.input-type';
|
||||
import {
|
||||
BigFloatScalarType,
|
||||
TSVectorScalarType,
|
||||
UUIDScalarType,
|
||||
} from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar';
|
||||
@@ -83,7 +85,7 @@ export class TypeMapperService {
|
||||
StringArrayScalarType as unknown as GraphQLScalarType,
|
||||
],
|
||||
[FieldMetadataType.RICH_TEXT, GraphQLString],
|
||||
[FieldMetadataType.TS_VECTOR, GraphQLString],
|
||||
[FieldMetadataType.TS_VECTOR, TSVectorScalarType],
|
||||
]);
|
||||
|
||||
return typeScalarMapping.get(fieldMetadataType);
|
||||
@@ -122,7 +124,7 @@ export class TypeMapperService {
|
||||
[FieldMetadataType.ARRAY, ArrayFilterType],
|
||||
[FieldMetadataType.MULTI_SELECT, MultiSelectFilterType],
|
||||
[FieldMetadataType.SELECT, SelectFilterType],
|
||||
[FieldMetadataType.TS_VECTOR, StringFilterType], // TODO: Add TSVectorFilterType
|
||||
[FieldMetadataType.TS_VECTOR, TSVectorFilterType],
|
||||
]);
|
||||
|
||||
return typeFilterMapping.get(fieldMetadataType);
|
||||
|
||||
Reference in New Issue
Block a user