从Lucene 4.0升级到4.1后解决了糟糕的性能

时间:2013-02-13 10:48:29

标签: java lucene

从Lucene 4.0升级到4.1后,我的解决方案的性能降低了一个数量级以上。直接原因是无条件压缩存储的字段。现在我恢复到4.0,但这显然不是前进的方向;我希望找到一种不同的解决方案。

我使用Lucene作为数据库索引,这意味着我存储的字段很短:最多只有几个字。

我使用CustomScoreQueryCustomScoreProvider#customScore中我最终加载所有候选文档并对查询执行详细的单词相似度评分。我使用两级启发式来缩小候选文档集(基于Dice's coefficient),但在最后一步中我需要将每个查询词与每个文档词匹配(它们可以按不同顺序)并计算总得分基于最佳单词匹配的总和。

我怎样才能以不同的方式处理这个问题并以避免在查询评估期间加载压缩字段的陷阱的方式进行计算?

2 个答案:

答案 0 :(得分:2)

IndexWriterConfig中,您可以传入Codec,它定义了索引要使用的存储方法。这仅在构造IndexWriter时生效(即,在构造后更改配置将不起作用)。您需要使用Lucene40Codec

类似的东西:

//You could also simply pass in Version.LUCENE_40 here, and not worry about the Codec
//(though that will likely affect other things as well)
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_41, analyzer);
config.setCodec(new Lucene40Codec());
IndexWriter writer = new IndexWriter(directory, config);

您还可以直接使用Lucene40StoredFieldsFormat获取旧的,未压缩的存储字段格式,并将其从自定义Codec实现中传回。您可以从Lucene41Codec获取大部分代码,只需替换storedFieldFormat()方法即可。可能是更有针对性的方法,但触摸更复杂,我不确定您是否可能遇到其他问题。

关于创建自定义编解码器的进一步说明,API指示您应该完成此操作的方式是扩展FilterCodec,并稍微修改它们的示例以适应:

public final class CustomCodec扩展了FilterCodec {

 public CustomCodec() {
   super("CustomCodec", new Lucene41Codec());
 }

 public StoredFieldsFormat storedFieldsFormat() {
   return new Lucene40StoredFieldsFormat();
 }

}


当然,我想到的其他实现:

我认为你也很清楚,问题是关于“我最终加载所有候选文件”。我不会对评分实施进行太多的编辑,我没有完整的细节或理解,但这听起来像是你在反对Lucene的架构,让它做你想做的事情。通常,存储的字段不应该用于评分,并且您可以预期使用4.0存储的字段格式会导致性能受到非常明显的影响,尽管程度稍低。可能有更好的实现,无论是在评分算法方面还是在文档结构方面,都将取消基于存储字段对文档进行评分的要求?

答案 1 :(得分:0)

使用Lucene 3.x我有这个:

new CustomScoreQuery(bigramQuery, new FieldScoreQuery("bigram-count", Type.BYTE)) {
  protected CustomScoreProvider getCustomScoreProvider(IndexReader ir) {
    return new CustomScoreProvider(ir) {
      public double customScore(int docnum, float bigramFreq, float docBigramCount) {
         ... calculate Dice's coefficient using bigramFreq and docBigramCount...
         if (diceCoeff >= threshold) {
           String[] stems = ir.document(docnum).getValues("stems");
           ... calculate document similarity score using stems ...
         }
      }
    };
  }
}

这种方法允许从存储的字段中有效地检索缓存的float值,我用它来获取文档的二元组计数;它不允许检索字符串,所以我需要加载文档以获得计算文档相似性得分所需的内容。它工作得很好,直到Lucene 4.1改为压缩存储的字段。

利用Lucene 4增强功能的正确方法是让DocValues像这样:

new CustomScoreQuery(bigramQuery) {
  protected CustomScoreProvider getCustomScoreProvider(ReaderContext rc) {
    final AtomicReader ir = ((AtomicReaderContext)rc).reader();
    final ValueSource 
       bgCountSrc = ir.docValues("bigram-count").getSource(),
       stemSrc = ir.docValues("stems").getSource();
    return new CustomScoreProvider(rc) {
      public float customScore(int docnum, float bgFreq, float... fScores) {
        final long bgCount = bgCountSrc.getInt(docnum);
        ... calculate Dice's coefficient using bgFreq and bgCount ...
        if (diceCoeff >= threshold) {
          final String stems = 
             stemSrc.getBytes(docnum, new BytesRef())).utf8ToString();
          ... calculate document similarity score using stems ...
        }
      }
    };
  }
}

这导致性能从16毫秒(Lucene 3.x)提高到10毫秒(Lucene 4.x)。