今天在用Solr5.1测试检索时,发现一个奇怪的问题,便于大家对比,先介绍下散仙的环境:
JDK1.7
Lucene5.1
Solr5.1
分词器用的ik(改的ik源码)
先看下测试的5条数据:
id,name,count
1503486364953346048,北京奇虎测腾科技有限公司,1
1503486365060300800,北京奇虎网力科技有限公司,2
1503486365065543680,北京奇虎科技有限公司,3
1503486826976903168,中国,7
1503486827011506176,中国人,8
使用IK(细粒度切词如下:)
北京 奇虎 测 腾 科技 有限公司 有限 有 限 公司
======================
北京 奇虎 网 力 科技 有限公司 有限 有 限 公司
======================
北京 奇虎 科技 有限公司 有限 有 限 公司
======================
中国人 中国 国人
======================
中国
OK,然后,大家想一个场景,假如我在搜索框里面搜索 北京奇虎 时,第几个搜索结果应该被排在第一位?大部分情况下我们都希望第三个结果,排在第一位,但结果会是这样吗?
不好意思,经实际验证,在Lucene5.1作为底层的Solr中,却并不是这样,截图如下:
等等,刚才里面不是还有一个中国人和中国的例子吗,我们搜个中国测试下,事实是这样吗?
奇怪了,因为在Lucene评分中,字段的长度也是影响评分的因素,默认情况下,是文本越短的会排在前面,但是上面两个例子中,竟然有一个出了异常,这难道是一个bug么?
在lucene的评分因素中,主要有如下几个因子:
1,tf
2,idf
3,coord
4,queryNorm
5,boost
6,norm(t,d)
7,payload
8,自定义评分
具体的介绍,请参考我以前的文章:
http://qindongliang.iteye.com/blog/2008396
针对本文这个评分问题,我们主要关注norm这个评分因子,因为它里面包含了长度因子的评分:
norm(t,d)压缩几个索引期间的加权和长度因子:
Document boost - 文档加权,在索引之前使用 doc.setBoost()
Field boost - 字段加权,也在索引之前调用 field.setBoost()
lengthNorm(field) - 由字段内的 Token 的个数来计算此值,字段越短,评分越高,在做索引的时候由 Similarity.lengthNorm 计算。
以上所有因子相乘得出 norm 值,如果文档中有相同的字段,它们的加权也会相乘:
norm(t,d) = doc.getBoost() · lengthNorm(field) · ∏ f.getBoost()
field f in d named as t
索引的时候,把 norm 值压缩(encode)成一个 byte 保存在索引中。搜索的时候再把索引中 norm 值解压(decode)成一个 float 值,这个 encode/decode 由 Similarity 提供。官方说:这个过程由于精度问题,以至不是可逆的,如:decode(encode(0.89)) = 0.75。
接下来,查看Lucene的DefaultSimilarity类源码,看下核心的几个方法代码
/** Cache of decoded bytes. */
private static final float[] NORM_TABLE = new float[256];
static {
for (int i = 0; i < 256; i++) {
NORM_TABLE[i] = SmallFloat.byte315ToFloat((byte)i);
}
}
//索引期间执行,将norm编码成一个8位字节
public final long encodeNormValue(float f) {
return SmallFloat.floatToByte315(f);
}
//搜索期间执行,将norm,还原成具体的分数,参与评分
public final float decodeNormValue(long norm) {
return NORM_TABLE[(int) (norm & 0xFF)]; // & 0xFF maps negative bytes to positive above 127
}
仔细看decodeNormValue方法,这个代码,发现里面竟然有将float强制转换为int一个强转,这意味着,精度损失。
什么意思?请看如下代码:
float a=1.524f;
float b=1.589f;
System.out.println((int)a);
System.out.println((int)b);
//结果都是1
知道这个东东后,就发现,文头题的那个问题,没错,就是因为两者的norm编码相差太少,所以导致他们的解码分数一样,从而出现了,排名问题,这也不能算Lucene的bug,可能Lucene的设计者认为,两个norm,相差的值只有大于0.1的情况下,才真正管用,否则一视同仁。
这就是答案了,所以如果想解决上面的问题,可以重新定义一个自己的评分类,并加入自己的逻辑,不建议直接修改源码实现。
最后,给出,本文测试的全部代码,以供参考:
package com.lucene.opera;
import java.io.StringReader;
import java.nio.file.Paths;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.TextField;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.similarities.DefaultSimilarity;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class LuceneTest {
static final String indexpath="D:\\tmp\\luceneindex";
public static void index()throws Exception{
Directory dir=FSDirectory.open(Paths.get(indexpath));
IKAnalyzer ik=new IKAnalyzer(false);
IndexWriterConfig iwc=new IndexWriterConfig(ik);
iwc.setOpenMode(OpenMode.CREATE);
IndexWriter iw=new IndexWriter(dir, iwc);
// FieldType type=new FieldType();
// type.setTokenized(true);
// type.setStored(true);
// type.setIndexOptions(IndexOptions.DOCS);
// type.setOmitNorms(true);
// type.freeze();
//
Document doc=null;
// Document doc=new Document();
// doc.add(new TextField("name", "北京奇虎测腾科技有限公司", Store.YES));
//// doc.add(new Field("pname", "北京奇虎测腾科技有限公司", type));
// iw.addDocument(doc);
//
// doc=new Document();
// doc.add(new TextField("name", "北京奇虎网力科技有限公司", Store.YES));
//// doc.add(new Field("pname", "北京奇虎网力科技有限公司", type));
// iw.addDocument(doc);
//
////
// doc=new Document();
// doc.add(new TextField("name", "北京奇虎科技有限公司", Store.YES));
//// doc.add(new Field("pname", "北京奇虎科技有限公司", type));
// iw.addDocument(doc);
////
doc=new Document();
doc.add(new TextField("name", "北京奇虎测腾科技有限公司", Store.YES));
// doc.add(new Field("pname", "中国", type));
iw.addDocument(doc);
doc=new Document();
doc.add(new TextField("name", "北京奇虎科技有限公司", Store.YES));
// doc.add(new Field("pname", "中国人", type));
iw.addDocument(doc);
iw.commit();
iw.close();
System.out.println("索引成功!");
}
public static void search(String kw)throws Exception{
Directory dir=FSDirectory.open(Paths.get(indexpath));
IKAnalyzer ik=new IKAnalyzer(false);
IndexReader ir=DirectoryReader.open(dir);
IndexSearcher search=new IndexSearcher(ir);
// search.setSimilarity(new DefaultSimilarity());
QueryParser qr=new QueryParser("name", ik);
Query parse = qr.parse(kw);
System.out.println(parse.toString());
// Explanation explain = search.explain(parse , 100);
// System.out.println(explain.toString());
TopDocs td=search.search(parse, 100);
for(ScoreDoc sd:td.scoreDocs){
int i=sd.doc;
Document doc=search.doc(i);
// Explanation explain = search.explain(parse , i);
// System.out.println(explain.toString());
System.out.println(" 名称: "+doc.get("name")+" 评分: "+sd.score);
}
ir.close();
dir.close();
}
public static void main(String[] args)throws Exception {
// float []s=DefaultSimilarity.NORM_TABLE;
// for(int i=0;i<s.length;i++){
// System.out.println(i+" "+s[i]);
// }
// index();
// search("奇虎");
//
float a=1.524f;
float b=1.589f;
System.out.println((int)a);
System.out.println((int)b);
//结果都是1
// test("北京奇虎测腾科技有限公司");
// System.out.println("======================");
// test("北京奇虎网力科技有限公司");
// System.out.println("======================");
// test("北京奇虎科技有限公司");
// System.out.println("======================");
// test("中国人");
// System.out.println("======================");
// test("中国");
}
// "北京奇虎测腾科技有限公司"
// "北京奇虎网力科技有限公司"
// "北京奇虎科技有限公司"
// "中国"
// "中国人"
/****
* 测试分词token
* @param kw
* @throws Exception
*/
public static void test(String kw)throws Exception{
IKAnalyzer ik=new IKAnalyzer(false);
TokenStream token=ik.tokenStream("", new StringReader(kw));
CharTermAttribute term=token.addAttribute(CharTermAttribute.class);
token.reset();
while(token.incrementToken()){
System.out.print(term.toString()+" ");
}
System.out.println();
token.end();
token.close();
}
}
最后欢迎大家扫码关注微信公众号:我是攻城师(woshigcs),我们一起学习,进步和交流!(woshigcs)
本公众号的内容是有关搜索和大数据技术和互联网等方面内容的分享,也是一个温馨的技术互动交流的小家园,有什么问题随时都可以留言,欢迎大家来访!
- 大小: 406.4 KB
- 大小: 281.3 KB
分享到:
相关推荐
### 基于Lucene的搜索策略研究 随着信息技术的发展和社会信息化程度的加深,人们面临着海量数据的管理和检索挑战。如何高效地从大量的文档中找到所需的信息成为了亟待解决的问题。在这种背景下,Lucene作为一种开源...
《Lucene5学习之Highlighter关键字高亮》 在信息技术领域,搜索引擎的使用已经变得...对于深入研究和使用Lucene的开发人员而言,理解并掌握Highlighter的工作原理和使用方法,无疑能提升搜索应用的质量和用户体验。
1. **分词技术**:对于中文等复杂语言,采用合理的分词算法对提高搜索效果至关重要。 2. **关键词提取**:从文档中提取关键词,用于提高索引质量和搜索效率。 3. **同义词处理**:解决用户查询词与文档实际内容不...
此外,还需要利用中文分词技术对抓取的中文网页内容进行处理,以提高搜索效果。 综上所述,垂直搜索引擎的设计和实现是一项综合性强的工作,涉及到爬虫技术、信息抽取、索引建立等多个方面。通过合理运用 Heritrix ...
- **真实应用场景**:通过多个具体案例,展示了Lucene在实际项目中的应用效果,包括性能优化、用户体验提升等方面的经验分享。 综上所述,“Lucene in Action”不仅是一本理论性强的技术书籍,更是实践指导手册,...
### Lucene与中文分词技术的研究及应用 #### Lucene简介与原理 Lucene是一款高性能、全...综上所述,通过深入研究Lucene及其在中文环境下的应用,不仅可以提升全文搜索系统的性能,还能更好地满足特定用户的需求。
总之,Lucene的BM25示例是一个极好的学习资源,它涵盖了从索引构建到查询执行的关键步骤,并通过实际对比展示了如何使用更先进的相似度算法提升搜索效果。对于希望在文本检索领域深入研究或应用Lucene的开发者来说,...
综上所述,"lucene Demo (Paoding)"项目不仅提供了一个完整的Lucene搜索应用实例,还特别突出了中文搜索场景下的解决方案,对于学习和掌握Lucene以及中文分词技术具有很高的参考价值。通过深入研究这个项目,开发者...
《深入剖析Lucene 5.0源码》 Lucene是一个高性能、全文检索库,它为Java开发者提供了在应用程序中实现全文检索功能的工具。...通过深入研究,开发者可以掌握如何构建高效、智能的搜索系统,为各种应用场景带来价值。
《深入剖析Lucene全文检索案例源码》 在信息技术领域,全文检索技术是搜索引擎的核心,而Lucene作为Java平台上的一个...通过源码的深入研究,有助于我们在实际开发中更好地运用Lucene,提升搜索系统的性能和用户体验。
与Lucene结合,PanGu能够提升Lucene在中文文本处理上的效果,提高搜索的准确性和召回率。 在"Lucene-PanGu-master"这个压缩包中,我们可以预见到包含了一个Lucene与PanGu集成的项目源码。这通常会包含以下部分: 1...
《深入理解Lucene C#与.NET环境下的应用实践》 ...通过研究和应用这个压缩包中的内容,开发者可以深入了解Lucene C#在.NET环境下的工作原理,提升自己的搜索技术能力,为构建高效、智能的信息检索系统打下坚实的基础。
标题“LUCENE的新闻搜索引擎的实现”揭示了本文的核心内容,即使用Apache Lucene这一开源全文检索库来构建一个专门针对新闻数据的搜索引擎。Lucene是一个强大的信息检索工具,它提供了丰富的搜索功能和高效的索引...
《基于Lucene的医疗搜索引擎排序算法的研究》 在信息技术飞速发展的今天,搜索引擎已经成为人们获取信息的重要工具。尤其是在医疗领域,精准、高效的搜索结果对于医生的诊断和患者的信息查询至关重要。Lucene,作为...
【摘要】中提到的研究主要关注如何提升医疗行业的搜索引擎性能,特别是改善...通过中文分词技术的优化和概率模型的应用,研究人员成功地提高了Lucene在医疗搜索中的表现,这对于智慧医疗和健康信息检索具有重要意义。
5. **案例研究**:可能提供一些实际应用场景的案例,帮助理解Nutch和Lucene在不同场景下的应用和优化。 通过学习和实践这些光盘资源,你可以掌握以下知识点: 1. **Lucene的原理和使用**:理解Lucene的索引结构、...
汉纳拼音Lucene插件(hanlp-lucene-plugin)是一个专为中文处理设计的扩展工具,它将著名的HanLP中文分词库与流行的全文检索框架Lucene进行了集成,旨在提升在Lucene及其衍生产品(如Solr)中的中文处理能力。...