diff --git a/plugins/search-postgres/server/PostgresSearchProvider.test.ts b/plugins/search-postgres/server/PostgresSearchProvider.test.ts index ad7fba7cd1..dd16a3121b 100644 --- a/plugins/search-postgres/server/PostgresSearchProvider.test.ts +++ b/plugins/search-postgres/server/PostgresSearchProvider.test.ts @@ -1412,5 +1412,13 @@ describe("PostgresSearchProvider", () => { `"this<->is<->a<->test"` ); }); + it("should strip interleaved trailing operator and escape characters", () => { + // pg-tsquery reorders trailing operators and may emit a tail like + // `&\` that, if stripped one character at a time in the wrong order, + // leaves a dangling `&` and triggers "no operand in tsquery". + expect(PostgresSearchProvider.webSearchQuery(`"plugins"\\&`)).toBe( + `"plugins"` + ); + }); }); }); diff --git a/plugins/search-postgres/server/PostgresSearchProvider.ts b/plugins/search-postgres/server/PostgresSearchProvider.ts index c00bc091e4..8eca93125a 100644 --- a/plugins/search-postgres/server/PostgresSearchProvider.ts +++ b/plugins/search-postgres/server/PostgresSearchProvider.ts @@ -861,10 +861,10 @@ export default class PostgresSearchProvider extends BaseSearchProvider { // spaces. Ref: https://github.com/caub/pg-tsquery/issues/27 quotedSearch ? limitedQuery.trim() : `${limitedQuery.trim()}*` ) - // Remove any trailing join characters - .replace(/&$/, "") - // Remove any trailing escape characters - .replace(/\\$/, "") + // Strip any trailing join (&) or escape (\) characters, in any + // combination, so we never hand to_tsquery an operator with no + // operand (e.g. a tail of "&\" would leave a dangling "&"). + .replace(/[&\\]+$/, "") ); }