关闭

lucene的分组(group by)

标签: luceneintegerquerycachebrowsereach
2043人阅读 评论(0) 收藏 举报
分类:

原文地址: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
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:454595次
    • 积分:5635
    • 等级:
    • 排名:第4824名
    • 原创:141篇
    • 转载:120篇
    • 译文:4篇
    • 评论:30条
    最新评论