Solr含撇号的名称搜索仅部分生效问题排查求助
嘿,我来帮你排查这个问题!你的场景很典型——撇号在分词过程中搞出了小麻烦,咱们一步步来拆解和解决。
问题根源分析
先看你的trname字段配置,核心问题出在WordDelimiterFilterFactory的行为上:
- 索引阶段:当处理
O'Brien时,这个过滤器会把撇号当作单词分隔符,拆分出o、brien,同时因为catenateWords=1,还会拼接出obrien。所以你的索引里会有这三个token。 - 查询阶段:当你输入
O'B时,查询分析器里的WordDelimiterFilterFactory设置了catenateWords=0,所以会把o'b拆成o和b两个独立token。这时候Solr执行的是o AND b的搜索,但你的索引里只有o和brien,没有b这个token,自然搜不到结果。 - 而搜索
O'时,拆分后只有o,能匹配所有含o的文档;搜索完整O'Brien时,拆分出o和brien,正好匹配索引里的token,所以正常返回结果。
可行解决方案
这里给你几个针对性的方案,你可以根据自己的需求选择:
方案1:让查询阶段保留原始分词(最简单的快速修复)
修改查询分析器中的WordDelimiterFilterFactory,添加preserveOriginal="1"参数。这样过滤器会保留o'b作为一个完整token,同时生成拆分后的o和b。修改后的配置如下:
<filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1" preserveOriginal="1"/>
这样查询O'B时,会同时用o'b、o、b去匹配索引。不过这里有个小问题:索引里的拼接token是obrien(不带撇号),而o'b是带撇号的,可能匹配不上,所以建议结合方案2一起用。
方案2:自定义规则,让撇号不被当作分隔符
创建一个worddelimitertypes.txt文件(放在Solr的配置目录下),内容如下:
' => ALPHA
这个配置告诉过滤器:撇号属于字母类,不会被当作分隔符拆分。然后在索引和查询阶段的WordDelimiterFilterFactory中添加types="worddelimitertypes.txt"参数:
<!-- 索引阶段的过滤器配置 --> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1" types="worddelimitertypes.txt"/> <!-- 查询阶段的过滤器配置 --> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1" types="worddelimitertypes.txt"/>
这样处理后,O'Brien会被当作一个完整的token(经过LowerCase后变成o'brien),查询O'B时会生成o'b,这时候如果想要部分匹配生效,还需要添加前缀分词的支持。
方案3:添加前缀分词(EdgeNGram)实现部分匹配
如果需要支持任意前缀的部分匹配(比如O'B、O'Br等),可以在索引阶段添加EdgeNGramFilterFactory,生成前缀token。修改索引分析器的配置:
<analyzer type="index"> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" format="wordset" /> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1" types="worddelimitertypes.txt"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.EdgeNGramFilterFactory" minGramSize="2" maxGramSize="15"/> <filter class="solr.RemoveDuplicatesTokenFilterFactory"/> </analyzer>
这个过滤器会把o'brien拆分成ob、obr、obri...obrien等前缀token,这样查询O'B生成的o'b就能匹配到对应的前缀了。注意:添加这个过滤器会增加索引大小,需要根据你的数据量权衡。
方案4:直接移除撇号(最简洁的方案)
如果你的业务不需要保留撇号,可以在索引和查询阶段都添加PatternReplaceFilterFactory,把撇号直接去掉:
<!-- 在LowerCaseFilterFactory之前添加 --> <filter class="solr.PatternReplaceFilterFactory" pattern="'" replacement="" replace="all"/>
这样O'Brien会变成obrien,O'B变成ob,查询ob就能直接匹配到obrien的前缀,完美解决部分匹配问题。
验证建议
修改配置后,记得重启Solr并重新索引数据,然后用Solr的Analysis页面测试分词效果,确认索引和查询的token是否匹配。
内容的提问来源于stack exchange,提问作者K. Meke

