Skip to content

Commit 6869a78

Browse files
feat: enhanced multi-word search functionality (#1001)
1 parent 52fe0d6 commit 6869a78

File tree

2 files changed

+78
-24
lines changed

2 files changed

+78
-24
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,15 @@ const paginateConfig: PaginateConfig<CatEntity> {
296296
* Description: Prevent `select` query param from limiting selection further. Partial selection will depend upon `select` config option only
297297
*/
298298
ignoreSelectInQueryParam: true,
299+
300+
/**
301+
* Required: false
302+
* Type: boolean
303+
* Default: false
304+
* Description: Enable multi-word search behavior. When true, each word in the search query
305+
* will be treated as a separate search term, allowing for more flexible matching.
306+
*/
307+
multiWordSearch: false,
299308
}
300309
```
301310

src/paginate.ts

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export interface PaginateConfig<T> {
9090
origin?: string
9191
ignoreSearchByInQueryParam?: boolean
9292
ignoreSelectInQueryParam?: boolean
93+
multiWordSearch?: boolean
9394
}
9495

9596
export enum PaginationLimit {
@@ -352,31 +353,75 @@ export async function paginate<T extends ObjectLiteral>(
352353
if (query.search && searchBy.length) {
353354
queryBuilder.andWhere(
354355
new Brackets((qb: SelectQueryBuilder<T>) => {
355-
for (const column of searchBy) {
356-
const property = getPropertiesByColumnName(column)
357-
const { isVirtualProperty, query: virtualQuery } = extractVirtualProperty(qb, property)
358-
const isRelation = checkIsRelation(qb, property.propertyPath)
359-
const isEmbeded = checkIsEmbedded(qb, property.propertyPath)
360-
const alias = fixColumnAlias(
361-
property,
362-
qb.alias,
363-
isRelation,
364-
isVirtualProperty,
365-
isEmbeded,
366-
virtualQuery
367-
)
368-
369-
const condition: WherePredicateOperator = {
370-
operator: 'ilike',
371-
parameters: [alias, `:${property.column}`],
356+
// Explicitly handle the default case - multiWordSearch defaults to false
357+
const useMultiWordSearch = config.multiWordSearch ?? false
358+
if (!useMultiWordSearch) {
359+
// Strict search mode (default behavior)
360+
for (const column of searchBy) {
361+
const property = getPropertiesByColumnName(column)
362+
const { isVirtualProperty, query: virtualQuery } = extractVirtualProperty(qb, property)
363+
const isRelation = checkIsRelation(qb, property.propertyPath)
364+
const isEmbedded = checkIsEmbedded(qb, property.propertyPath)
365+
const alias = fixColumnAlias(
366+
property,
367+
qb.alias,
368+
isRelation,
369+
isVirtualProperty,
370+
isEmbedded,
371+
virtualQuery
372+
)
373+
374+
const condition: WherePredicateOperator = {
375+
operator: 'ilike',
376+
parameters: [alias, `:${property.column}`],
377+
}
378+
379+
if (['postgres', 'cockroachdb'].includes(queryBuilder.connection.options.type)) {
380+
condition.parameters[0] = `CAST(${condition.parameters[0]} AS text)`
381+
}
382+
383+
qb.orWhere(qb['createWhereConditionExpression'](condition), {
384+
[property.column]: `%${query.search}%`,
385+
})
372386
}
373-
374-
if (['postgres', 'cockroachdb'].includes(queryBuilder.connection.options.type)) {
375-
condition.parameters[0] = `CAST(${condition.parameters[0]} AS text)`
376-
}
377-
378-
qb.orWhere(qb['createWhereConditionExpression'](condition), {
379-
[property.column]: `%${query.search}%`,
387+
} else {
388+
// Multi-word search mode
389+
const searchWords = query.search.split(' ').filter((word) => word.length > 0)
390+
searchWords.forEach((searchWord, index) => {
391+
qb.andWhere(
392+
new Brackets((subQb: SelectQueryBuilder<T>) => {
393+
for (const column of searchBy) {
394+
const property = getPropertiesByColumnName(column)
395+
const { isVirtualProperty, query: virtualQuery } = extractVirtualProperty(
396+
subQb,
397+
property
398+
)
399+
const isRelation = checkIsRelation(subQb, property.propertyPath)
400+
const isEmbedded = checkIsEmbedded(subQb, property.propertyPath)
401+
const alias = fixColumnAlias(
402+
property,
403+
subQb.alias,
404+
isRelation,
405+
isVirtualProperty,
406+
isEmbedded,
407+
virtualQuery
408+
)
409+
410+
const condition: WherePredicateOperator = {
411+
operator: 'ilike',
412+
parameters: [alias, `:${property.column}_${index}`],
413+
}
414+
415+
if (['postgres', 'cockroachdb'].includes(queryBuilder.connection.options.type)) {
416+
condition.parameters[0] = `CAST(${condition.parameters[0]} AS text)`
417+
}
418+
419+
subQb.orWhere(subQb['createWhereConditionExpression'](condition), {
420+
[`${property.column}_${index}`]: `%${searchWord}%`,
421+
})
422+
}
423+
})
424+
)
380425
})
381426
}
382427
})

0 commit comments

Comments
 (0)