lucene的分组(group by)

原文地址:http://xtuhcy.iteye.com/blog/1143440

 

最近在优化搜索服务,以前的搜索服务十分简单,现在需要增加查询结果分组统计的功能,以提高用户体验。

g了一番,有以下几个主要的实现方式:

1、利用search中的collect,自己实现一个collect输出分组统计结果;

2、一个开源的lucene插件bobo browser

3、lucnen3.2后有个grouping模块

最后我选择了grouping这个模块来实现

grouping模块有2种方法实现,2次遍历法和1次遍历法。1次遍历法是在lucene3.3之后才开始提供。

1次遍历法效率很高,但是需要在索引时添加特殊的标志:

To use the single-pass BlockGroupingCollector, first, at indexing time, you must ensure all docs in each group are added as a block, and you have some way to find the last document of each group. One simple way to do this is to add a marker binary field:

网站访问量目前还很小,又不想重新建索引,于是还是采用了2次遍历法。2次遍历的第一次遍历是把分组拿出来,如果您只需要分组,不需要各个分组的数量,可以一次遍历即可。第二次遍历是把各个分组的搜索结果拿出来,当然也就能知道各个分组的数量。

2次遍历法由于需要经过2次搜索效率较低,因此引入了一个cache机制,CachingCollector。这样在第二次遍历是就可以直接读内存了。

代码片段如下:

public Map<String, Integer> groupBy(Query query, String field, int topCount) {
  Map<String, Integer> map = new HashMap<String, Integer>();
 
  long begin = System.currentTimeMillis();
  int topNGroups = topCount;
  int groupOffset = 0;
  int maxDocsPerGroup = 100;
  int withinGroupOffset = 0;
  try {
   TermFirstPassGroupingCollector c1 = new TermFirstPassGroupingCollector(field, Sort.RELEVANCE, topNGroups);
   boolean cacheScores = true;
   double maxCacheRAMMB = 4.0;
   CachingCollector cachedCollector = CachingCollector.create(c1, cacheScores, maxCacheRAMMB);
   indexSearcher.search(query, cachedCollector);
   Collection<SearchGroup<String>> topGroups = c1.getTopGroups(groupOffset, true);
   if (topGroups == null) {
    return null;
   }
   TermSecondPassGroupingCollector c2 = new TermSecondPassGroupingCollector(field, topGroups, Sort.RELEVANCE, Sort.RELEVANCE, maxDocsPerGroup, true, true, true);
   if (cachedCollector.isCached()) {
    // Cache fit within maxCacheRAMMB, so we can replay it:
    cachedCollector.replay(c2);
   } else {
       // Cache was too large; must re-execute query:
    indexSearcher.search(query, c2);
   }
  
   TopGroups<String> tg = c2.getTopGroups(withinGroupOffset);
   GroupDocs<String>[] gds = tg.groups;
   for(GroupDocs<String> gd : gds) {
    map.put(gd.groupValue, gd.totalHits);
   }
  } catch (IOException e) {
   e.printStackTrace();
  }
  long end = System.currentTimeMillis();
  System.out.println("group by time :" + (end - begin) + "ms");
  return map;
}

几个参数说明:

  • groupField: 分组域
  • groupSort: 分组排序
  • topNGroups: 最大分组数
  • groupOffset: 分组分页用
  • withinGroupSort: 组内结果排序
  • maxDocsPerGroup: 每个分组的最多结果数
  • withinGroupOffset: 组内分页用

 

评论

 

2 楼    wyyl1    2011-12-07      引用
非常感谢!
今日搜索了,lucene的解决方法,你说的这个可以用。但是只用一次循环的那个方法,我试了好长时间没有搞定!有时间希望交流一下,循环一次的那个方法。

我使用的是lucene3.5.0版本的组件
就是参考http://lucene.apache.org/java/3_5_0/api/contrib-grouping/org/apache/lucene/search/grouping/package-summary.html
循环一次的没有成功

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值