一、search设计的主要的类
IndexSearcher 搜索的关键途径,所有的search都必须通过它
QueryParser将自然语言转化为一个Query对象。
Query 用于searcher的对象,有很多子类。
TopDocs 包括所有的已经几分的子类
ScoreDoc 可以使用它访问返回的document
具体的过程如下:
//build a indexSearch on a indexReader
QueryParserparser = new QueryParser(Version.LUCENE_36,"contents",
newStandardAnalyzer(Version.LUCENE_36));
什么是term?
term其实就是一个<String,string>的pair。 在经过analyzer分析过的term都经过了删除term,大写变小写,转换到单词的词根等操作。有可能首字母大写的搜不出来。
searcher 返回的是一个TopDocs对象,这个对象包括了经过排序的搜索结果,排序的默认是分数按照从大到小排列的,这个分数是相对分数。
{
IndexSearcher searcher = newIndexSearcher(this.indexReader);
//Term t = newTerm(fieldName,q.toLowerCase());
Query query = parser.parse(q);
TopDocs hits =searcher.search(query, 20);
System.out.println("searchresult:");
for(ScoreDoc doc :hits.scoreDocs)
{
Document d =searcher.doc(doc.doc);
System.out.println(d.get("contents"));
}
搜索一个term
分析一个用户输入
分析过程如下所示,分析后会生成一个Query对象供IndexSearcher使用。
二、IndexSearcher 基本使用方法是
QueryParser praser = newQueryParser(Version.LUCENE_CURRENT,"contents",newStandardAnalyzer(Version.LUCENE_CURRENT));
Query query = praser.parse("Ilove you");
由Analyzer分析出来的 term 默认是 OR的关系,比如 ”I love you“ 分词后是 I OR love OR you 或的关系
可以使用parser.setDefaultOperator(QueryParser.AND_OPERATOR);修改为AND的关系。
也可以使用其他表达
例如
Query query =praser.parse("+bridge -Amsterdam");
Query query =praser.parse("bridge And Amsterdam");
他们的意思如下图
三、IndexSearch 与 IndexReader
打开索引文件(index)的任务是indexReader做的,使用indexReader打开index是非常耗时的,因为有打开文件的IO操作。所以最好多个IndexSearcher公用一个indexRader。
例如
IndexReader reader =IndexReader.open(dir);
IndexSearcher searcher1 = newIndexSearcher(reader);
IndexSearcher searcher2 = newIndexSearcher(reader);
如果使用 IndexSearcher searcher= IndexSearcher(dir);
会创建这个indexSearcher的私有的indexReader。
这个searcher关闭了相应的私有的indexReader也就关闭了。
前面提到过 indexReader自己自己做好了同步操作,不需要担心这些。
他们的关系如下
如果想包括当前的最新的index可以使用indexReader.reopen(),来包含所有的改变。
例如:
publicvoid testReopen() throws ParseException,IOException
{
IndexSearcher searcher = newIndexSearcher(this.indexReader);
QueryParser praser = newQueryParser(Version.LUCENE_CURRENT,"contents",newStandardAnalyzer(Version.LUCENE_CURRENT));
//note
Queryquery = praser.parse("+bridge -Amsterdam");
System.out.println("query = " +query.toString());
TopDocshits = searcher.search(query, 20);
//reopen a index and will cover current modification ofindex.
IndexReader newReader =indexReader.reopen();
if(indexReader != newReader)
{
indexReader = newReader;
// ifindexReader is changed , searcher must beconstructed.
searcher.close();
searcher =null;
searcher =new IndexSearcher(this.indexReader);
}
hits =searcher.search(query, 20);
showResult(hits,searcher);
}
四、TopDocs andScoredDoc
TopDocs hits = searcher.search(query,20);
每一个ScoreDoc存储命中的Document的基本信息。
doc是docment的号(这个在merge后是可以改变的)
可以使用Document d =searcher.doc(doc.doc);取得需要的Document
score是用于排序的分数。
五、给document打分--Similarity类
给文档打分从而给搜索到的文档排序这个是搜索引擎最难做到的,因为这决定了绝大部分搜索的用户体验。毕竟一个搜索引擎搜索速度比另一个快0.01秒,对用户毫无影响。而搜索到的结果与用户的相关性才是王道。
lucene给search的document排序的依据是分数,而这个分数反应了document与查询的Query相似性的大小。
Lucene就是使用Similarity给文档打分从而确定相似性的,lucene默认是使用DefaultSimilarity类来给打分的。
我们也可以自己决定打分类只需要继承自Similarity类即可。
DefaultSimilarity 的计算的分数以下面这些因素有关
1、tf(t in d) tf就是term frequency ,比如一个term =("content","mother").在某个field中出现了N次那么tf就等于N
2、coord(q, d)与query中的term命中的数量,比如query中有“mother”,“father”,“son”,某document中含有“son”那么coord就是1
3、lengthNorm(t.field in d) lengthNorm 其实是norm/length的意思,length表示某个document中的term的总数,leng越长,lengthNorm越小。为神马呢?比如两篇文章同时match了3个term,第一篇文章只有6个term,另一个有100个term,那么第一篇有50%的match了,而第二篇只有3%。所以lenth越小分数越大。
4、boost(t.field ind) 在boost时候设置的 boost值
详情看看DefaultSimilarity这个类。
要看看某一个doc的分数是如何计算出来的,可以使用Explanation类
for(ScoreDoc match : topDocs.scoreDocs)
Explanation explanation=searcher.explain(query, match.doc);Document doc =searcher.doc(match.doc);System.out.println(doc.get("title"));System.out.println(explanation.toString());
}
结果
0.61658424= (MATCH) fieldWeight(contents:junit in 9), productof:
1.0 = tf(termFreq(contents:junit)=1)
2.466337 = idf(docFreq=2, maxDocs=13)
0.25 = fieldNorm(field=contents, doc=9)