mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 20:13:21 +03:00
Improve search algorithm (#7955)
We were previously checking for matching with each search term independently. Ex searching for "felix malfait" we were searching for correspondances with "felix" and "malfait". As a result record A with name "Marie-Claude Mala" and email "ma.lala@email.com" had a biggest search score than record B "Felix Malfait" with email felix@email.com for search "felix ma": for record A we had 0 match with felix and 3 matches with "ma" ("marie", "mala", "ma") for record B we had 1 match with felix and 1 match with "ma" (with "malfait"). So we want to give more weight to a row that would combine matches with both terms, considering "felix malfait" altogether.
This commit is contained in:
parent
e767f16dbe
commit
f0a2d38471
@ -13,7 +13,6 @@ import { SearchResolverArgs } from 'src/engine/api/graphql/workspace-resolver-bu
|
||||
import { QUERY_MAX_RECORDS } from 'src/engine/api/graphql/graphql-query-runner/constants/query-max-records.constant';
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { ObjectRecordsToGraphqlConnectionHelper } from 'src/engine/api/graphql/graphql-query-runner/helpers/object-records-to-graphql-connection.helper';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/constants/search-vector-field.constants';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { isDefined } from 'src/utils/is-defined';
|
||||
@ -24,7 +23,6 @@ export class GraphqlQuerySearchResolverService
|
||||
{
|
||||
constructor(
|
||||
private readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
private readonly featureFlagService: FeatureFlagService,
|
||||
) {}
|
||||
|
||||
async resolve<
|
||||
@ -61,7 +59,8 @@ export class GraphqlQuerySearchResolverService
|
||||
hasPreviousPage: false,
|
||||
});
|
||||
}
|
||||
const searchTerms = this.formatSearchTerms(args.searchInput);
|
||||
const searchTerms = this.formatSearchTerms(args.searchInput, 'and');
|
||||
const searchTermsOr = this.formatSearchTerms(args.searchInput, 'or');
|
||||
|
||||
const limit = args?.limit ?? QUERY_MAX_RECORDS;
|
||||
|
||||
@ -86,11 +85,22 @@ export class GraphqlQuerySearchResolverService
|
||||
: `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery(:searchTerms)`,
|
||||
searchTerms === '' ? {} : { searchTerms },
|
||||
)
|
||||
.orWhere(
|
||||
searchTermsOr === ''
|
||||
? `"${SEARCH_VECTOR_FIELD.name}" IS NOT NULL`
|
||||
: `"${SEARCH_VECTOR_FIELD.name}" @@ to_tsquery(:searchTermsOr)`,
|
||||
searchTermsOr === '' ? {} : { searchTermsOr },
|
||||
)
|
||||
.orderBy(
|
||||
`ts_rank("${SEARCH_VECTOR_FIELD.name}", to_tsquery(:searchTerms))`,
|
||||
`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[];
|
||||
|
||||
@ -110,7 +120,10 @@ export class GraphqlQuerySearchResolverService
|
||||
});
|
||||
}
|
||||
|
||||
private formatSearchTerms(searchTerm: string) {
|
||||
private formatSearchTerms(
|
||||
searchTerm: string,
|
||||
operator: 'and' | 'or' = 'and',
|
||||
) {
|
||||
if (searchTerm === '') {
|
||||
return '';
|
||||
}
|
||||
@ -121,7 +134,7 @@ export class GraphqlQuerySearchResolverService
|
||||
return `${escapedWord}:*`;
|
||||
});
|
||||
|
||||
return formattedWords.join(' | ');
|
||||
return formattedWords.join(` ${operator === 'and' ? '&' : '|'} `);
|
||||
}
|
||||
|
||||
async validate(
|
||||
|
Loading…
Reference in New Issue
Block a user