lucene學習教程

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 、longdouble、,還有日期能夠轉換爲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();
        }
    }
相關文章
相關標籤/搜索