Java 解决从Lucene 4.0升级到4.1后性能不佳的问题
从Lucene 4.0升级到4.1后,我的解决方案的性能下降了一个数量级以上。直接原因是存储字段的无条件压缩。目前我正在恢复到4.0,但这显然不是前进的方向;我希望找到一种不同的解决方法 我使用Lucene作为数据库索引,这意味着我存储的字段非常短:最多只有几个字 我使用Java 解决从Lucene 4.0升级到4.1后性能不佳的问题,java,lucene,Java,Lucene,从Lucene 4.0升级到4.1后,我的解决方案的性能下降了一个数量级以上。直接原因是存储字段的无条件压缩。目前我正在恢复到4.0,但这显然不是前进的方向;我希望找到一种不同的解决方法 我使用Lucene作为数据库索引,这意味着我存储的字段非常短:最多只有几个字 我使用CustomScoreQuery在CustomScoreProvider#customScore中加载所有候选文档,并对查询执行详细的单词相似性评分。我使用了两个层次的启发式方法来缩小候选文档集(基于),但在最后一步中,我需要将
CustomScoreQuery
在CustomScoreProvider#customScore
中加载所有候选文档,并对查询执行详细的单词相似性评分。我使用了两个层次的启发式方法来缩小候选文档集(基于),但在最后一步中,我需要将每个查询词与每个文档词进行匹配(它们的顺序可能不同),并根据最佳匹配词的总和计算总分
如何以不同的方式进行计算,以避免在查询求值期间加载压缩字段的陷阱?在中,您可以传入一个,它定义了索引要使用的存储方法。这仅在构造了IndexWriter
时生效(也就是说,构造后更改配置将无效)。你会想用的
比如:
//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);
您还可以直接使用获取旧的未压缩存储字段格式,并将其从自定义编解码器实现传回。您可以从中获取大部分代码,只需替换storedFieldFormat()
方法即可。可能是更具针对性的方法,但更为复杂,我不确定您是否会遇到其他问题
关于创建自定义编解码器的另一个注意事项,API指出您应该实现这一点的方式是扩展FilterCodec,并稍微修改他们的示例以适应:
公共最终类CustomCodec扩展了FilterCodec{
public CustomCodec() {
super("CustomCodec", new Lucene41Codec());
}
public StoredFieldsFormat storedFieldsFormat() {
return new Lucene40StoredFieldsFormat();
}
}
当然,我想到的另一个实现是:
我想你也很清楚,问题就在“我最终加载了所有候选文档”附近。我不会对一个计分实现进行太多的编辑,我没有完整的细节或理解,但听起来像是你在与Lucene的架构抗争,让它做你想做的事情。一般来说,存储字段不应用于评分,使用4.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
值,我用它来获取文档的bigram计数;它不允许检索字符串,所以我需要加载文档以获得计算文档相似性分数所需的内容。在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)。我首先尝试了
Lucene40Codec
,但正如其Javadoc所声称的,它是只读的。现在我正在编写一个定制的编解码器,但我的问题是我在Clojure上,所以它不是那么简单。关于你的最后一点,这确实是我的主要问题:我如何才能获得我所需要的分数,并与索引查找的思想一起发挥作用。我已经花了很多天和不眠之夜试图想出更好的3.6,没有运气。我希望4中可能有一些新的东西我可以使用。我不能很好地回答如何修改您的逻辑以使用索引字段而不是存储字段。我不清楚你想用它来完成什么。我认为,您指出的最后一步似乎是问题所在,您将每个查询词与每个文档词进行匹配,但这意味着要完成什么?具体来说,我将输入的街道名称与街道名称数据库进行匹配。我需要忽略单词顺序,甚至忽略遗漏的单词(例如,以一个人命名的街道通常只称为姓氏,但完整的街道名称也包含名字);另一方面,我必须知道“文档”(街道名称)中的所有单词,才能得出总分。您只使用字段中单词的子集进行搜索的情况是典型的。问题是,为什么你需要知道所有的术语?没有一种有效的方法可以将所有内容与文档一起存储。你能给我一些关于Lucene典型得分方案的不足之处吗?事实上,它不仅仅是一个子集;对遗漏的单词有一个小的惩罚(只有通过测试,我才确定惩罚应该是那么小)。还要注意的是,这是对称的:单词的两端都可能缺失。接下来,我事先不知道应该匹配文档中的哪些术语,因为它们都基于JaroWinklerDistance
。Lucene的得分毫无道理:我不需要考虑TF或IDF。我有一个自定义的相似性
,它将这些相似性修复为统一性。