From 2ab88a6cfe8b9d16425672b7c6c36e1f5d740e7b Mon Sep 17 00:00:00 2001 From: Etienne <45695613+etiennejouan@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:26:19 +0100 Subject: [PATCH] fix slow search resolver without searchTerm input (#9947) ### Issue Empty searchTerm search resolver ``` .orderBy( `ts_rank_cd("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTerms))`, 'DESC', ) .addOrderBy( `ts_rank("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTermsOr))`, 'DESC', ) ``` builds the following SQL query: ``` ORDER BY ts_rank_cd("searchVector", to_tsquery('')) DESC, ts_rank("searchVector", to_tsquery('')) DESC ``` I haven't been able to find doc on this issue, but after testing, `ts_rank_cd("searchVector", to_tsquery(''))` slows down/freezes my local db. As well, ordering by ts_rank is useless without a searchTerm. ### Result In local, with the imported 300k rows appEvent custom object, 'combinedSearchRecords' gql request with empty searchTerm reduces exec time from 3s to 300ms. Co-authored-by: etiennejouan --- .../graphql-query-search-resolver.service.ts | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts index 16d42ef1a..0957978f0 100644 --- a/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/graphql-query-runner/resolvers/graphql-query-search-resolver.service.ts @@ -78,34 +78,41 @@ export class GraphqlQuerySearchResolverService extends GraphqlQueryBaseResolverS const countQueryBuilder = queryBuilder.clone(); - const resultsWithTsVector = (await queryBuilder - .andWhere( - new Brackets((qb) => { - qb.where( - searchTerms === '' - ? `"${SEARCH_VECTOR_FIELD.name}" IS NOT NULL` - : `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery('simple', :searchTerms)`, - searchTerms === '' ? {} : { searchTerms }, - ).orWhere( - searchTermsOr === '' - ? `"${SEARCH_VECTOR_FIELD.name}" IS NOT NULL` - : `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery('simple', :searchTermsOr)`, - searchTermsOr === '' ? {} : { searchTermsOr }, - ); - }), - ) - .orderBy( - `ts_rank_cd("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTerms))`, - 'DESC', - ) - .addOrderBy( - `ts_rank("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTermsOr))`, - 'DESC', - ) - .setParameter('searchTerms', searchTerms) - .setParameter('searchTermsOr', searchTermsOr) - .take(limit) - .getMany()) as ObjectRecord[]; + const resultsQueryBuilder = + searchTerms !== '' + ? queryBuilder + .andWhere( + new Brackets((qb) => { + qb.where( + `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery('simple', :searchTerms)`, + { searchTerms }, + ).orWhere( + `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery('simple', :searchTermsOr)`, + { searchTermsOr }, + ); + }), + ) + .orderBy( + `ts_rank_cd("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTerms))`, + 'DESC', + ) + .addOrderBy( + `ts_rank("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTermsOr))`, + 'DESC', + ) + .setParameter('searchTerms', searchTerms) + .setParameter('searchTermsOr', searchTermsOr) + .take(limit) + : queryBuilder + .andWhere( + new Brackets((qb) => { + qb.where(`"${SEARCH_VECTOR_FIELD.name}" IS NOT NULL`); + }), + ) + .take(limit); + + const resultsWithTsVector = + (await resultsQueryBuilder.getMany()) as ObjectRecord[]; const objectRecords = formatResult( resultsWithTsVector,