上篇文章(Lucene (created Index) & search Index, http://blog.sina.com.cn/s/blog_740c74b20101ggho.html )已经介绍了Lucene建立索引的所有过程了,这章将介绍如何搜索(Search)。
涉及到的类是IndexSearcher,同样的你首先需要知道你之前建立索引的具体位置,然后通过搜索索引来找到你需要的信息。因此,在Search过程中你仍需要构建Directory,以及Directory阅读器(DirectoryReader),通过调用DirectoryReader将Directory的信息读入,以用于之后的搜索。当然你也需要分析器(Analyzer),用于对你输入的关键词进行解析。
一、 查询(Query)
在做完这些基础设置之后,接下来我们将进行对关键字的查询(query),你可以直接使用这个类进行查询,具体代码如下:
QueryParser parser = new QueryParser(Version.LUCENE_40,"FieldID", analyzer);
Query query = parser.parse("keyWord");
PS: QueryParser类是用来设置你所要进行搜索的Field(e.g. new Field("FieldID","Value")),以及采用什么样的分析器(Analyzer)进行解析。换句话讲,类似于Mysql语句查询,QueryParser是在定义你搜索的属性范围,即搜索某一列数据,以及相应的分析方法。
二、 其他查询方式
除了上述方法外,Lucene还有其他一些Query类,如:TermQuery,BooleanQuery,RangeQuery以及WildcardQuery等。
1、TermQuery
TermQuery是Lucene里面最基本的一种原子查询。一般可以通过它来检索索引中含有指定词条的Document。具体代码如下:
Term term = new Term("FieldID","keyWord");
Query termQuery = new TermQuery(term);
2、BooleanQuery
顾名思义,就是可附带逻辑关系的查询方法,你可以选择“或”,“与”之类的逻辑来查询你的关键字。具体代码如下:
Term term1 = new Term("FieldID","keyword1");
Term term2 = new Term("FieldID","keyword2");
TermQuery termquery1 = new TermQuery(term1);
TermQuery termquery2 = new TermQuery(term2);
BooleanQuery blquery = new BooleanQuery();
blquery.add(termquery1,BooleanClause.Occur.MUST);
blquery.add(termquery2,BooleanClause.Occur.MUST);
上面这串代码的意思就是两个关键字必须同时出现。
PS: 其中对于逻辑的选择除了
BooleanClause.Occur.MUST 外还有BooleanClause.Occur.MUST_NOT,
BooleanClause.Occur.SHOULD等等。
3、RangeQuery
也是顾名思义,范围查询,即在一定范围内进行查询,比如1~10范围,或者什么,当然你也可以尝试两段文字之间的范围查询(笔者没有尝试过)。下面是具体代码:
Term term1 = new Term("FieldID","keyword1");
Term term2 = new Term("FieldID","keyword2");
RangeQuery query = new RangeQuery(term1,term2,true);
PS: 这里解释一下RangeQuery构造函数最后一个参数的意义。它指的是是否包含边界值。
三、 Score相关介绍及查询结果展示
在介绍完查询方式之后,我们就可以对关键词进行相应的搜索了,在这里笔者特意想向大家介绍一下Lucene的一个特别有用的东西,即Score。对于每一条搜索的结果,Lucene都对其做出了相应的打分,以评价该搜索结果与关键词有多大的相关性(这是一个非常重要的功能)。打分的具体函数如下:
coord(q,d):
评分因子,是基于文档中出现查询项的个数。越多的查询项在一个文档中,说明该文档的匹配程序越高。默认是出现查询项的百分比。
queryNorm(q): 查询的标准查询,使不同查询之间可以比较。此因子不影响文档的排序,因为所有有文档都会使用此因子。
queryNorm(q): 查询的标准查询,使不同查询之间可以比较。此因子不影响文档的排序,因为所有有文档都会使用此因子。
tf(t in d):
查询的词在文档中出现的次数,
默认取根号值。
idf(t):
表示反转文档频率,
文档频率指出现查询词
的文档数。文档数越少 idf 就越高,但在同一个查询下些值是相同的。
t.getBoost():
激励因子,可以通过setBoost方法设置(通过field和doc都可以设置),即给查询词加权(一般不怎么使用)。
norm(t,d):
压缩几个索引期间的加权和长度因子。
PS:说白了就是一种打分机制:单个文档中出现的频率越高相关性越大,多个文档中都出现的话就没什么重要性了。
具体应用代码如下:
Hits hits = isearcher.search(query);
if(hits.length>0){
for(int i=0;i
Document hitDoc = hits.doc(i);
System.out.println("Score:"+hits.score(i)+"/"+hitDoc.get("FieldID"));
}
}
PS:
(1)
isearcher 是 IndexSearcher 的一个对象,query就是之前定义的Query的一个对象。
(2) hits 中存储的是所有的搜索结果(其实还有另外一种表示方式,最后提供的全套代码里采用的就是另外一种方式)。
(3) hits.score(i)的意思就不用多说了吧~
(4) hitDoc.get("FieldID"),调用这个函数就能显示你所搜索的含有 关键词 的相关文段。
下面是完整代码:
package lxwo.lucenecore;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
public class SearchIndex {
private Analyzer analyzer;
private IndexSearcher isearcher;
private int MaxFieldsNum;
@SuppressWarnings("deprecation")
public SearchIndex(String path1, String path2) throws Exception {
// step1: initiate the directory of Index
Directory directoryHD = FSDirectory.open(new File(path1));
// step2: initiate the analyzer
analyzer = new StandardAnalyzer(Version.LUCENE_40);
// ============start searching=================================
// step1: initiate the IndexSearcher
DirectoryReader ireader = DirectoryReader.open(directoryHD);
isearcher = new IndexSearcher(ireader);
BufferedReader br = new BufferedReader(new FileReader(new File(path2)));
String strLine = null;
if ((strLine = br.readLine()) != null)
this.MaxFieldsNum = Integer.valueOf(strLine);
br.close();
}
public void search(String keyWord) throws Exception {
// step2: initiate the QueryParser
for (int index = 0; index < this.MaxFieldsNum; index++) {
@SuppressWarnings("deprecation")
QueryParser parser = new QueryParser(Version.LUCENE_40,
index+"", analyzer);
// step3: customize the search query
Query query = parser.parse(keyWord);
// step4: searching
ScoreDoc[] hits = isearcher.search(query, 10000000).scoreDocs;
if(hits.length>0){
System.out.println("Fields:"+index+"/Hits: "+hits.length);
for(int i=0;i
Document hitDoc = isearcher.doc(hits[i].doc);
System.out.println("Score: "+hits[i].score+"/"+hitDoc.get(index+""));
}
}
}
}
public void searchAndWrite(String keyWord,File file) throws Exception {
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
// step2: initiate the QueryParser
for (int index = 0; index < this.MaxFieldsNum; index++) {
@SuppressWarnings("deprecation")
QueryParser parser = new QueryParser(Version.LUCENE_40,
index+"", analyzer);
// step3: customize the search query
Query query = parser.parse(keyWord);
// step4: searching
ScoreDoc[] hits = isearcher.search(query, 100000000).scoreDocs;
if(hits.length>0){
bw.append("Fields:"+index+"/Hits: "+hits.length);
bw.newLine();
for(int i=0;i
Document hitDoc = isearcher.doc(hits[i].doc);
bw.append("Score: "+hits[i].score+"/PMID: "+hitDoc.get("0")+"/Title: "+hitDoc.get("1"));
bw.newLine();
bw.append("Sentence: "+hitDoc.get(index+""));
bw.newLine();
}
}
}
bw.close();
}
}
big - PS:有些关键词例如:“AB/C”,“NOT”等等可能Lucene不好解析,需要再做一些处理,这里就暂时提一句,日后再详细解说。