设为首页 收藏本站
查看: 1111|回复: 0

[经验分享] Apache Solr使用自定义QParser后同义词扩展及Token去重的感悟

[复制链接]

尚未签到

发表于 2015-7-16 11:25:56 | 显示全部楼层 |阅读模式
  好久没写博客了。近期在用solr做一套系统,期间有不少心得尚未记录。这里先记录一下solr中自定义QParser如何与SynonymFilter和RemoveDuplicatesTokenFilter配合以实现检索时Token同义词扩展与Token去重。
  起初按照solr wiki上的说明,在schema.xml里配置了如下filter:





1

2

3

4

5

6

7
  但是在实际使用过程中,发现RemoveDuplicatesTokenFilterFactory并未能过滤掉重复的Token,例如:“摩托罗拉 motorola 里程碑2代”,经过同义词扩展后(此处的同义词扩展为品牌中英文扩展,下同)变成了“摩托罗拉 摩托 motorola moto motorola 摩托罗拉 摩托 moto 里程碑 2代”,其中的【摩托罗拉】、【摩托】、【motorola】、【moto】都重复了一次。而我使用了基于DisMaxQParser的自定义Qparser,因此扩展后的同义词会对min-should-match参数带来影响,降低匹配精度。
  为了看看究竟为何RemoveDuplicatesTokenFilter不起作用,打开它的源码看了一下:





01@Override

02public boolean incrementToken() throws IOException {

03while (input.incrementToken()) {

04final char term[] = termAttribute.buffer();

05final int length = termAttribute.length();

06final int posIncrement = posIncAttribute.getPositionIncrement();

07if (posIncrement > 0) {

08previous.clear();

09}

10boolean duplicate = (posIncrement == 0 && previous.contains(term, 0, length));

11// clone the term, and add to the set of seen terms.

12char saved[] = new char[length];

13System.arraycopy(term, 0, saved, 0, length);

14previous.add(saved);

15if (!duplicate) {

16return true;

17}

18}

19return false;

20}
  可以看出来,RemoveDuplicatesTokenFilter只对positionIncrement为0的token进行判断是否重复;但是,经过SynonymFilter扩展出的同义词,虽然positionIncrement为0,但肯定不会与原Token重复的,后面可能出现的重复的Token则因为positionIncrement必然大于0而导致无法去重了。
  针对这种情况,决定从自定义的QParser入手,采用以下思路来解决问题:

  • 想办法在QParser中获得Solr的TokenizerChain,从中获取SynonymFilterFactory
  • 在QParser中取得分词的Analyzer,并通过Analyzer的TokenStream构建SynonymFilter实例
  • 通过SynonymFilter遍历Token(调用incrementToken方法),并针对同义词扩展的positionIncrement进行逻辑判断:

    • 若positionIncrement>0,则判断该词是否已经出现过,未出现则放行,并放在Set中待下次判断是否重复
    • 若positionIncrement==0,则只放在Set中供下次判断

  经过这样的处理逻辑,实际上除了过滤掉了重复的Token,还完成了Token“归一化”的过程。因为自定义QParser在solr检索的生命周期中要先于schema.xml中配置的TokenizerChain,因此在归一化之后,还会再进行一次同义词扩展,扩展之后,不会出现重复的Token,也不影响检索的精度了。
  部分代码如下:





01Analyzer analyzer = req.getSchema().getQueryAnalyzer();

02final TokenizerChain tokennizerChain = (TokenizerChain) req.getSchema().getField("title").getType().getQueryAnalyzer();

03SynonymFilterFactory sff = null;

04for (TokenFilterFactory tf : tokennizerChain.getTokenFilterFactories()) {

05if (tf instanceof SynonymFilterFactory) {

06sff = (SynonymFilterFactory) tf;

07}

08}

09if (null == analyzer) {

10return;

11}

12…………

13StringReader reader = new StringReader(qstr);

14StringBuilder buffer = new StringBuilder(128);

15Set tokenSet = new LinkedHashSet();

16…………

17TokenStream tokens = analyzer.reusableTokenStream("title", reader);

18SynonymFilter sf = sff.create(tokens);

19sf.reset();

20TermAttribute termAtt = (TermAttribute) sf.getAttribute(TermAttribute.class);

21PositionIncrementAttribute positionIncrementAttribute = sf.getAttribute(PositionIncrementAttribute.class);

22OffsetAttribute offsetAttribute = sf.getAttribute(OffsetAttribute.class);

23Set dumplicatedTokenSet = new HashSet();

24while (sf.incrementToken()) {

25final String token = (new String(termAtt.termBuffer(), 0, termAtt.termLength())).toLowerCase();

26final int posIncr = positionIncrementAttribute.getPositionIncrement();

27if (posIncr > 0) {

28if (!dumplicatedTokenSet.contains(token)) {

29dumplicatedTokenSet.add(token);

30tokenSet.add(token);

31}

32} else {

33dumplicatedTokenSet.add(token);

34}

35}

36…………

37for (String tok : tokenSet) {

38buffer.append(tok).append(" ");

39}

40if (buffer.length() > 0) {

41qstr = buffer.toString();

42}
  后记:
solr的DisjunctionMaxQuery是个很有意思的东西,抽时间好好看一下代码,总结一下。
  摘自:http://www.jnan.org/archives/2011/10/hacking-solr-synonymfilter-and-removeduplicatestokenfilter-with-custom-qparser.html#more-528
  
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-87257-1-1.html 上篇帖子: Solr系列:Linux下部署Solr 下篇帖子: EasyNet.Solr系列:处理xml或者json格式返回数据
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表