前段时间同事也开始对搜索感兴趣,他看到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;
}
}
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;
}
}
标签:Term,terms,TermInfo,索引,fields,term,关键字,频率 From: https://blog.51cto.com/u_2544485/7394859