MongoDB/Mongoid非标准字符搜索匹配配置咨询
解决MongoDB/Mongoid匹配带重音字符的问题
嘿,这个问题我之前处理过类似的情况,MongoDB默认的字符串匹配是严格按二进制来的,不像MySQL那样默认忽略重音差异。不过不用全文搜索也能解决,而且考虑到你用的是MongoDB 2.4.14这个比较老的版本,给你两个实用的方案:
方案1:预存归一化后的姓氏字段(最稳妥,适配你的MongoDB版本)
因为MongoDB 2.4.14不支持后续版本才有的collation特性,所以最靠谱的办法是在保存用户时,把姓氏转换成无重音的标准化版本,存在额外字段里,搜索时直接匹配这个字段。
步骤:
- 给User模型添加一个
last_name_normalized字段:
class User include Mongoid::Document field :last_name, type: String field :last_name_normalized, type: String end
- 添加回调,在保存前自动生成归一化字段:
before_save :normalize_last_name private def normalize_last_name # 方法一:用Rails自带的I18n转写 self.last_name_normalized = I18n.transliterate(last_name).downcase # 方法二:用unidecode gem(推荐,转写更准确) # 先在Gemfile里加 gem 'unidecode',执行bundle install后使用 # self.last_name_normalized = last_name.to_ascii.downcase end
- 搜索时,先把输入的字符串也做同样的归一化处理:
search_term = I18n.transliterate('Lordon').downcase # 用unidecode的话:search_term = 'Lordon'.to_ascii.downcase User.where(last_name_normalized: search_term)
这样不管用户存的是Lordôn还是Lordon,归一化后都会变成lordon,就能实现匹配了。
方案2:正则表达式匹配(仅小数据集适用)
如果不想加额外字段,也可以用正则表达式配合字符类来匹配,但这个方法在数据量大的时候性能很差(无法利用索引),而且需要手动处理每个可能的重音字符:
# 把带重音的字符替换成包含原字符和重音字符的字符类 normalized_regex = /^Lordo[nñ]$/i User.where(last_name: normalized_regex)
这个方法比较繁琐,只适合数据量很小的场景,更推荐方案1。
补充说明
MySQL默认的排序规则(比如utf8_general_ci)会自动忽略重音和大小写差异,但MongoDB默认是二进制比较,所以必须手动处理归一化或者用特定匹配规则。由于你的MongoDB版本太低,无法使用3.2+才支持的collation(可以通过collation: { locale: 'en', strength: 1 }来忽略重音),所以方案1是最适合你的选择。
内容的提问来源于stack exchange,提问作者RobL
相关产品推荐
相关产品推荐

