You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需自定义文本搜索解析器,能否用纯文本搜索替代正则表达式处理数字与单位的格式匹配问题?

无需自定义文本搜索解析器,能否用纯文本搜索替代正则表达式处理数字与单位的格式匹配问题?

首先得给你明确说:不用写C代码的自定义解析器是可以的,但完全抛弃正则预处理可能不太现实——不过我们可以把现有方案优化得更简洁,或者用更贴合PostgreSQL文本搜索生态的方式来解决你这个数字+单位(比如16GB/16 GB)、连字符(HJ-20/HJ 20)的匹配问题。

先聊聊你的现有方案:思路其实很对,就是把数字和单位之间的空格(不管有没有)统一拆成空格,连字符也换成空格,让to_tsvector能把它们识别成独立的词,这样就能正常匹配。但确实嵌套两层regexp_replace再加子查询,代码看起来有点啰嗦,而且以后如果要加新的格式规则(比如处理MB-PS这种单位连字符),正则会越来越臃肿。

先优化你的现有正则方案

其实可以把两次regexp_replace合并成一次,用正则的分支匹配一次性处理两种场景,代码会清爽很多:

select
   source
   , query
   , to_tsvector('portugues_sem_acento', source) @@ plainto_tsquery('portugues_sem_acento', query) as match
from (
   select
      regexp_replace(source, '(\d+)\s*(\D+)|(\D+)-(\d+)', '\1\3 \2\4', 'g') as source,
      regexp_replace(query, '(\d+)\s*(\D+)|(\D+)-(\d+)', '\1\3 \2\4', 'g') as query
   from (values
      ('Memória 16 GB parte 32MB HJ-20', '16 gb')
      , ('Memória 16GB parte 32 MB', '16gb')
      , ('Memória 16 GB parte 32MB mbx-12', '16gb mbx')
      , ('Memória 16GB parte 32 MB', '16 gb')
   ) ts (source, query)
) s

这个正则用|分支同时匹配「数字+可选空格+非数字」和「非数字+连字符+数字」两种情况,一次性替换成带空格的格式,效果和你原来的代码完全一样,但简洁多了。

不用嵌套子查询:把预处理逻辑封装成函数

如果以后要调整格式规则,每次改正则还要找所有用到的地方太麻烦,不如把预处理逻辑封装成一个SQL函数,这样代码更易维护:

-- 先创建你的自定义文本搜索配置(和你原来的一致)
create extension if not exists unaccent;
drop text search configuration if exists portugues_sem_acento cascade;
create text search configuration portugues_sem_acento (copy = portuguese);
alter text search configuration portugues_sem_acento
    alter mapping for asciiword, word, numword, asciihword, hword, numhword, hword_asciipart, hword_part, hword_numpart
    with unaccent, portuguese_stem;
alter database sample_db set default_text_search_config to 'portugues_sem_acento';
set default_text_search_config to 'portugues_sem_acento';

-- 封装预处理函数
create or replace function normalize_search_text(text) returns text as $$
select regexp_replace(
  $1,
  '(\d+)\s*(\D+)|(\D+)-(\d+)',
  '\1\3 \2\4',
  'g' -- 全局替换所有匹配项
);
$$ language sql immutable; -- immutable保证函数结果稳定,能被索引缓存

-- 现在查询就非常简洁了
select
  normalize_search_text(source) as source,
  normalize_search_text(query) as query,
  to_tsvector(normalize_search_text(source)) @@ plainto_tsquery(normalize_search_text(query)) as match
from (values
  ('Memória 16 GB parte 32MB HJ-20', '16 gb')
  , ('Memória 16GB parte 32 MB', '16gb')
  , ('Memória 16 GB parte 32MB mbx-12', '16gb mbx')
  , ('Memória 16GB parte 32 MB', '16 gb')
) ts (source, query);

这样以后要加新的格式处理(比如把MB-PS换成MB PS),只需要修改normalize_search_text函数里的正则就行,不用动查询逻辑。

有没有完全不用正则的纯文本搜索方式?

其实PostgreSQL默认的文本搜索解析器会把16GB这种数字+字母的混合词当成一个完整的词,而16 GB会拆成16和GB两个词,所以默认的to_tsvector是没法直接匹配这两种格式的——除非你用自定义解析器,但那需要写C代码,你明确不想这么做。

那如果不想用正则,还有一个替代方案:用pg_trgm扩展的模糊匹配。pg_trgm不关心词的边界,它通过比较字符串的 trigram(三元组)相似性来判断是否匹配,比如16gb和16 gb的相似性非常高,能被匹配到:

-- 先安装pg_trgm扩展
create extension if not exists pg_trgm;

-- 调整相似性阈值(默认0.3,你可以根据需求改)
set pg_trgm.similarity_threshold = 0.5;

select
  source,
  query,
  -- 同时用全文搜索和模糊匹配,兼顾准确性和灵活性
  (to_tsvector(source) @@ plainto_tsquery(query)) or (source % query) as match
from (values
  ('Memória 16 GB parte 32MB HJ-20', '16 gb')
  , ('Memória 16GB parte 32 MB', '16gb')
  , ('Memória 16 GB parte 32MB mbx-12', '16gb mbx')
  , ('Memória 16GB parte 32 MB', '16 gb')
) ts (source, query);

不过要注意,pg_trgm的模糊匹配可能会出现一些误匹配(比如相似但不相关的字符串),而且大数据量下性能不如全文搜索,所以最好是结合两者:用全文搜索处理大部分精确匹配的场景,用pg_trgm补漏那些格式不一致的情况。

总结最优方案

  1. 优先选择:预处理+全文搜索(封装函数):这是最务实的方案,既保持了全文搜索的高性能和准确性,又通过封装函数避免了正则代码的冗余,后期维护也方便。
  2. 替代方案:pg_trgm模糊匹配:如果不想用正则,可以尝试这种方式,但要注意调整相似性阈值,避免误匹配,同时考虑大数据量下的性能问题。
  3. 不推荐:完全不用预处理的纯文本搜索:默认解析器不支持拆分数字+字母的混合词,不用预处理的话无法准确匹配16GB和16 GB这种格式。

备注:内容来源于stack exchange,提问作者Clodoaldo Pinto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 18:14:36