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

[经验分享] 自动提示功能实现:solr中TermsComponent源代码分析

[复制链接]

尚未签到

发表于 2015-11-12 05:09:53 | 显示全部楼层 |阅读模式

  • 一个曾实现的简单思路:
  自动提示功能,以前的有一种实现思路就是在数据库里建一张表,其主要字段有:
  keyword-检索关键字;kcount-检索次数;dissect_word-对检索关键字分词后的结果;kdate:检索时间
  由于用户输入的检索关键字可能很乱,又可能很杂,所以想到通过分词器把检索关键字进行分词处理,若数据库中在dissect_word中找到含有相同的值则认为是检索相同的关键字,kcount+1.
  当用户检索“lucene”的时候就根据keyword like "lucene%" order by kcount desc 来展示给前台。
  虽然看起来像是那么回事了,但是没有专业性。今天研究了一下solr的自动提示组件TermsComponent类文件。

  • solr中TermsComponent源代码:
  为了便于大家理解可以先执行下面的测试代码:
FSDirectory directory = FSDirectory.open(new File("D://DATAMANAGER//INDEX//SYS_3000"));
  IndexReader r = IndexReader.open(directory);
  
  //TermEnum te = r.terms();  //测试1
  TermEnum te = r.terms(new Term("content","防")); //测试2
  while(te.next()){
   Term t = te.term();
   if(t.field().equals("content")){
    System.out.print(t.field()+"="+t.text()+":"+te.docFreq()+";");
   }
  }  分别执行一下“测试1”和“测试2”,查看一下打印输出结果。其中“D://DATAMANAGER//INDEX//SYS_3000”换成你的索引文件目录;“content”为Field域字段;“”为content域字段的一个term的text值,你可以根据你自己的环境相应替换这些值。
  TermsComponent主要理解了一下它的思路,在代码上加了些注释。
  public static final int UNLIMITED_MAX_COUNT = -1;
  //例如,请求参数串:terms=true&terms.fl=name&terms.lower=py&terms.prefix=py&terms.lower.incl=false&indent=true&wt=json
  public void process(ResponseBuilder rb) throws IOException {
    SolrParams params = rb.req.getParams();
    if (params.getBool(TermsParams.TERMS, false)) {//判断请求传来的参数terms=true
      String lowerStr = params.get(TermsParams.TERMS_LOWER, null);//开始Term terms.lower=py
      String[] fields = params.getParams(TermsParams.TERMS_FIELD); //在哪个域 terms.fl=name
      if (fields != null && fields.length > 0) {
        NamedList terms = new NamedList();
        rb.rsp.add("terms", terms);
        int limit = params.getInt(TermsParams.TERMS_LIMIT, 10); //返回个数
        if (limit < 0) {
          limit = Integer.MAX_VALUE;
        }
        String upperStr = params.get(TermsParams.TERMS_UPPER); //截止Term
        boolean upperIncl = params.getBool(TermsParams.TERMS_UPPER_INCLUSIVE, false);
        boolean lowerIncl = params.getBool(TermsParams.TERMS_LOWER_INCLUSIVE, true);
        boolean sort = !TermsParams.TERMS_SORT_INDEX.equals(
                          params.get(TermsParams.TERMS_SORT, TermsParams.TERMS_SORT_COUNT)); //按索引、数量排序
        int freqmin = params.getInt(TermsParams.TERMS_MINCOUNT, 1); // initialize freqmin 限制最小频率
        int freqmax = params.getInt(TermsParams.TERMS_MAXCOUNT, UNLIMITED_MAX_COUNT); // initialize freqmax 限制最大频率,UNLIMITED_MAX_COUNT=-1代表不限制
        if (freqmax<0) {
          freqmax = Integer.MAX_VALUE;
        }
        String prefix = params.get(TermsParams.TERMS_PREFIX_STR); //前缀
        boolean raw = params.getBool(TermsParams.TERMS_RAW, false); //是否做类型转换
        for (int j = 0; j < fields.length; j++) {
          String field = StringHelper.intern(fields[j]);
          FieldType ft = raw ? null : rb.req.getSchema().getFieldTypeNoEx(field);
          if (ft==null) ft = new StrField();
          // If no lower bound was specified, use the prefix
          String lower = lowerStr==null ? prefix : (raw ? lowerStr : ft.toInternal(lowerStr));
          if (lower == null) lower="";
          String upper = upperStr==null ? null : (raw ? upperStr : ft.toInternal(upperStr));
          Term lowerTerm = new Term(field, lower);
          Term upperTerm = upper==null ? null : new Term(field, upper);
         
          TermEnum termEnum = rb.req.getSearcher().getReader().terms(lowerTerm); //this will be positioned ready to go
          int i = 0;
          BoundedTreeSet<CountPair<String, Integer>> queue = (sort ? new BoundedTreeSet<CountPair<String, Integer>>(limit) : null);
          NamedList fieldTerms = new NamedList();
          terms.add(field, fieldTerms);
          Term lowerTestTerm = termEnum.term();
          //Only advance the enum if we are excluding the lower bound and the lower Term actually matches
          if (lowerTestTerm!=null && lowerIncl == false && lowerTestTerm.field() == field  // intern'd comparison
                  && lowerTestTerm.text().equals(lower)) {
            termEnum.next();
          }
         //IndexReader.terms()返回的Term默认是根据text的字符前缀相同排列在一起的,因此当没有找到匹配的,则后面也不会再有匹配的Term
          while (i<limit || sort) {
            Term theTerm = termEnum.term();
            // check for a different field, or the end of the index.
            if (theTerm==null || field != theTerm.field())  // intern'd comparison
              break;
            String indexedText = theTerm.text();
            // stop if the prefix doesn't match
            if (prefix != null && !indexedText.startsWith(prefix)) break;
            if (upperTerm != null) {
              int upperCmp = theTerm.compareTo(upperTerm);
              // if we are past the upper term, or equal to it (when don't include upper) then stop.
              if (upperCmp>0 || (upperCmp==0 && !upperIncl)) break;
            }
            // This is a good term in the range.  Check if mincount/maxcount conditions are satisfied.
            //实现自动提示的核心代码如下:
            int docFreq = termEnum.docFreq();
            if (docFreq >= freqmin && docFreq <= freqmax) { //判断当前Term频率是否在有效范围内
              // add the term to the list
              String label = raw ? indexedText : ft.indexedToReadable(indexedText);
              if (sort) {//排序(在这个队列方法里会根据频率排序,当队列个数大于初始化空间大小则自动移除最后一个元素)(默认则根据频率排序)
                queue.add(new CountPair<String, Integer>(label, docFreq));
              } else {//不需要排序则直接取出前缀相匹配的Term值与频率,最多取出limit个(不会考虑频率的排序)
                fieldTerms.add(label, docFreq);
                i++;
              }
              //下面给出获取一个索引文件content域字段的Term打印出来的结果,相信有助于大家理解上面的核心代码:
              //content=防旱:1;content=防汛:1;content=防火:1;content=防风:1;content=阳春:1;content=陈:63;content=陈少:1;content=陈总:2;content=陈振:121;content=陈旗:16;content=陈根:6;content=限:1;content=集团:300;
              //其中格式为:Field名=Term的text值:Term的频率值,从上面的输出结果中可看出其前缀相同的会紧跟在一块,但是默认是不会根据Term频率排序,也没体现出按字母顺序的规则。
            }
            termEnum.next();
          }
          termEnum.close();
         
          if (sort) {//若排序
            for (CountPair<String, Integer> item : queue) {
              if (i < limit) {
                fieldTerms.add(item.key, item.val);
                i++;
              } else {
                break;
              }
            }
          }
        }
      } else {
        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "No terms.fl parameter specified");
      }
    }
  }

             版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 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-138066-1-1.html 上篇帖子: solr主从配置 自动实现索引同步 下篇帖子: Linux 下 solr 安装注意事项
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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