一、Lucene:是一個世界上最流行的開源的全文檢索框架java
官網網址:http://lucene.apache.orgmysql
版本:7.3.3sql
Jdk要求:1.8數據庫
一、好比購物商城:假設經過傳統的SQL語句進行書籍查詢的時候 ,輸入關鍵字‘Lucene實戰’,進行查詢的時候,查詢字段中的數據必須有 ‘Lucene實戰’這些關鍵字必須連在一塊。 這是經過sql語句搞不定的apache
二、性能問題:在對大數據進行檢索時,lucene的檢索速度明顯快於傳統的sql檢索api
三、實現高亮效果oracle
四、 作系統的站內檢索,即對一個系統內的資源進行搜索。框架
如BBS、BLOG中的文章搜索,網上商店中的商品搜索、OA系統中公文檢索、郵件檢索……post
一、解壓下載的Lucene壓縮包,獲得以下文件(Lucene/api/lucene-7.3.0)性能
analysis:分詞器
core:Lucene核心jar包
demo:Lucene示例
docs:Lucene使用文檔
highlighter:實現高亮效果
2、Lucene與mysql、oracle等數據庫的比較
-一、mysql、oracle等數據庫須要創建本身的數據庫以及表
-二、Lucene須要創建本身的索引庫
/** *添加索引(單字分詞器) */ private static void addIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分詞器 單字分詞器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引庫中添加記錄 Lucene中一個document實例表明數據表中的一條記錄 Document document=new Document(); /* *StringFiled:不會對關鍵字進行分詞 *TextFiled:會對關鍵字進行分詞 * * Store.YES:會將數據進行存儲並分詞 * Store.NO:不會將數據進行存儲,不會分詞,索引仍是會建立。有索引,沒有內容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "生活不止眼前的苟且", Store.YES)); document.add(new TextField("content", "媽媽坐在門前,哼着花兒與少年,雖已隔多年。", Store.YES));
indexWriter.addDocument(document);
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("添加成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 更新索引(單字分詞器) */ private static void updateIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分詞器 單字分詞器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引庫中添加記錄 Lucene中一個document實例表明數據表中的一條記錄 Document document=new Document(); /* *StringFiled:不會對關鍵字進行分詞 *TextFiled:會對關鍵字進行分詞 * * Store.YES:會將數據進行存儲並分詞 * Store.NO:不會將數據進行存儲,不會分詞,索引仍是會建立。有索引,沒有內容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "幽幽而來", Store.YES)); document.add(new TextField("content", "這世界真好呀", Store.YES));
//更新的原理:先刪除以前的索引,再建立新的索引====刪除與添加兩個動做的合集 indexWriter.updateDocument(new Term("articalId","0001"), document);
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("更新成功"); } catch (Exception e) { e.printStackTrace(); } }
|
/** * 刪除索引(單字分詞器) */ private static void deleteIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//建立分詞器 單字分詞器 Analyzer analyzer=new StandardAnalyzer(); //建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//刪除索引庫中指定的索引 indexWriter.deleteDocuments(new Term("articalId","0001"));
//刪除索引庫中所有的索引 //indexWriter.deleteAll();
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("刪除成功"); } catch (Exception e) { e.printStackTrace(); } } |
a. IndexWriter : 對索引庫進行CUD操做.
b. Directory : 存儲的目錄.
-- FSDirectory : 文件磁盤目錄。
-- RAMDirectory: 內存中。
c. IndexWriterConfig: 指定建立索引須要的配置信息.
d. Analyzer : 分詞器
e. OpenMode : 指定打開索引庫的模式。
-- OpenMode.CREATE : 建立(每次都會建立).
-- OpenModel.APPEND : 追加模式
-- OpenMode.CREATE_OR_APPEND: 建立或追加模式。
f. Document: 文檔
g. Store : 是否存儲
-- YES: 存儲
-- NO: 不存儲
/** * 全文檢索(單字分詞器) */ private static void searchIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定須要讀取的索引庫信息,並返回相應的實例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher實例,經過IndexSearcher實例進行全文檢索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//TermQuery:指定了查詢的關鍵字以及查詢哪個字段;不會對關鍵字進行分詞 Query query=new TermQuery(new Term("title","生")); /*經過indexSearch進行檢索,並指定兩個參數 *第一個參數:封裝查詢的相關信息,好比查詢的關鍵字、分詞器,是否須要分詞,或者須要分詞的話,採起什麼分詞器 *第二個參數:最多隻要多少條記錄 * *查詢數據,最終都封裝在TopDocs的實例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //經過topDocs獲取匹配所有記錄 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("獲取到的記錄數"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //獲取記錄的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//獲取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |
package org.crdit.com;
import java.util.Arrays;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.CharArraySet; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.cjk.CJKAnalyzer; import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute; import org.apache.lucene.util.BytesRef;
/** * @author crd * 分詞效果演示 */ public class AnalyzerTest { public static void main(String[] args) throws Exception { String str = "廣西桂林好玩的地方";
//單字分詞 //Analyzer analyzer = new StandardAnalyzer();
// 二分法分詞 //Analyzer analyzer = new CJKAnalyzer();
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar //Analyzer analyzer = new SmartChineseAnalyzer();
// 詞庫分詞 智能分詞器 Analyzer analyzer = new SmartChineseAnalyzer(new CharArraySet(Arrays.asList("的", "了","啊"), true));//使用自定義的停用詞集合
// 對指定的字符串進行分詞 TokenStream ts = analyzer.tokenStream(null, str);
ts.reset(); // 先要對tokenStream執行reset
// 採用循環,不斷地獲取下一個token while(ts.incrementToken()) { // 獲取其中token信息 TermToBytesRefAttribute attr = ts.getAttribute(TermToBytesRefAttribute.class); final BytesRef bytes = attr.getBytesRef(); System.out.println(bytes.utf8ToString()); } ts.close(); } }
|
英文分詞器:
a. 按空格來分詞。welcome to fkjava
b. 去掉停用詞(stop word).
is、an、a、the、of、的、地、嗎、嘛、了、得、標識符號(, ; :)
c. 大寫字母轉成小寫字母。
中文分詞器:
a、去掉停用詞(stop word).
is、an、a、the、of、的、地、嗎、嘛、了、得、標識符號(, ; :)
中華人民共和國,採用不一樣分詞器效果以下
單字分詞器:(StandardAnalyzer):中|華|人|民|共|和|國
二分法分詞(CJKAnalyzer):中華|華人|人民|民共|共和|和國
智能分詞器(SmartChineseAnalyzer):中華|華人|人民|共和國
以上項目代碼在code/01下
/** * 添加索引 */ private static void addIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb")); //詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引庫中添加記錄 Lucene中一個document實例表明數據表中的一條記錄 Document document=new Document(); /* *StringFiled:不會對關鍵字進行分詞 *TextFiled:會對關鍵字進行分詞 * * Store.YES:會將數據進行存儲並分詞 * Store.NO:不會將數據進行存儲,不會分詞,索引仍是會建立。有索引,沒有內容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "桂林好玩的地方", Store.YES)); document.add(new TextField("content", "能夠竹筏遊灕江,觀美景", Store.YES));
indexWriter.addDocument(document);
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("添加成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 更新索引 */ private static void updateIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//往索引庫中添加記錄 Lucene中一個document實例表明數據表中的一條記錄 Document document=new Document(); /* *StringFiled:不會對關鍵字進行分詞 *TextFiled:會對關鍵字進行分詞 * * Store.YES:會將數據進行存儲並分詞 * Store.NO:不會將數據進行存儲,不會分詞,索引仍是會建立。有索引,沒有內容。 */ document.add(new StringField("articalId", "0001", Store.YES)); document.add(new TextField("title", "幽幽而來", Store.YES)); document.add(new TextField("content", "這世界真好呀", Store.YES));
//更新的原理:先刪除以前的索引,再建立新的索引====刪除與添加兩個動做的合集 indexWriter.updateDocument(new Term("articalId","0001"), document);
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("更新成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 刪除索引 */ private static void deleteIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer();
//建立IndexWriterConfig實例 指定添加索引的配置信息 IndexWriterConfig config=new IndexWriterConfig(analyzer); //若是索引不存在,則建立。存在,則追加。 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
//建立IndexWriter IndexWriter indexWriter=new IndexWriter(directory, config);
//刪除索引庫中指定的索引 indexWriter.deleteDocuments(new Term("articalId","0001"));
//刪除索引庫中所有的索引 //indexWriter.deleteAll();
//提交事務 indexWriter.commit(); //關閉事務 indexWriter.close();
System.out.println("刪除成功"); } catch (Exception e) { e.printStackTrace(); } } |
/** * 全文檢索 */ private static void searchIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定須要讀取的索引庫信息,並返回相應的實例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher實例,經過IndexSearcher實例進行全文檢索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser實例 經過QueryParser能夠指定須要查詢的字段以及分詞器信息 QueryParser queryParser=new QueryParser("title", analyzer); //經過queryParser實例獲得query實例,並指定查詢的關鍵字 Query query =queryParser.parse("桂林有哪些好玩的地方"); /*經過indexSearch進行檢索,並指定兩個參數 *第一個參數:封裝查詢的相關信息,好比查詢的關鍵字、分詞器,是否須要分詞,或者須要分詞的話,採起什麼分詞器 *第二個參數:最多隻要多少條記錄 * *查詢數據,最終都封裝在TopDocs的實例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //經過topDocs獲取匹配所有記錄 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("獲取到的記錄數"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //獲取記錄的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//獲取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |
/** * 全文檢索 */ private static void searchIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定須要讀取的索引庫信息,並返回相應的實例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher實例,經過IndexSearcher實例進行全文檢索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser實例 經過MultiFieldQueryParser能夠指定須要查詢的多個字段以及分詞器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //經過queryParser實例獲得query實例,並指定查詢的關鍵字 Query query =queryParser.parse("桂林");
/*經過indexSearch進行檢索,並指定兩個參數 *第一個參數:封裝查詢的相關信息,好比查詢的關鍵字、分詞器,是否須要分詞,或者須要分詞的話,採起什麼分詞器 *第二個參數:最多隻要多少條記錄 * *查詢數據,最終都封裝在TopDocs的實例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //經過topDocs獲取匹配所有記錄 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("獲取到的記錄數"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //獲取記錄的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//獲取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } }
|
// 廣州 默認Field裏包含"廣州"關鍵詞 Query query1 = queryParser.parse("廣州"); // 景區 OR 廣州 默認Field裏包含"景區"或 "廣州" Query query2 = queryParser.parse("景區 廣州"); //+白雲山 +廣州 (白雲山 AND 廣州) AND必須爲大寫 默認Field裏包含"廣州"和"白雲山" Query query3 = queryParser.parse("廣州 AND 白雲山"); // title:廣州 title字段中包含廣州 Query query4 = queryParser.parse("title:廣州"); //title:廣州 -content:廣州|景區 (title:廣州 AND NOT content:廣州|景區 ) //(title裏包含廣州,但content裏不能包含"廣州 或者 景區") Query query5 = queryParser.parse("title:廣州 -content:廣州|景區"); |
導包:
lucene-highlighter-7.3.0.jar
lucene-memory-7.3.0.jar
/** * 全文檢索 */ private static void searchIndex() { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定須要讀取的索引庫信息,並返回相應的實例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher實例,經過IndexSearcher實例進行全文檢索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser實例 經過MultiFieldQueryParser能夠指定須要查詢的多個字段以及分詞器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //經過queryParser實例獲得query實例,並指定查詢的關鍵字 Query query =queryParser.parse("桂林");
//經過格式器指定咋樣對關鍵字進行處理 Formatter formatter=new SimpleHTMLFormatter("<font color='red'>", "</font>"); //經過Scorer包裝query Scorer fragmentScorer=new QueryScorer(query); //建立高亮器 Highlighter highlighter=new Highlighter(formatter, fragmentScorer);
//建立格式化片斷 Fragmenter fragmenter=new SimpleFragmenter(10);
//將格式化片斷實例與高亮器關聯,最終經過高亮器對文本信息進行處理 highlighter.setTextFragmenter(fragmenter);
/*經過indexSearch進行檢索,並指定兩個參數 *第一個參數:封裝查詢的相關信息,好比查詢的關鍵字、分詞器,是否須要分詞,或者須要分詞的話,採起什麼分詞器 *第二個參數:最多隻要多少條記錄 * *查詢數據,最終都封裝在TopDocs的實例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //經過topDocs獲取匹配所有記錄 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("獲取到的記錄數"+scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) { //獲取記錄的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//獲取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id);
String articalId= document.get("articalId"); //獲取標題信息,高亮前title String title= document.get("title"); //獲取內容信息,高亮前的content String content= document.get("content"); System.out.println("articalId:"+articalId+" 高亮前title:"+title+" 高亮前content:"+content);
/*經過高亮器對title和content進行高亮處理 *三個參數的含義: * 第一個:分詞器 * 第二個:哪個字段進行高亮 * 第三個:須要進行高亮的文本 * 備註:若是須要高亮的文本信息不包括查詢的關鍵字則getBestFragment會返回null */ String postTitle=highlighter.getBestFragment(analyzer, "title", title); String postContent=highlighter.getBestFragment(analyzer, "content", content);
System.out.println("articalId:"+articalId+" 高亮後title:"+postTitle+" 高亮後content:"+postContent);
} } catch (Exception e) { e.printStackTrace(); } }
|
/** * 全文檢索 * @param pageIndex 當前頁碼 * @param pageSize 每頁顯示的記錄數 */ private static void searchIndex(int pageIndex,int pageSize) { try { //指定索引所在目錄 Directory directory=FSDirectory.open(Paths.get("E:\\Lucene\\Lucene_db\\artical_tb"));
//DirectoryReader的open方法指定須要讀取的索引庫信息,並返回相應的實例 IndexReader indexReader=DirectoryReader.open(directory); //建立IndexSearcher實例,經過IndexSearcher實例進行全文檢索 IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//詞庫分詞 須要導jar包 lucene-analyzers-smartcn.jar Analyzer analyzer = new SmartChineseAnalyzer(); //建立QueryParser實例 經過MultiFieldQueryParser能夠指定須要查詢的多個字段以及分詞器信息 QueryParser queryParser=new MultiFieldQueryParser(new String[]{"title","content"}, analyzer); //經過queryParser實例獲得query實例,並指定查詢的關鍵字 Query query =queryParser.parse("桂林");
/*經過indexSearch進行檢索,並指定兩個參數 *第一個參數:封裝查詢的相關信息,好比查詢的關鍵字、分詞器,是否須要分詞,或者須要分詞的話,採起什麼分詞器 *第二個參數:最多隻要多少條記錄 * *查詢數據,最終都封裝在TopDocs的實例中 */ TopDocs topDocs=indexSearcher.search(query, 100); //經過topDocs獲取匹配所有記錄 ScoreDoc[] scoreDocs=topDocs.scoreDocs;
System.out.println("獲取到的記錄數"+scoreDocs.length);
//定義查詢的起始記錄號以及結束的記錄號 int startSize=(pageIndex-1)*pageSize; int endSize=pageIndex*pageSize>scoreDocs.length?scoreDocs.length:pageIndex*pageSize;
for (int i = startSize; i < endSize; i++) { //獲取記錄的id int id=scoreDocs[i].doc; System.out.println("id:"+id);
//獲取文章得分 float score=scoreDocs[i].score; System.out.println("score :"+score);
Document document=indexSearcher.doc(id); String articalId= document.get("articalId"); String title= document.get("title"); String content= document.get("content"); System.out.println("articalId:"+articalId+" title:"+title+" content:"+content); } } catch (Exception e) { e.printStackTrace(); } } |