我有一个包含多个字段的大型语料库索引。只有其中一个字段包含文本。 我需要基于该字段从整个索引中提取唯一的单词。 请问有人知道如何在Java的Lucene中实现这个功能吗?
如果您正在使用Lucene 4.0 API,您需要从索引读取器中获取字段。然后,Fields提供了获取索引中每个字段的词语的方法。以下是如何执行此操作的示例:
Fields fields = MultiFields.getFields(indexReader);
Terms terms = fields.terms("field");
TermsEnum iterator = terms.iterator(null);
BytesRef byteRef = null;
while((byteRef = iterator.next()) != null) {
String term = new String(byteRef.bytes, byteRef.offset, byteRef.length);
}
最终,对于Lucene的新版本,您可以通过调用BytesRef获取字符串:
byteRef.utf8ToString();
代替
new String(byteRef.bytes, byteRef.offset, byteRef.length);
如果您想获得文档频率,可以执行:
int docFreq = iterator.docFreq();
HashSet
中。IndexReader reader = IndexReader.open(index);
TermEnum terms = reader.terms();
Set<String> uniqueTerms = new HashSet<String>();
while (terms.next()) {
final Term term = terms.term();
if (term.field().equals("field_name")) {
uniqueTerms.add(term.text());
}
}
这不是最优解决方案,您正在阅读并且丢弃所有其他字段。在Lucene 4中有一个名为Fields
的类,仅返回单个字段的terms(field)。
reader.terms(new Term(fieldName, termText))
将按字段名称排序返回Term
,并且在相同字段的术语中按术语文本排序。因此,如果您在上面使用了terms.term(fieldName, "")
,并且在第一次出现!term.field().equals(fieldName)
时进行break
,则会得到所需的结果。但是,由于这没有记录,它是否会有一天崩溃呢?就我所看到的,Lucene自己的WildcardQuery
也是基于此构建的,而且Lucene 3不太可能再发生太大变化了。 - ddekany同样的结果,只是更加简洁,可以使用lucene-suggest
包中的LuceneDictionary
。它会处理不包含任何术语的字段,并返回一个BytesRefIterator.EMPTY
。这将避免出现NPE错误 :)
LuceneDictionary ld = new LuceneDictionary( indexReader, "field" );
BytesRefIterator iterator = ld.getWordsIterator();
BytesRef byteRef = null;
while ( ( byteRef = iterator.next() ) != null )
{
String term = byteRef.utf8ToString();
}
从Lucene 7+开始,上述链接以及一些相关链接已经过时。
以下是当前的内容:
// IndexReader has leaves, you'll iterate through those
int leavesCount = reader.leaves().size();
final String fieldName = "content";
for(int l = 0; l < leavesCount; l++) {
System.out.println("l: " + l);
// specify the field here ----------------------------->
TermsEnum terms = reader.leaves().get(l).reader().terms(fieldName).iterator();
// this stops at 20 just to sample the head
for(int i = 0; i < 20; i++) {
// and to get it out, here -->
final Term content = new Term(fieldName, BytesRef.deepCopyOf(terms.next()));
System.out.println("i: " + i + ", term: " + content);
}
}
TermsEnum
和 terms.next()
存在微妙的 off by one bug。这是因为 TermsEnum
已经指向第一个 term,所以 while(terms.next())
会导致第一个 term 被跳过。TermEnum terms = reader.terms();
for(Term term = terms.term(); term != null; terms.next(), term = terms.term()) {
// do something with the term
}
修改已接受答案中的代码:
IndexReader reader = IndexReader.open(index);
TermEnum terms = reader.terms();
Set<String> uniqueTerms = new HashSet<String>();
for(Term term = terms.term(); term != null; terms.next(), term = terms.term()) {
if (term.field().equals("field_name")) {
uniqueTerms.add(term.text());
}
}
Terms terms = MultiTerms.getTerms(indexReader, "title");
if (terms != null) {
TermsEnum iter = terms.iterator();
BytesRef byteRef = null;
while ((byteRef = iter.next()) != null) {
System.out.printf("%s (freq=%s)%n",
byteRef.utf8ToString(),
iter.docFreq()
);
}
}