【问题标题】:How can I get the list of unique terms from a specific field in Lucene?如何从 Lucene 的特定字段中获取唯一术语列表?
【发布时间】:2012-01-18 12:19:50
【问题描述】:

我有一个来自包含多个字段的大型语料库的索引。这些字段中只有一个包含文本。 我需要根据该字段从整个索引中提取唯一词。 有谁知道我如何在 java 中使用 Lucene 做到这一点?

【问题讨论】:

    标签: java lucene


    【解决方案1】:

    从 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);
      }
    }
    

    【讨论】:

      【解决方案2】:

      您正在寻找term vectors(字段中所有单词的集合以及每个单词的使用次数,不包括停用词)。您将对索引中的每个文档使用 IndexReader 的 getTermFreqVector(docid, field),并用它们填充 HashSet

      替代方法是使用terms() 并仅选择您感兴趣的领域的术语:

      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)

      【讨论】:

      • 我知道这个功能。但我需要整个语料库中的唯一词。不是每个文档。
      • 是的,谢谢我使用了 terms() 但它把所有已经被索引的东西都还给了我。我找不到一个地方告诉它只选择特定领域的术语。你对此有什么参考吗?
      • if (term.field().equals("field_name")) {...} 你想要的字段:)
      • 即使在 Lucene 3 上,您也不必为此扫描所有字段的条款。这似乎没有记录,但reader.terms(new Term(fieldName, termText)) 将返回按字段名称排序的Term-s,以及按术语文本排序的同一字段的术语。因此,如果您在上面使用terms.term(fieldName, ""),并在第一次使用!term.field().equals(fieldName) 时使用break,您就会得到您想要的。但既然这是无证的,那它不会有一天会坏掉吗?据我所知,Lucene 自己的 WildcardQuery 也是基于此构建的,Lucene 3 不太可能再发生太大变化。
      • @milan 为什么将变量term 声明为final?任何性能原因?
      【解决方案3】:

      使用TermsEnumterms.next() 的答案有一个错误。这是因为TermsEnum已经指向第一个词,所以while(terms.next())会导致第一个词被跳过。

      改为使用 for 循环:

      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());
              }
      }
      

      【讨论】:

        【解决方案4】:

        如果您使用的是 Lucene 4.0 api,则需要从索引读取器中获取字段。然后,字段提供了获取索引中每个字段的术语的方法。以下是如何做到这一点的示例:

                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();
        

        【讨论】:

        • 这是现代版 Lucene 的正确答案。
        【解决方案5】:

        使用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();
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-16
          • 1970-01-01
          • 2019-03-17
          • 2012-03-25
          • 1970-01-01
          • 1970-01-01
          • 2021-07-10
          相关资源
          最近更新 更多