对于lucene的统计,我基本放弃使用factedSearch了,效率不高,而且两套索引总觉得有点臃肿!

这次我们通过改造Collector,实现简单的统计功能。经过测试,对几十万的统计还是比较快的。

首先我们简单理解下Collector在search中的使用情况!

Collector是一个接口,主要包括以下重要方法:

public abstract class Collector {//指定打分器public abstract void setScorer(Scorer scorer) throws IOException;//对目标结果进行收集,很重要!public abstract void collect(int doc) throws IOException;//一个索引可能会有多个子索引,这里相当于是对子索引的遍历操作public abstract void setNextReader(IndexReader reader, int docBase) throws IOException;//public abstract boolean acceptsDocsOutOfOrder();}

在search中我们来看看collector是怎么收集结果的!

public void search(Weight weight, Filter filter, Collector collector)throws IOException {// TODO: should we make this// threaded...? the Collector could be sync'd?// always use single thread:for (int i = 0; i < subReaders.length; i++) { // 检索每个子索引collector.setNextReader(subReaders[i], docBase + docStarts[i]);final Scorer scorer = (filter == null) ? weight.scorer(subReaders[i], !collector.acceptsDocsOutOfOrder(), true): FilteredQuery.getFilteredScorer(subReaders[i],getSimilarity(), weight, weight, filter);//构建打分器if (scorer != null) {scorer.score(collector);//打分}}}

scorer.score(collector)的过程如下:

public void score(Collector collector) throws IOException {collector.setScorer(this);int doc;while ((doc = nextDoc()) != NO_MORE_DOCS) {collector.collect(doc);//搜集结果}}

collector.collect(doc)的过程如下:

    @Overridepublic void collect(int doc) throws IOException {float score = scorer.score();// This collector cannot handle these scores:assert score != Float.NEGATIVE_INFINITY;assert !Float.isNaN(score);totalHits++;if (score <= pqTop.score) {// 以下的实现使用了优先级队列,如果当前分值小于队列中pqTop.score则直接pass!return;}pqTop.doc = doc + docBase;pqTop.score = score;pqTop = pq.updateTop();}

从上面这一坨坨代码我们可以大概看清collector在search中的应用情况。

那么统计呢?

首先我们来分析最简单的统计——“一维统计”,就只对一个字段的统计。例如统计图书每年的出版量、专利发明人发明专利数量的排行榜等。

统计的输入:检索式、统计字段

统计的输出:<统计项、数量>的集合

其中关键是我们怎么拿到统计项。这个又分成以下一种情况:

1)统计字段没有存储、不分词

我们可以使用FieldCache.DEFAULT.getStrings(reader, f);获取统计项。

2)统计字段没有存储、分词

需要通过唯一标识从数据库(如果正向信息存在数据库的话)取出统计项(字段内容),然后统计分析。可想而知效率极低。

3)统计字段存储、分词

可以通过doc.get(fieldName)取出统计项,依然比较低效

4)统计字段存储、不分词

和1)类似

因此我们如果要对某个字段进行统计,那么最好选用不分词(Index.NOT_ANALYZED),这个和排序字段的要求类似!

拿到统计项后,我们可以通过累加然后排序。(这里可以借助map)

下面给出主要代码:

package com.fox.group;import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.search.Scorer;/*** @author huangfox* @data 2012-7-10* @email huangfox009@126.com* @desc */
public class GroupCollectorDemo extends Collector {private GF gf = new GF();// 保存分组统计结果private String[] fc;// fieldCacheprivate String f;// 统计字段String spliter;int length;public void setFc(String[] fc) {this.fc = fc;}@Overridepublic void setScorer(Scorer scorer) throws IOException {}@Overridepublic void setNextReader(IndexReader reader, int docBase)throws IOException {fc = FieldCache.DEFAULT.getStrings(reader, f);}@Overridepublic void collect(int doc) throws IOException {// 添加的GroupField中,由GroupField负责统计每个不同值的数目gf.addValue(fc[doc]);}@Overridepublic boolean acceptsDocsOutOfOrder() {return true;}public GF getGroupField() {return gf;}public void setSpliter(String spliter) {this.spliter = spliter;}public void setLength(int length) {this.length = length;}public void setF(String f) {this.f = f;}
}class GF {// 所有可能的分组字段值,排序按每个字段值的文档个数大小排序private List<String> values = new ArrayList<String>();// 保存字段值和文档个数的对应关系private Map<String, Integer> countMap = new HashMap<String, Integer>();public Map<String, Integer> getCountMap() {return countMap;}public void setCountMap(Map<String, Integer> countMap) {this.countMap = countMap;}public List<String> getValues() {Collections.sort(values, new ValueComparator());return values;}public void setValues(List<String> values) {this.values = values;}public void addValue(String value) {if (value == null || "".equals(value))return;if (countMap.get(value) == null) {countMap.put(value, 1);values.add(value);} else {countMap.put(value, countMap.get(value) + 1);}}class ValueComparator implements Comparator<String> {public int compare(String value0, String value1) {if (countMap.get(value0) > countMap.get(value1)) {return -1;} else if (countMap.get(value0) < countMap.get(value1)) {return 1;}return 0;}}
}

这里是对collector的collect方法的讨巧应用,search是对打分的排序,统计是构造一个结果收集器,提供排序功能。

测试类:

package com.fox.group;import java.io.File;
import java.io.IOException;
import java.util.List;import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.SimpleFSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;/*** @author huangfox* @data 2012-7-10* @email huangfox009@126.com* @desc */
public class GroupTest {public static void main(String[] f) throws IOException, ParseException {FSDirectory dir = SimpleFSDirectory.open(new File("d:/nrttest"));IndexReader reader = IndexReader.open(dir);IndexSearcher searcher = new IndexSearcher(reader);// GroupCollector是自定义文档收集器,用于实现分组统计String str = "";QueryParser parser = new QueryParser(Version.LUCENE_36, "f",new IKAnalyzer());while (true) {str = "an:cn*";long bt = System.currentTimeMillis();Query query = parser.parse(str);System.out.println(query);GroupCollectorDemo myCollector = new GroupCollectorDemo();// myCollector.setFc(ad);myCollector.setF("in");searcher.search(query, myCollector);// GroupField用来保存分组统计的结果GF gf = myCollector.getGroupField();List<String> values = gf.getValues();long et = System.currentTimeMillis();System.out.println((et - bt) + "ms");for (int i = 0; i < 10; i++) {String value = values.get(i);System.out.println(value + "=" + gf.getCountMap().get(value));}}}
}

以上是对200多万数据的统计,而且是全数据统计。测试结果如下:

an:cn*
6616ms
毛裕民;谢毅=13728
邱则有=10126
杨孟君=3771
王尔中=1712
王信锁=1658
张逶=1314
朱炜=1200
赵蕴岚;何唯平=1039
杨贻方=872
黄金富=871

系统使用情况:

你可能会说——这不是坑爹吗?要6s的时间消耗!!!

解释:

1.数据量,统计的数据量在200万;

如果数据量在几十万,测试结果如下:

ad:2006*
213ms
邱则有=1244
张云波=628
赵蕴岚;何唯平=398
余内逊;余谦梁=376
杨贻方=298
王尔中=258
汪铁良=224
赵发=222
黄振华=212
陆舟;于华章=196

  

2.运行在pc机上;

以上解释也可以理解成借口,那么还有哪些环节可以优化呢?

从cpu和io来看,cpu应该主要是由于hashMap的操作引起的,io主要是由FieldCache.DEFAULT.getStrings(reader, f)获取统计项引起的。

如果高并发的情况下,io无疑是个大问题,我们可以考虑缓存。

对于运算量大的情况,我们可以考虑分布式。

后续我们将分析:

1)二维统计、多维统计

2)个性化统计

[ lucene扩展 ] 自定义Collector实现统计功能相关推荐

  1. 利用bobo-browse 实现lucene的分组统计功能

    bobo-browse 是一用java写的lucene扩展组件,通过它可以很方便在lucene上实现分组统计功能. 可以从http://sna-projects.com/bobo/上下载和查看相关文档 ...

  2. CSDN产品公告第3期:博客数据统计功能上线,OFFER大挑战等你来!

    各位小主,咱们又见面了!在前两期的公告中,CSDN产品组已感受到大家对我们深沉的爱,再此谢谢大家的褒贬.服务好每一位用户,是我们一直不变的初心. 在过去一周,开发在产品的鞭策下,又带来了哪些功能呢? ...

  3. 基于netty+websocket实现门户游客实时统计功能

    基于netty+websocket实现门户游客实时统计功能 基本需求 商城门户页面需要实时展示游客访问的数量,商城后台页面需要实时游客访问量.登录用户数量,以及下订单用户数量. 技术选型 1.首先实时 ...

  4. 在APICloud开发平台使用友盟统计功能教程

    APICloud开发平台模块库中的umAnalytics模块封装了友盟APP统计SDK.实现了友盟统计功能,包括启动次数.事件.页面等app数据的统计. 模块的使用步骤主要如下: 1.首先需注册好AP ...

  5. php redis 签到,如何利用 Redis 快速实现签到统计功能

    @这是小豪的第十一篇文章 上篇文章 已经对 Redis 基础命令进行了一个大致的学习,接下来我们就需要解决 Issue "增加用户活跃度统计" 啦! 其实当我看到这个 Issue ...

  6. Java8中Collector详解及自定义Collector

    文章目录 1.Collector介绍 2.Collector约束 3.Collector接口方法 4.理解Collector接口声明的方法 5.整合自定义Collector 6.使用collect方法 ...

  7. UWP 扩展/自定义标题栏的方法,一些概念和一些注意事项

    在 Windows 10 的前几个版本中将页面内容扩展到标题栏上还算简单,主要是没什么坑.直到一些新控件的引入和一些外观设计趋势变化之后,扩展标题栏开始出现一些坑了. 本文将重温 UWP 自定义标题栏 ...

  8. Leangoo敏捷开发项目管理平台新增测试用例管理、测试结果统计功能

    ​Leangoo领歌新增测试用例管理.测试计划规划.测试结果统计功能,以便管理项目中的测试工作 管理测试用例 通过在"测试用例库"类型看板中添加"测试用例"类型 ...

  9. 微信小程序图标不支持html,微信小程序实现自定义加载图标功能

    效果图 实现思路 1.首先通过HTML+CSS实现加载动画的静态效果: 2.根据需求给每个动画设计不同的动画效果. 例如第一个加载图标的静态绘制 1.首先确定动画的盒子宽高: 2.设置盒子中每一个长方 ...

最新文章

  1. int和long计算问题
  2. python程序员在公司都是做什么的-程序员是做什么的?工资待遇怎么样?
  3. python3多线程第三方库_Python之多线程爬虫抓取网页图片的示例代码
  4. CSS权重的等级划分
  5. 理工科学生如何建立对于财务三大表的基本分析呢?
  6. spark on yarn 内存分配详解
  7. 得到鹅厂最新前端开发手册一份
  8. JS 数组常用函数(数组合并、数组转字符串、顺序反转、范围选择、排序、插入数据、删除数据)
  9. 不止是 Oracle 读物
  10. Python 异步 ASGI 服务器及框架
  11. OCP 12c最新考试原题及答案(071-7)
  12. caffe︱Places365-CNNs For Scene Recognition
  13. apicloud开发时的一些注意点
  14. [附源码]计算机毕业设计JAVAJAVA大方汽车租赁管理系统
  15. 最全搭建自己的SOCKS代理服务器
  16. 系统登录页面短信验证码方式登录实现
  17. 工作压力不容忽视——网易公司宣布首席执行官孙德棣18日辞世
  18. Win7-Win10快捷键
  19. 天猫手机卖到第一:手机厂在想什么
  20. 最大传输单元MTU详解

热门文章

  1. postsql——存储(TableSpace)
  2. JDBC驱动加载机制详解以及spi机制
  3. c语言房屋销售管理信息系统
  4. PNG序列帧转webm格式视频播放时有白边问题
  5. 超级电容容量及放电时间计算方法
  6. 宁波栎社机场停车场怎么收费,栎社机场停车场收费标准
  7. 后端-数据字典模块开发
  8. 全面分析中国菜刀及隐藏后门
  9. tomcat原理简要分析,java
  10. VMware Workstation 7.1.5 build 491717 精简汉化版