PostgreSQL中tsquery前缀词法单元(a:*等)的异常行为问题
哈哈,这个问题我太熟了!你遇到的a、i、s、t可不是什么特殊字符,它们是PostgreSQL默认全文搜索配置里的停用词(stop words)——就是那些因为太常用,全文搜索默认会忽略的小词。
为什么会出现警告和慢查询?
当你执行to_tsquery('a:*')时,PostgreSQL会把a识别成停用词直接忽略,导致你的查询变成了一个空的tsquery(相当于没有任何过滤条件)。这时候数据库只能对物化视图mv_fulltextsearch1做全表扫描,自然耗时极长,同时会输出提示:
NOTICE: text-search query contains only stop words or doesn't contain lexemes, ignored
而b不在默认停用词列表里,所以to_tsquery('b:*')能正常解析成有效的前缀查询,可以利用全文索引,因此速度正常。
修复方法
下面给你几种可行的解决方案,按需选择:
1. 临时查询时跳过停用词
如果你只是偶尔需要查这些停用词的前缀,可以在to_tsvector和to_tsquery里指定忽略停用词的配置参数:
SELECT id FROM mv_fulltextsearch1 WHERE to_tsvector('english', text) @@ to_tsquery('english', 'a:*', 'stopwords=none') LIMIT 50;
这里的'english'是你当前使用的文本搜索配置(可以用SHOW default_text_search_config;查看),'stopwords=none'会临时禁用停用词过滤。
2. 创建自定义文本搜索配置(永久解决)
如果你经常需要查询这类停用词的前缀,建议创建一个自定义的文本搜索配置,移除这些停用词:
- 第一步:复制现有的默认配置(比如
english)CREATE TEXT SEARCH CONFIGURATION public.my_custom_config (COPY = pg_catalog.english); - 第二步:创建新的停用词表,移除
a、i、s、t
先查看原停用词表的内容:
然后创建新的停用词表,导入原停用词但去掉目标词:SELECT * FROM pg_catalog.english_stops;CREATE TEXT SEARCH STOPLIST public.my_custom_stops; -- 这里把原停用词表里除了a、i、s、t的词都加进去,比如: ALTER TEXT SEARCH STOPLIST public.my_custom_stops ADD 'the', 'and', 'of', 'to', ...; - 第三步:关联自定义配置和新停用词表
ALTER TEXT SEARCH CONFIGURATION public.my_custom_config ALTER MAPPING FOR asciiword, word WITH english_stem, public.my_custom_stops; - 第四步:使用自定义配置查询
SELECT id FROM mv_fulltextsearch1 WHERE to_tsvector('public.my_custom_config', text) @@ to_tsquery('public.my_custom_config', 'a:*') LIMIT 50;
3. 使用simple文本搜索配置
如果你不需要词干提取(比如只需要精确匹配前缀),可以直接用simple配置——它没有停用词列表,所有词都会被保留:
SELECT id FROM mv_fulltextsearch1 WHERE to_tsvector('simple', text) @@ to_tsquery('simple', 'a:*') LIMIT 50;
注意:simple配置不会对词汇做词干化处理,所以'a:*'只会匹配以a开头的原始词汇,而不会匹配apple这类词的词干形式。
内容的提问来源于stack exchange,提问作者Shinigami

