使用连字符和数字进行 Postgres 全文搜索

Postgres Full-Text Search with Hyphen and Numerals

我观察到在我看来 Postgres 的 to_tsvector 函数有一个奇怪的行为。

SELECT to_tsvector('english', 'abc-xyz');

returns

'abc':2 'abc-xyz':1 'xyz':3

然而,

SELECT to_tsvector('english', 'abc-001');

returns

'-001':2 'abc':1

为什么不是这样的?

'abc':2 'abc-001':1 '001':3

我应该怎么做才能仅按数字部分进行搜索,而不使用连字符?

文本搜索解析器似乎将连字符后跟数字识别为有符号整数符号。使用 ts_debug():

进行调试
SELECT * FROM ts_debug('english', 'abc-001');

   alias   |   description   | token | dictionaries | dictionary | lexemes 
-----------+-----------------+-------+--------------+------------+---------
 asciiword | Word, all ASCII | abc   | {simple}     | simple     | {abc}
 int       | Signed integer  | -001  | {simple}     | simple     | {-001}

其他文本搜索配置(例如 'simple' 而不是 'english')将无济于事,因为解析器本身是 "at fault" 这里(值得商榷)。

一个简单的解决方法(除了修改解析器,我从未尝试过)是预处理字符串并将连字符替换为 m-dash () 或只是空白以确保它们是标识为 "Space symbols"。 (实际有符号整数在此过程中会丢失负号。)

SELECT to_tsvector('english', translate('abc-001', '-', '—'))
    @@ to_tsquery ('english', '001');  -- true now

db<>fiddle here

这可以通过 PG13 的 dict-int 插件的 absval 选项来规避。参见 the official documentation

但如果您受困于较早的 PG 版本,这里是查询中“数字或负数”解决方法的通用版本。

select regexp_replace($$'test' & '1':* & '2'$$::tsquery::text,
            '''([.\d]+''(:\*)?)', '('' | ''-)', 'g')::tsquery;

这导致:

'test' & ( '1':* | '-1':* ) & ( '2' | '-2' )

它将看起来像正数的词素替换为“数字或负数”类型的子查询。
双转换 ::tsquery::text 只是为了展示如何将转换为文本的 tsquery 传递。
请注意,它也处理前缀匹配数字词素。