1Lucene的介紹java
①Lucene是什麼:spring
是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎
②Lucene有什麼用數據庫
Lucene是一個高性能、可伸縮的信息搜索(IR)庫。它能夠爲你的應用程序添加索引和搜索能力,和對搜索詞進行分析過濾
③Lucene怎麼用apache
1 // Lucene使用步驟 2 // 1建立索引 3 // 1.1建立索引目錄 4 Directory directory=FSDirectory.open(new File("indexDir")); 5 // 1.2建立indexWriter 6 IndexWriterConfig conf=new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)); 7 IndexWriter indexWriter=new IndexWriter(directory, conf); 8 // 1.3建立document 9 Document document=new Document(); 10 // 1.4爲document指定不一樣的域(Field) 11 document.add(new Field("fileName","java.txt",Store.YES,Index.ANALYZED)); 12 document.add(new NumericField("creatDate",Store.YES,true).setLongValue(new Date().getTime())); 13 document.add(new NumericField("size",Store.YES,true).setDoubleValue(10101.22)); 14 document.add(new Field("content",FileUtils.readFileToString(new File("java.txt")),Store.NO,Index.ANALYZED)); 15 // 1.5使用indexWriter.add(doc)方法,添加索引 16 indexWriter.addDocument(document); 17 // 1.6關閉indexWriter 18 indexWriter.close(); 19 // 2搜索索引 20 // 2.1指定索引存放位置 21 Directory indexDirectory=FSDirectory.open(new File("indexDir")); 22 // 2.2建立indexReader 23 IndexReader indexReader=IndexReader.open(indexDirectory); 24 // 2.3建立indexSearcher 25 IndexSearcher indexSearcher=new IndexSearcher(indexReader); 26 // 2.4建立query 27 Query query=new TermQuery(new Term("fileName","java")); 28 // 2.5根據indexSearcher.seacher(query,maxDoc);獲取topDocs 29 TopDocs topDocs = indexSearcher.search(query, 100); 30 // 2.6根據topDocs獲取ScoreDocs[] 31 ScoreDoc[] scoreDocs=topDocs.scoreDocs; 32 // 2.7遍歷ScoreDocs[]獲取docId 33 for (ScoreDoc scoreDoc : scoreDocs) { 34 int docId=scoreDoc.doc; 35 // 2.8根據docId調用indexSearcher.doc(docId)方法獲取一個document 36 Document doc = indexSearcher.doc(docId); 37 // 2.9對document進行解析,獲取須要的值 38 System.out.println("fileName-->"+document.get("fileName")+"createDate--->"+new Date(Long.parseLong(doc.get("createDate")))); 39 } 40 // 3.0關閉indexSearcher和indexReader 41 indexSearcher.close(); 42 indexReader.close();
2Lucene的組成數組
①索引架構
I索引創建的主要流程app
1 // 1指定索引的存放目錄 2 Directory directory=FSDirectory.open(new File("paht"));//硬盤 3 // //OR 4 Directory directory2=new RAMDirectory();//內存 5 // 2建立indexWriter 6 IndexWriterConfig conf=new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)); 7 IndexWriter indexWriter=new IndexWriter(directory, conf); 8 // 3建立文檔(document)(對於數據庫而言,一個條記錄就是一個文檔,對於文件而言,一個文件就是一個文檔) 9 // 1.3建立document 10 Document document=new Document(); 11 // 4爲文檔指定域(Field)(對於數據庫而言,域至關於字段,對於文件而言域至關於屬性) 12 document.add(new Field("fileName","java.txt",Store.YES,Index.ANALYZED)); 13 document.add(new NumericField("creatDate",Store.YES,true).setLongValue(new Date().getTime())); 14 document.add(new NumericField("size",Store.YES,true).setDoubleValue(10101.22)); 15 document.add(new Field("content",FileUtils.readFileToString(new File("java.txt")),Store.NO,Index.ANALYZED)); 16 // 5添加索引 17 indexWriter.addDocument(document);
II、重要類的介紹ide
directory工具
是用來指定索引的存放位置,能夠是內存也能夠是硬盤,FSDirectory.open(new File("paht")),會根據本地文件系統,自動選擇一種最合適的方式存儲索引
indexWriter 性能
是用來對索引的進行增刪改的重要操做類
Document
document對象對於數據庫而言,一個條記錄就是一個document,對於文件而言,一個文件就是一個document
Field
Field對象對於數據庫而言,Field至關於字段(例如 name、age、、、、),對於文件而言Field至關於屬性(例如文件名(name)..) 子類NumericField是用來存儲數據類型的的字段的值,例如int 、long、double、,還有日期能夠轉換爲long型後存儲
II索引的增刪改
①增長索引
indexWriter.addDocument(document);
②刪除索引
1 //刪除索引 2 indexWriter.deleteDocuments(new Term("fileName","java"));//刪除文件名等於Java的document--刪除後只是放在一個臨時文檔裏,不被檢索,並無真正刪除 3 indexWriter.forceMergeDeletes();//強制把刪除的document刪除掉
③更新索引
1 //更新索引--索引的更新原理:1根據query刪除掉對應document,而後再把新的document放進去 2 indexWriter.updateDocument(new Term("fileName","java"), document);
III索引的權重
1 //①對於默認狀況下,索引的排序是按照評分來排序的,評分公式是Score=Score*Boot , 2 //分數*權重,只要保證Boot的足夠大,那麼對應搜索的document就會排在第一位 3 //設置權重的辦法是: 4 document.setBoost(1000F);
②分詞
I分詞運行流程分析
①searchWord首先會被Tokenizer分紅一個一個的語彙單元,
②而後會通過一系列的TokenFilter(分詞過濾器),過濾掉沒意義的分詞,例如「的,啊 」這些感嘆詞
③通過一系列TokenFilter後,返回一個TokenStream,就是一個分詞字符流,流裏存有分詞個各類信息
以下圖:
II分詞的類介紹
1Analyzer:分詞器,是一個抽象類
1 //其主要包含兩個接口,用於生成TokenStream: 2 TokenStream tokenStream(String fieldName, Reader reader); 3 TokenStream reusableTokenStream(String fieldName, Reader reader) ; 4 //爲了提升性能,在同一個線程中無需再生成新的TokenStream對象,舊的能夠被重用,reusableTokenStream是獲取當前線程TokenSteam。
2Tokenizer
Tokenizer繼承與TokenStream,是用來對searchWord的reader流進行分詞,把searchWord分紅一個一個的語彙單元
3TokenFilter
TokenFilter,過濾分詞後的語彙單元,
主要方法incrementToken(),能夠依次遍歷語彙單元的信息
4TokenStream
分詞字符流,流裏存有分詞個各類信息
例如:CharTermAttribute、OffsetAttribute、PositionIncrementAttribute、TypeAttribute、、等等
III擴展分詞器
基本原理:就是使用自定義分詞器的擴展原生analyzer的構造方法,而後用analyzer對應的Tokenizer分詞,而後再使用自定義的TokenFilter過濾業務邏輯數據,
1自定義一個類繼承analyzer
1 public class MyAnalyzer extends Analyzer {2}
2實現tokenStream方法
1 public class MyAnalyzer extends Analyzer { 2 @Override 3 public TokenStream tokenStream(String arg0, Reader reader) { 4 return null; 5 } 6 }
3自定義TokenFilter
package com.lucence.analyzer; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.apache.lucene.analysis.TokenFilter; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; /** * 自定義分詞過濾器 * 1自定義類繼承TokenFilter * 2實現指定的方法-incrementToken * 3在incrementToken裏會遍歷全部被分詞的詞彙單元, * 4實現本身的業務邏輯 * */ public class MyAnalyzerFilter extends TokenFilter { private CharTermAttribute cta; private PositionIncrementAttribute pia; private State state; private Stack<String> sameWordStack; protected MyAnalyzerFilter(TokenStream input) { super(input); this.cta=input.addAttribute(CharTermAttribute.class); this.pia=input.addAttribute(PositionIncrementAttribute.class); sameWordStack=new Stack<String>(); } @Override public boolean incrementToken() throws IOException { if(sameWordStack.size()>0){ String pop = sameWordStack.pop(); //恢復狀態 restoreState(state); cta.setEmpty(); cta.append(pop); pia.setPositionIncrement(0); //System.out.print("["+cta+"]"+pia.getPositionIncrement()); System.out.println(state.hashCode()); return true; } if (!input.incrementToken()) { return false; } if(getSameWorder(cta.toString())){ //捕獲當前狀態 state=captureState(); } return true; } /** * 同義詞處理--數據 */ private Boolean getSameWorder(String key){ //1申明一個map存放同義詞---模擬數據庫 Map<String, String[]> map=new HashMap<String, String[]>(); map.put("我",new String[]{"咱","吾","俺"}); map.put("中國",new String[]{"大陸","天朝"}); String[] strings = map.get(key); if(strings!=null&&strings.length>0){ for (int i = 0; i < strings.length; i++) { sameWordStack.push(strings[i]); } return true; }else{ return false; } } }
4使用自定義的TokenFilter返回處理後的TokenStream
public class MyAnalyzer extends Analyzer { @Override public TokenStream tokenStream(String arg0, Reader reader) { return new MyAnalyzerFilter(new IKTokenizer(reader,false)); } }
③搜索
I搜索的運行流程
// 1.1指定索引存放位置 Directory indexDirectory=FSDirectory.open(new File("indexDir")); // 1.2建立indexReader---indexReader.openIfChanged(oldReader),監聽索引是否有改變,若索引有改變則從新獲取indexReader IndexReader indexReader=IndexReader.open(indexDirectory); // 1.3建立indexSearcher IndexSearcher indexSearcher=new IndexSearcher(indexReader); // 1.4建立query Query query=new TermQuery(new Term("fileName","java")); // 1.5根據indexSearcher.seacher(query,maxDoc);獲取topDocs TopDocs topDocs = indexSearcher.search(query, 100); // 1.6根據topDocs獲取ScoreDocs[] ScoreDoc[] scoreDocs=topDocs.scoreDocs; // 1.7遍歷ScoreDocs[]獲取docId for (ScoreDoc scoreDoc : scoreDocs) { int docId=scoreDoc.doc; // 1.8根據docId調用indexSearcher.doc(docId)方法獲取一個document Document doc = indexSearcher.doc(docId); // 1.9對document進行解析,獲取須要的值 System.out.println("fileName-->"+doc.get("fileName")+"createDate--->"+new Date(Long.parseLong(doc.get("createDate")))); } // 2.0關閉indexSearcher和indexReader indexSearcher.close(); indexReader.close();
IIquery類的介紹
①termQuery:精確查詢 new term(field,value) ②termRangeQuery:字符串範圍查詢new TermRangeQuery(field, lowerTerm, upperTerm, includeLower, includeUpper) ③NumericRange:數字範圍查詢,NumericRangeQuery.newTRange(field, min, max, minInclusive, maxInclusive)--T表明泛型 ④QueryParser: ⑤prefixQuery:前綴查詢--new PrefixQuery(new term(field,prefix)) ⑥wildCartQuery:通配符查詢--new wildCartQuery(new term(field,value))---value已經包含通配符,例如"*bb*",查找包含bb的數據 ⑦BooleanQuery:條件查詢,能夠鏈接多個多個條件 例如:
1 BooleanQuery booleanQuery=new BooleanQuery(); 2 booleanQuery.add(query1,occur) 3 booleanQuery.add(query2,occur) 4 booleanQuery.add(query3,occur)
occur的值說明
must:必定,必須有,至關於數據庫的and
should:可能有,,至關於數據庫的or
MUST_NOT:必定沒有,不存在,至關於數據庫的不等於
⑧FuzzyQuery:模糊查詢,
new FuzzyQuery(term, minimumSimilarity),能夠 設置minimumSimilarity來設置匹配程度,越高匹配程度越高, new FuzzyQuery(new term("name","bbcs"), minimumSimilarity)---含有bbc或者bbXs會被匹配出來
⑨phraseQuery:語義查詢,對於中文,使用做用不大,
PhraseQuery phraseQuery=new PhraseQuery(); // 1設置跳躍的範圍 phraseQuery.setSlop(2); //2設置開始的單詞 phraseQuery.add(new Term("content","i")); // 3設置結束的單詞 phraseQuery.add(new Term("content","you")); //例如包含i love you的內容將會出來
III擴展queryParse類
package com.lucence.query; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.util.Version; /** * 擴展queryParse類 * queryParse的查詢的原理:先對查詢的字符串進行分析,而後再使用對應的query去查詢, * 例若有通配符字符,就添加wildCartQuery去查詢 ,若是*這些的,就添加FuzzyQuery去查詢 * 也就說,若是咱們想擴展queryParse的查詢,那麼能夠自定義一個類,而後繼承queryParse的,而後重構對應的getxxxquery()方法,而且在裏面實現業務邏輯,則就能夠實現擴展queryParse的功能 */ //1第一步自定義類繼承Lucene的queryParse public class myQueryParse extends QueryParser { //2選擇一個重寫一個構造方法 public myQueryParse(Version matchVersion, String f, Analyzer a) { super(matchVersion, f, a); } //3重寫對應的getXXQuery方法--而且在方法裏實現業務邏輯 /** * field--搜索域 * termStr---搜索值 */ @Override protected org.apache.lucene.search.Query getWildcardQuery(String field, String termStr) throws ParseException { if(termStr.indexOf("?")!=-1){ throw new ParseException("不能使用通配符查詢"); } return super.getWildcardQuery(field, termStr); } /** * field--搜索域 * termStr---搜索值 */ @Override protected org.apache.lucene.search.Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException { return super.getFuzzyQuery(field, termStr, minSimilarity); } }
IV排序與分頁
排序:
//①排序:默認是根據score排序,score默認是=score(關聯性)*boot(權重) SortField sortField1=new SortField("fileName",SortField.STRING,true);//content--"字段名稱", ,SortField.STRING-"字段在存放時的類型",true--是否反轉 SortField sortField2=new SortField("size",SortField.INT,false);//content--"字段名稱", ,SortField.BYTE-"字段在存放時的類型",true--是否反轉 Sort sort=new Sort(sortField1,sortField2); TopDocs topDocs = searcher.search(query,10,sort);
分頁:
//1searchAfter(scoreDocAfter, query, pagezie)方法是每次返回scoreDocAfter後面的document, int docId=(pagezie-1)*pageNumber-1;//每次查詢是記錄開始行 ScoreDoc scoreDocAfter=new ScoreDoc(docId,0f); TopDocs topDocs = searcher.searchAfter(scoreDocAfter, query, pagezie);
V搜索過濾器
package com.lucence.searchFilter; import java.io.IOException; import org.apache.lucene.document.Document; import org.apache.lucene.index.AbstractAllTermDocs; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.Filter; import org.apache.lucene.util.OpenBitSet; /** * 自定義搜索過濾器 * 1新建一個類繼承Lucene的Filter * 2實現getDocIdSet方法 * 3根據indexReader獲取到返回的document * 4根據本身的業務邏輯處理後返回DocIdSet * @author Jeremy * */ public class MySearchFilter extends Filter { @SuppressWarnings("unused") @Override public DocIdSet getDocIdSet(IndexReader indexReader) throws IOException { // TODO Auto-generated method stub int maxDoc = indexReader.maxDoc();//獲取返回document的數目 OpenBitSet docIdSet=new OpenBitSet(maxDoc);//默認是64位大小,可是若是超出沒報異常,因此通常在indexReader裏獲取返回document的大小 //docIdSet是一個至關於一個列表--以下面 //status document // 0 docId // 1 docId //若status是0 ,則document將不會被顯示出來, //也就說,咱們在filter把不須要的document能夠過濾掉 //1填滿列表---默認是空 docIdSet.set(0, maxDoc); //2獲取返回的document //2.1直接使用indexReader來獲取符合過濾條件的document //TermDocs---存儲了兩個變量,一個是返回的docId數組,一個每一個document出現"javass.txt"的頻率次數 TermDocs termDocs =indexReader.termDocs(new Term("content","jeremy")); while (termDocs.next()) { System.out.println(termDocs.doc()); Document document = indexReader.document(termDocs.doc()); System.out.println("fileName"+document.get("fileName")+"---出現頻率:"+termDocs.freq()+"---被過濾掉了"); docIdSet.clear(termDocs.doc());//clear()方法至關於把status設置爲0 } return docIdSet; } }
VI自定義評分
實現步驟:
* 自定義評分
* 默認的評分機制是 score=score*Root = 分數*索引的權重
* 自定義評分的實現流程
* 1新建一個類及承諾CustomScoreQuery
* 2覆蓋CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery)的構造方法--ValueSourceQuery:評分域查詢對象
* 3覆蓋 getCustomScoreProvider(IndexReader reader)方法
* 4新建一個類繼承CustomScoreProvider
* 5覆蓋CustomScoreProvider的customScore(int doc, float subQueryScore, float valSrcScore)方法,
* --------doc:docId,subQueryScore:原有評分,valSrcScore:咱們自定義傳入的評分
* 6在customScore(int doc, float subQueryScore, float valSrcScore)方法裏返回通過業務邏輯處理的的自定義評分
* 7在getCustomScoreProvider返回自定的義的MyCustomScoreProvider對象
* 8在查詢中使用MyCustomerScroeQuery
代碼示例:
package com.lucence.scoreQuery;
import java.io.IOException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Query; import org.apache.lucene.search.function.CustomScoreProvider; import org.apache.lucene.search.function.CustomScoreQuery; import org.apache.lucene.search.function.ValueSourceQuery; /** * 自定義評分 * 默認的評分機制是 score=score*Root = 分數*索引的權重 * 自定義評分的實現流程 * 1新建一個類及承諾CustomScoreQuery * 2覆蓋CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery)的構造方法--ValueSourceQuery:評分域查詢對象 * 3覆蓋 getCustomScoreProvider(IndexReader reader)方法 * 4新建一個類繼承CustomScoreProvider * 5覆蓋CustomScoreProvider的customScore(int doc, float subQueryScore, float valSrcScore)方法, * --------doc:docId,subQueryScore:原有評分,valSrcScore:咱們自定義傳入的評分 * 6在customScore(int doc, float subQueryScore, float valSrcScore)方法裏返回通過業務邏輯處理的的自定義評分 * 7在getCustomScoreProvider返回自定的義的MyCustomScoreProvider對象 * 8在查詢中使用MyCustomerScroeQuery * @author Jeremy * */ // 1新建一個類及承諾CustomScoreQuery public class MyCustomerScroeQuery extends CustomScoreQuery { //2覆蓋CustomScoreQuery(Query subQuery, ValueSourceQuery valSrcQuery)的構造方法--ValueSourceQuery:評分域查詢對象 public MyCustomerScroeQuery(Query subQuery, ValueSourceQuery valSrcQuery) { super(subQuery, valSrcQuery); } public MyCustomerScroeQuery(Query subQuery) { super(subQuery); } @Override //3覆蓋 getCustomScoreProvider(IndexReader reader)方法 protected CustomScoreProvider getCustomScoreProvider(IndexReader reader)throws IOException { //7在getCustomScoreProvider返回自定的義的MyCustomScoreProvider對象 return new MyCustomScoreProvider(reader); } }
package com.lucence.scoreQuery; import java.io.IOException; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.function.CustomScoreProvider; // 4新建一個類繼承CustomScoreProvide public class MyCustomScoreProvider extends CustomScoreProvider{ public MyCustomScoreProvider(IndexReader reader) { super(reader); } @Override /** * 5覆蓋CustomScoreProvider的customScore(int doc, float subQueryScore, float valSrcScore)方法, * --------doc:docId,subQueryScore:原有評分,valSrcScore:咱們自定義傳入的評分 */ //subQueryScore * valSrcScore;---默認的評分是原有的評分乘以評分域的的值 public float customScore(int doc, float subQueryScore, float valSrcScore) throws IOException { //6在customScore方法裏返回業務邏輯處理後的自定義評分 System.out.println(subQueryScore+"------"+valSrcScore); Document document = reader.document(doc); if(document.get("fileName").endsWith(".txt")){//文件類型爲。txt的優先排序 return subQueryScore*valSrcScore*100; } return super.customScore(doc, subQueryScore, valSrcScore); } }
使用代碼示例:
public void test01(){ //3.1指定搜索目錄 try { Directory directory=FSDirectory.open(new File("C:/lucence/index")); //3.2建立索引讀取器(IndexReader) IndexReader indexReader=IndexReader.open(directory); //3.3根據IndexReader建立索引搜索器(indexSeacher) IndexSearcher searcher=new IndexSearcher(indexReader); //3.4建立查詢器query----使用QueryParser的parser()方法建立--建立query Query query=null; QueryParser parser=new QueryParser(Version.LUCENE_35,"content",new StandardAnalyzer(Version.LUCENE_35)); query=parser.parse("spring"); // //3.4.2c建立評分域---可使用評分域去評分,也能夠不使用----評分域的得類型必須是數據類型--- // FieldScoreQuery fieldScoreQuery=new FieldScoreQuery("fileName",Type.BYTE); // //3.4.3使用MyCustomerScroeQuery來構建query // MyCustomerScroeQuery myCustomerScroeQuery = new MyCustomerScroeQuery(query, fieldScoreQuery); MyCustomerScroeQuery myCustomerScroeQuery=new MyCustomerScroeQuery(query); //3.5使用自定義的myCustomerScroeQuery進行查詢過,IndexSeacher執行查詢,並獲取返回TopDocs---文檔集合 TopDocs topDocs = searcher.search(myCustomerScroeQuery,100); //3.6根據TopDocs(文檔集合)獲取scoreDocs---分數文檔 ScoreDoc[] scoreDocs=topDocs.scoreDocs; for (ScoreDoc scoreDoc : scoreDocs) { //3.7根據ScoreDocs的doc Id在indexSeacher(索引搜索器)中獲取文檔對象, Document doc = searcher.doc(scoreDoc.doc); //3.8解析文檔對象,獲取對應的值 System.out.println(doc.get("fileName")+"["+doc.get("dir")+"]"+doc.getBoost()); } //3.9關閉索引讀取器 indexReader.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } }