Lucene和令牌太长

时间:2016-03-10 14:06:02

标签: java lucene

我正在使用Lucene处理索引器,上周又添加了TermVectors存储代码,以便我可以尝试反馈和其他有趣的东西。在存储术语向量的这一步之后,我开始收到以下错误:

  

java.lang.IllegalArgumentException:Document包含至少一个   字段中的巨大术语=“f_common.document.text”(其UTF8编码   比最大长度32766更长,所有这些都被跳过了。   请更正分析仪以不生成此类条款。前缀   第一个巨大的术语是:[34,60,97,99,114,101,58,98,   108,111,99,107,62,32,32,32,60,100,105,118,32,97,99,   114,101,58,100,101,102,61] ......',原始消息:字节可以   最多32766个;得到34444

好的,没问题。 (好吧,死在一个过长的令牌上对我来说感觉就像一个错误,但这是一个有意识的API在4.8中的移动。)所以我添加了以下AnalyzerWrapper来解决问题:

import java.io.IOException;
import java.io.StringReader;
import java.util.HashSet;
import java.util.Set;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.AnalyzerWrapper;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.miscellaneous.LengthFilter;
import org.apache.lucene.index.IndexWriter;

/*
 * Lucene's IndexWriter.addDocument() will pitch an exception if the document contains a token that is too long.
 * I would rather just drop long tokens.
 */

public class SafetyAnalyzer extends AnalyzerWrapper {

    private Analyzer baseAnalyzer;

    public SafetyAnalyzer(Analyzer baseAnalyzer) {
        super(Analyzer.PER_FIELD_REUSE_STRATEGY);
        this.baseAnalyzer = baseAnalyzer;
    }

    @Override
    public void close() {
        baseAnalyzer.close();
        super.close();
    }

    @Override
    protected Analyzer getWrappedAnalyzer(String fieldName) {
        return baseAnalyzer;
    }

    @Override
    protected TokenStreamComponents wrapComponents(String fieldName, TokenStreamComponents components) {
        TokenStream ts = components.getTokenStream();
        LengthFilter drop_long_tokens = new LengthFilter(ts, 0, IndexWriter.MAX_TERM_LENGTH);
        return new TokenStreamComponents(components.getTokenizer(), drop_long_tokens);
    }
}

哪个会删除IndexWriter将要关闭的长令牌。这包装了其他分析器:

public Analyzer getDefaultIndexAnalyzer() throws Exception {
    if (defaultIndexAnalyzer == null) {
        String defaultAnalyzerName = getConfig("LUCENE_INDEX_ANALYZER_DEFAULT");
        if (defaultAnalyzerName == null)
            defaultAnalyzerName = "org.apache.lucene.analysis.standard.StandardAnalyzer";
        defaultIndexAnalyzer = new SafetyAnalyzer((Analyzer)Class.forName(defaultAnalyzerName).newInstance());
    }
    return defaultIndexAnalyzer;
}

只是,它不起作用。我仍然会在这些令牌上获得IllegalArgumentExceptions。我能找到的唯一解决办法是在writer.addDocument()上抓住IAE。

我是否错误地编写了分析仪?

1 个答案:

答案 0 :(得分:0)

字段的长度小于字符中的IndexWriter限制,但超过字节的限制。 LengthFilter根据字符长度(UTF-16代码单位)过滤掉令牌。另一方面,IndexWriter.MAX_TERM_LENGTH采用UTF-8编码的字节。

此过滤器应该可以满足您的需求。与LengthFilter的工作方式相同,因此应该很容易将这样的内容放到SafetyAnalyzer中(它实际上只是LengthFilter的副本,其中包含经过修改的accept()法):

import java.nio.CharBuffer;
import java.nio.charset.Charset;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.util.FilteringTokenFilter;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;

public final class ByteLengthFilter extends FilteringTokenFilter {

  private final int min;
  private final int max;
  private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);

  public ByteLengthFilter(TokenStream in, int min, int max) {
    super(in);
    if (min < 0) {
      throw new IllegalArgumentException("minimum length must be greater than or equal to zero");
    }
    if (min > max) {
      throw new IllegalArgumentException("maximum length must not be greater than minimum length");
    }
    this.min = min;
    this.max = max;
  }

  @Override
  public boolean accept() {
    final int len = Charset.forName("UTF-8").encode(CharBuffer.wrap(termAtt)).remaining();
    return (len >= min && len <= max);
  }
}

警告:不确定在实践中表现如何。它肯定比LengthFilter更贵,后者只是使用CharSequence.length()来获取长度。