前段时间同事也开始对搜索感兴趣,他看到luke工具界面上会显示Term的频率数,提出通过索引用户的搜索日志中的检索关键字,我们是否可以利用这个Field字段中Term频率高低来说明其是否代表热点关键字。
想想觉得也是有一点在理,特别是对用户没有任何可分析性的情况下。但这就引出了一个问题:如何获取整个索引文件里频率最高的哪几个Term?
翻了几次API,网上也搜了但没找到什么信息,仅仅看到网上有一篇文章讲到通过IndexReader取出所有的Term,然后再比较每一个的频率,最终得出最高频率的哪几个Term。但这又让我们顾虑的是当大数据量时如何确保其速度?
既然luke可以在界面上展示出来,那它一定实现了这个功能,所以今天下了源代码,不过发现其已经不支持Lucene3.0.2,查看更新日志原来他们已经更新到可兼容未来Lucene4.0.0版本。最后从日志里找出已被删除的两个java文件:HighFreqTerms.java 和TermInfo.java,源代码如下(本人在里面添加了注释,如有错误请大家帮忙纠正,:-D):
import org.apache.lucene.index.Term;
public class TermInfo {
public Term term;
public int docFreq; //term频率
public TermInfo(Term t, int df) {
this.term = t;
this.docFreq = df;
}
}
public class TermInfo {
public Term term;
public int docFreq; //term频率
public TermInfo(Term t, int df) {
this.term = t;
this.docFreq = df;
}
}
import org.apache.lucene.util.PriorityQueue;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermEnum;
import java.io.File;
import java.util.Hashtable;
/**
* 获取一个索引文件中最高频率Term数组
* <code>HighFreqTerms</code> class extracts terms and their frequencies out
* of an existing Lucene index.
*
* @version $Id: HighFreqTerms.java,v 1.2 2003/11/08 10:55:40 Administrator Exp $
*/
public class HighFreqTerms {
public static int defaultNumTerms = 10; //初始化默认取出top defaultNumTerms-1条
public static void main(String[] args) throws Exception {
Directory dir = FSDirectory.open( new File( "D://DATAMANAGER//INDEX//SYS_3000"));
TermInfo[] terms = getHighFreqTerms(IndexReader.open(dir), null, new String[]{ "content"});
for ( int i = 0; i < terms.length; i ++) {
System.out.println(i + "./t" + terms[i].term + ":" +terms[i].docFreq);
}
}
/**
* 取出频率最高的term列表
* @param ir IndexReader
* @param junkWords Hashtable 过滤词
* @param fields 可获取多个Field new String[]{"title","content"}
* @return TermInfo[]
* @throws Exception
*/
public static TermInfo[] getHighFreqTerms(IndexReader ir, Hashtable junkWords, String[] fields) throws Exception {
return getHighFreqTerms(ir, junkWords, defaultNumTerms, fields);
}
/**
* 取出频率最高的term列表
* @param reader IndexReader
* @param junkWords Hashtable 过滤词
* @param numTerms 初始化队列的大小即取出numTerms-1条
* @param fields 可获取多个Field new String[]{"title","content"}
* @return TermInfo[]
* @throws Exception
*/
public static TermInfo[] getHighFreqTerms(IndexReader reader, Hashtable junkWords, int numTerms, String[] fields) throws Exception {
if (reader == null || fields == null) return null;
TermInfoQueue tiq = new TermInfoQueue(numTerms); //实例化一个numTerms大小存放TermInfo的队列
TermEnum terms = reader.terms(); //读取索引文件里所有的Term
int minFreq = 0; //队列最后一个Term的频率即当前最小频率值
while (terms.next()) { //取出一个Term对象出来
String field = terms.term().field();
if (fields != null && fields.length > 0) {
boolean skip = true; //跳过标识
for ( int i = 0; i < fields.length; i ++) {
if (field.equals(fields[i])) { //当前Field属于fields数组中的某一个则不跳过
skip = false;
break;
}
}
if (skip) continue;
}
//当前term的内容是过滤词,则直接跳过
if (junkWords != null && junkWords.get(terms.term().text()) != null) continue;
//获取最高频率term的核心代码
//(队列底层是最大频率Term,顶层是最小频率Term,当插入一个元素后超出初始化队列大小则pop最上面的那个元素,重新设置最小频率值minFreq)
if (terms.docFreq() > minFreq) { //当前Term的频率大于最小频率则插入队列中
tiq.insertWithOverflow( new TermInfo(terms.term(), terms.docFreq()));
if (tiq.size() > = numTerms) // if tiq overfull 当队列中的个数大于numTerms
{
tiq.pop(); // remove lowest in tiq 取出最小频率的元素即最上面的一个元素
minFreq = ((TermInfo)tiq.top()).docFreq; // reset minFreq 重新设置当前最顶层Term的频率为minFreq
}
}
}
//pop出队列元素,最终存放在数组中元素的term频率按从大到小排列
TermInfo[] res = new TermInfo[tiq.size()];
for ( int i = 0; i < res.length; i ++) {
res[res.length - i - 1] = (TermInfo)tiq.pop();
}
return res;
}
}
//队列,用于term频率比较的队列
final class TermInfoQueue extends PriorityQueue <TermInfo > {
TermInfoQueue( int size) {
initialize(size);
}
protected final boolean lessThan(TermInfo a, TermInfo b) {
return a.docFreq < b.docFreq;
}
}
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermEnum;
import java.io.File;
import java.util.Hashtable;
/**
* 获取一个索引文件中最高频率Term数组
* <code>HighFreqTerms</code> class extracts terms and their frequencies out
* of an existing Lucene index.
*
* @version $Id: HighFreqTerms.java,v 1.2 2003/11/08 10:55:40 Administrator Exp $
*/
public class HighFreqTerms {
public static int defaultNumTerms = 10; //初始化默认取出top defaultNumTerms-1条
public static void main(String[] args) throws Exception {
Directory dir = FSDirectory.open( new File( "D://DATAMANAGER//INDEX//SYS_3000"));
TermInfo[] terms = getHighFreqTerms(IndexReader.open(dir), null, new String[]{ "content"});
for ( int i = 0; i < terms.length; i ++) {
System.out.println(i + "./t" + terms[i].term + ":" +terms[i].docFreq);
}
}
/**
* 取出频率最高的term列表
* @param ir IndexReader
* @param junkWords Hashtable 过滤词
* @param fields 可获取多个Field new String[]{"title","content"}
* @return TermInfo[]
* @throws Exception
*/
public static TermInfo[] getHighFreqTerms(IndexReader ir, Hashtable junkWords, String[] fields) throws Exception {
return getHighFreqTerms(ir, junkWords, defaultNumTerms, fields);
}
/**
* 取出频率最高的term列表
* @param reader IndexReader
* @param junkWords Hashtable 过滤词
* @param numTerms 初始化队列的大小即取出numTerms-1条
* @param fields 可获取多个Field new String[]{"title","content"}
* @return TermInfo[]
* @throws Exception
*/
public static TermInfo[] getHighFreqTerms(IndexReader reader, Hashtable junkWords, int numTerms, String[] fields) throws Exception {
if (reader == null || fields == null) return null;
TermInfoQueue tiq = new TermInfoQueue(numTerms); //实例化一个numTerms大小存放TermInfo的队列
TermEnum terms = reader.terms(); //读取索引文件里所有的Term
int minFreq = 0; //队列最后一个Term的频率即当前最小频率值
while (terms.next()) { //取出一个Term对象出来
String field = terms.term().field();
if (fields != null && fields.length > 0) {
boolean skip = true; //跳过标识
for ( int i = 0; i < fields.length; i ++) {
if (field.equals(fields[i])) { //当前Field属于fields数组中的某一个则不跳过
skip = false;
break;
}
}
if (skip) continue;
}
//当前term的内容是过滤词,则直接跳过
if (junkWords != null && junkWords.get(terms.term().text()) != null) continue;
//获取最高频率term的核心代码
//(队列底层是最大频率Term,顶层是最小频率Term,当插入一个元素后超出初始化队列大小则pop最上面的那个元素,重新设置最小频率值minFreq)
if (terms.docFreq() > minFreq) { //当前Term的频率大于最小频率则插入队列中
tiq.insertWithOverflow( new TermInfo(terms.term(), terms.docFreq()));
if (tiq.size() > = numTerms) // if tiq overfull 当队列中的个数大于numTerms
{
tiq.pop(); // remove lowest in tiq 取出最小频率的元素即最上面的一个元素
minFreq = ((TermInfo)tiq.top()).docFreq; // reset minFreq 重新设置当前最顶层Term的频率为minFreq
}
}
}
//pop出队列元素,最终存放在数组中元素的term频率按从大到小排列
TermInfo[] res = new TermInfo[tiq.size()];
for ( int i = 0; i < res.length; i ++) {
res[res.length - i - 1] = (TermInfo)tiq.pop();
}
return res;
}
}
//队列,用于term频率比较的队列
final class TermInfoQueue extends PriorityQueue <TermInfo > {
TermInfoQueue( int size) {
initialize(size);
}
protected final boolean lessThan(TermInfo a, TermInfo b) {
return a.docFreq < b.docFreq;
}
}