原文地址:http://rritw.com/a/bianchengyuyan/C__/20121201/263564.html
項目中都會有搜索的功能,有些搜索非常簡單,就是按照姓名查詢或者按着性別查詢。這樣的查詢我們會用到模糊查詢,也就是like。如果是兩個也就是用like 和or關鍵字。
Like關鍵字是非常影響效率的,這點我們可以從一個生活中的例子來看就能夠知道。我們使用like關鍵字就好像是我們在查一本沒有目錄的字典,我們要在從字典的第一頁開始,一個一個的找,知道找出所有符合條件的結果。所以說like關鍵字是非常影響效率的。
那麼我們在生活中比較正規的方法查字典的方法是,我們會通過目錄(這個目錄可能是拼音或者偏旁)查詢,進而一部定位結果。
所以,lucene給我們提供了這樣一種比較符合於我們生活中的方式來查閱信息的一種實現。也就是說lucene的出現就是方便我們快速檢索信息。他主要完成的工作就是首先對我們的整個內容建立一個索引(這個索引就相當於我們生活中的那個字典的目錄),之後我們通過目錄可以直接定位並且查出我們所需要的結果。這就是lucene出現的意義所在。
實際上,我們所看到的穀歌、百度、還有一個搜索的工具如穀歌桌面搜索、everything所實現的原理都是這樣的,我們在用這些工具的時候他們也是通過這種思路來實現搜索功能的。
Lucene架構:
他的架構圖還是比較簡單的,我們從圖中就能看出,lucene的架構就是兩方面內容:
收集數據--->建立索引--->存儲索引
用戶查詢--->搜索索引---->從數據庫中查詢---->呈現搜索結果---->用戶
Lucene中有這樣一些核心類,通過他們我們可以完成lucene架構圖中展示的建立索引和搜索功能:
另外在我們建立索引的時候會涉及到一個是否要建立索引的問題和一個是否要存儲的問題,一下的紅色字體部分爲這兩個知識點的解釋:
Index.ANALYZED:進行分詞和索引,適用於標題、內容等
Index.NOT_ANALYZED:進行索引,但是不進行分詞,如果身份證號,姓名,ID等,適用於精確搜索
Index.ANALYZED_NOT_NORMS:進行分詞但是不存儲norms信息,這個norms中包括了創建索引的時間和權值等信息
Index.NOT_ANALYZED_NOT_NORMS:即不進行分詞也不存儲norms信息
Index.NO:不進行索引
YES:將會存儲域值,原始字符串的值會保存在索引,以此可以進行相應的恢復操作,對於主鍵,標題可以是這種方式存儲
NO:不會存儲域值,通常與Index.ANAYLIZED合起來使用,索引一些如文章正文等不需要恢復的文檔
NOT_ANALYZED_NOT_NORMS |
YES |
標識符(主鍵、文件名),電話號碼,身份證號,姓名,日期 |
ANAYLZED |
YES |
文檔標題和摘要 |
ANAYLZED |
NO |
文檔正文 |
NO |
YES |
文檔類型,數據庫主鍵(不進行索引) |
NOT_ANALYZED |
NO |
隱藏關鍵字 |
另外,強大的lucene全文搜索工具中還有一個非常重要的概念就是分詞,這個概念可以說是搜索中非常關鍵的部分。分詞器做好處理之後得到的一個流,這個流中存儲了分詞的各種信息,可以通過TokenStream有效的獲取到分詞單元信息。這個過程如圖所示:
Lucene中帶有的分詞器有SimpleAnalyzer、StopAnalyzer、WhitespaceAnalyzer、StandardAnalyzer(這個就部分別給大家做事例說明他們的區別了,大家可以自己研究一下)。另外lucene支持我們自定義分詞器。而且lucene中自帶分詞器一般是支持英文的,那麼我們中文分詞器比較常見的有兩種,一個是庖丁解牛分詞器【沒有更新了】,一個是mmseg4j【使用的是穀歌詞庫】分詞器。
和lucene相關的工具還有tika,solr,luke(下面介紹):
Tika是apache的一個項目,主要是用於打開各種不同的文檔,以便lucene做索引;solr是一個全文搜索的服務器,可以與tomcat集成。還有就是luke,這個工具主要是查看lucene建立的索引的。
最後做一個簡單的建立索引和簡單查詢的實例:
用一個簡單的實例來建立索引:
1、創建Directory
2、創建IndexWriter
3、循環遍曆文件,創建document
4、添加document(文檔相當於表中的每一條記錄,域相當於表中每一個字段)
5、 關閉IndexWriter
publicvoid index(){
IndexWriter writer=null;
try {
//Directory dir = new RAMDirectory();
Directory dir=FSDirectory.open(new File("d:/java/lucence/index01"));
IndexWriterConfig iwc=new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36));
writer=new IndexWriter(dir, iwc);
Document doc=null;
File file=new File("d:/java/lucence/example");
for(File f:file.listFiles()){
doc=new Document();
doc.add(new Field("content", new FileReader(f)));
doc.add(new Field("filename", f.getName(),Field.Store.YES,Field.Index.NOT_ANALYZED));
doc.add(new Field("path",f.getAbsolutePath(),Field.Store.YES,Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
}
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(writer!=null){
try {
writer.close();
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
建立索引爲:
這些索引如何來看呢?
用lukeall-3.5.0.jar 這個工具(注意要和lucene版本一致)
搜索的簡單實現:
1、 創建IndexReader
2、 創建IndexSearcher
3、 創建Term和TermQuery
4、 根據TermQuery獲取TopDocs
5、 根據TopDocs獲取ScoreDoc
6、 根據ScoreDoc獲取相應文檔
publicvoid searcher(){
try {
Directory dir=FSDirectory.open(new File("d:/java/lucence/index01"));
IndexReader reader=IndexReader.open(dir);
IndexSearcher searcher=new IndexSearcher(reader);
QueryParser parser=new QueryParser(Version.LUCENE_36, "content", new StandardAnalyzer(Version.LUCENE_36));
Query query=parser.parse("java");
TopDocs tds=searcher.search(query, 10);
ScoreDoc[] sds=tds.scoreDocs;
for(ScoreDoc sd:sds){
Document d=searcher.doc(sd.doc);
System.out.println(d.get("filename")+"~~~"+d.get("path"));
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
備注:lucene工具支持很多查詢:通配符、數字、日期、範圍、模糊等:
最後要說的是,這篇博客僅僅簡單介紹了一下lucene這個工具,關於深層次的使用問題,我想還是要靠大家自己完成。當我們知道lucene這個工具的時候,面對我們的問題也就有很多了,像我們如何完成實時搜索,如何完成像百度推廣似的排名靠前,如何完成高亮顯示,如何更好的保證搜索質量和效率等,我想這個問題都是比較不太容易完美實現的問題,所以這些問題還有待大家共同來思考