Rails 中如何过滤 Unicode 搜索字符并保留越南语?
结论
不要按“字符码值高于某个阈值就删除”的规则处理。不同语言的 Unicode 字母分布在多个区段,越南语的声调字符也可能由组合符号表示。按数值截断会误删有效文字。
在 Rails 端,可以保留 Unicode 字母、组合标记和空白,并单独移除数学字母数字符号区段 U+1D400 到 U+1D7FF。𝓵𝓲𝓵𝓵𝔂𝓬𝓻𝓸𝓯𝓽 中的字符就在这个区段。过滤后,如果搜索词为空,就跳过 MATCH 查询。
Ruby 过滤示例
def sanitize_search_term(value)
value.to_s
.gsub(/[\u{1D400}-\u{1D7FF}]/u, "")
.gsub(/[^\p{L}\p{M}\s]/u, " ")
.gsub(/\s+/, " ")
.strip
end
这里的 \p{L} 用来保留 Unicode 字母,\p{M} 保留组合标记,后者对某些分解形式的重音字符有用。第二次替换会把其余字符(包括布尔全文搜索运算符 +)换成空格。代码不会把数学字母转换成普通拉丁字母;这个示例会直接删除它们。
使用结果前,先检查搜索词是否为空:
term = sanitize_search_term(params[:q])
if term.present?
# 在这里构造 BOOLEAN MODE 查询并执行搜索
else
# 不执行 MATCH 查询,按应用需要返回空结果
end
如果搜索逻辑会给词项添加 +,应先过滤并确认词项非空,再构造布尔查询。将构造好的查询字符串作为 SQL bind 参数传入 AGAINST (?),不要直接插入 SQL 文本。参数绑定可以避免把输入当作 SQL 语法,但 BOOLEAN MODE 仍会解析参数内容,所以过滤和空值检查仍有必要。
越南语与边界
上述规则会保留 trường đại học 中的越南语字母和声调标记。不过,搜索能否按预期匹配,还取决于 MySQL 的字符集、排序规则、全文索引解析器和索引配置。现有日志不足以确认这些配置,因此应使用与生产环境相同的配置验证目标词的搜索结果。
这套规则只针对日志中的数学字母数字符号,不是完整的 Unicode 反混淆方案。Unicode 没有通用的数值分界,能可靠地区分所有语言中的“普通字母”和装饰性字形。如果还要处理其他相似字符,应根据具体输入补充明确的区段或策略。也不要用 NFKC 解决这个问题:它可能把装饰字形转换成普通字母,而这里需要丢弃这类输入。
备注:内容仅供参考。
