Skip to content

Commit f9defb6

Browse files
authored
fix: error occurs when searching for anything containing a full stop. #1463 (#1196)
2 parents d038cec + 957ff8c commit f9defb6

4 files changed

Lines changed: 47 additions & 20 deletions

File tree

apps/backend/src/customTypes/index.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ declare module 'knex' {
3434
query: string,
3535
userInput: string
3636
): QueryBuilder;
37+
38+
whereJsonFieldLikeEscaped(
39+
column: string,
40+
field: string,
41+
userInput: string
42+
) : QueryBuilder;
43+
44+
orWhereJsonFieldLikeEscaped(
45+
column: string,
46+
field: string,
47+
userInput: string
48+
) : QueryBuilder;
3749
}
3850
}
3951
}

apps/backend/src/datasources/postgres/ProposalDataSource.ts

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,6 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
377377
return query;
378378
}
379379

380-
safeEscapeRegexExpression(expression: string): string {
381-
const escaped = expression.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
382-
383-
return escaped;
384-
}
385380
async getProposalsFromView(
386381
filter?: ProposalsFilter,
387382
first?: number,
@@ -442,10 +437,6 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
442437
searchText !== null &&
443438
searchText !== undefined
444439
) {
445-
const regexLiteral = this.safeEscapeRegexExpression(searchText);
446-
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with name equal to searchText case insensitive
447-
const jsonPathPattern = `$[*] ? (@.name.type() == "string" && @.name like_regex "${regexLiteral}" flag "i")`;
448-
449440
query.andWhere((qb) =>
450441
qb
451442
.orWhereRaw('proposal_id ILIKE ?', [`%${searchText}%`])
@@ -455,9 +446,11 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
455446
.orWhere('users.firstname', 'ilike', `%${searchText}%`)
456447
.orWhere('users.lastname', 'ilike', `%${searchText}%`)
457448
.orWhere('principal_investigator', 'in', principalInvestigator)
458-
.orWhereRaw('jsonb_path_exists(instruments, ?)', [
459-
jsonPathPattern,
460-
])
449+
.orWhereJsonFieldLikeEscaped(
450+
'instruments',
451+
'name',
452+
`${searchText}`
453+
)
461454
);
462455
}
463456

@@ -634,10 +627,10 @@ export default class PostgresProposalDataSource implements ProposalDataSource {
634627
.orWhereRaw('users.email ILIKE ?', `%${filter.text}%`)
635628
.orWhereRaw('users.firstname ILIKE ?', `%${filter.text}%`)
636629
.orWhereRaw('users.lastname ILIKE ?', `%${filter.text}%`)
637-
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with name equal to searchText case insensitive
638-
.orWhereRaw(
639-
'jsonb_path_exists(instruments, \'$[*].name \\? (@.type() == "string" && @ like_regex :searchText: flag "i")\')',
640-
{ searchText: filter.text }
630+
.orWhereJsonFieldLikeEscaped(
631+
'instruments',
632+
'name',
633+
`${filter.text}`
641634
)
642635
);
643636
}

apps/backend/src/datasources/postgres/databaseExtensions.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import Knex from 'knex';
33
const escapeLike = (s: string) =>
44
s.replace(/\\/g, '\\\\').replace(/[%_]/g, '\\$&');
55

6+
function safeJsonPath(input: string): string {
7+
return input.replace(/[.*+?^${}()|[\]\\"]/g, '\\$&').trim();
8+
}
9+
610
const addExtensions = () => {
711
// Add the custom methods
812
Knex.QueryBuilder.extend(
@@ -23,6 +27,24 @@ const addExtensions = () => {
2327
return this.orWhereILike(column, finalQuery);
2428
}
2529
);
30+
Knex.QueryBuilder.extend(
31+
'whereJsonFieldLikeEscaped',
32+
function (column: string, field: string, userInput: string) {
33+
const escapedInput = safeJsonPath(userInput);
34+
const jsonPath = `$[*].${field}? (@ like_regex "${escapedInput}" flag "i")`;
35+
36+
return this.whereRaw('jsonb_path_exists(??, ?)', [column, jsonPath]);
37+
}
38+
);
39+
Knex.QueryBuilder.extend(
40+
'orWhereJsonFieldLikeEscaped',
41+
function (column: string, field: string, userInput: string) {
42+
const escapedInput = safeJsonPath(userInput);
43+
const jsonPath = `$[*].${field}? (@ like_regex "${escapedInput}" flag "i")`;
44+
45+
return this.orWhereRaw('jsonb_path_exists(??, ?)', [column, jsonPath]);
46+
}
47+
);
2648
};
2749

2850
export default addExtensions;

apps/backend/src/datasources/stfc/StfcProposalDataSource.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ export default class StfcProposalDataSource extends PostgresProposalDataSource {
129129
.orWhere('users.firstname', 'ilike', `%${filter.text}%`)
130130
.orWhere('users.lastname', 'ilike', `%${filter.text}%`)
131131
.orWhere('principal_investigator', 'in', stfcUserIds)
132-
// NOTE: Using jsonpath we check the jsonb (instruments) field if it contains object with name equal to searchText case insensitive
133-
.orWhereRaw(
134-
'jsonb_path_exists(instruments, \'$[*].name \\? (@.type() == "string" && @ like_regex :searchText: flag "i")\')',
135-
{ searchText: filter.text }
132+
.orWhereJsonFieldLikeEscaped(
133+
'instruments',
134+
'name',
135+
`${filter.text}`
136136
);
137137
});
138138
}

0 commit comments

Comments
 (0)