5八、lucene

數據分類:html

一、結構化數據:格式固定,長度固定,數據類型固定,例如:數據庫中的數據,java

二、非結構化數據:和上面相反,例如:word文檔,pdf文檔,郵件,htmlspring

 

數據的查詢:數據庫

一、結構化數據的查詢:apache

SQL語句,特色:簡單,速度快windows

二、非結構化數據的查詢:數據結構

把非結構化數據變成結構化數據:先根據空格進行字符串的拆分,獲得一個單詞列表,基於單詞列表建立一個索引(一個爲了提升查詢速度,建立某種數據結構的集合),查詢索引,根據單詞和文檔的對應關係找到文檔列表。工具

 

應用場景:搜索引擎

一、搜索引擎編碼

二、站內搜索

三、電商搜索

四、只要有搜索的地方

 

lucene:一個基於java開發全文檢索的工具包

實現全文檢索的流程:

一、建立索引

1·一、得到文檔

原始文檔:要基於那些數據來進行搜索,那麼這些數據就是原始文檔

搜索引擎得到文檔:使用爬蟲得到原始文檔

站內搜索得到文檔:數據庫中的數據

磁盤得到文檔:直接使用io流讀取磁盤上的文檔

1·二、構建文檔對象

對應每一個原始文檔建立一個Document對象,每一個Document對象中包含多個域,域中保存就是原始文檔數據,包含域的名稱和值。

注意:每一個文檔都有一個惟一的編號,就是文檔ID

1·三、分析文檔

分詞的過程:

    一、根據空格進行字符串拆分,獲得一個單詞列表

    二、把單詞統一轉換成小寫

    三、去除標點符號

    四、去除停用詞(無心義的詞)

每一個關鍵詞都封裝成一個term對象中,包含關鍵詞所在域和關鍵詞自己。

注意:不一樣的域中拆分出來的相同的關鍵詞是不一樣的term

1·四、建立索引

基於關鍵詞列表建立一個索引,保存到索引庫中。索引庫中包含:索引,document對象,關鍵詞和文檔的對應關係。

補充:經過詞語找文檔,這種索引的結構叫倒排索引結構。

 

二、查詢索引

2·一、用戶查詢接口

用戶輸入查詢條件的地方,例如:百度的搜索框

2·二、把關鍵詞封裝成一個查詢對象,包含:要查詢的域和要搜索的關鍵詞

2·三、執行查詢,根據要查詢的關鍵詞到對應的域上進行搜索

2·四、渲染結果,根據文檔的id找到文檔對象,對關鍵詞進行處理,最終展現出來

 

入門代碼:

一、添加MAVEN依賴

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- lucene核心庫 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-core</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- Lucene的查詢解析器 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-queryparser</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- lucene的默認分詞器庫 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-analyzers-common</artifactId>
        <version>${lunece.version}</version>
    </dependency>
    <!-- lucene的高亮顯示 -->
    <dependency>
        <groupId>org.apache.lucene</groupId>
        <artifactId>lucene-highlighter</artifactId>
        <version>${lunece.version}</version>
    </dependency>
</dependencies>


 二、代碼建立索引

public void createIndex() throws Exception{

        File file = new File("./index");
        if (!file.exists()){
            file.mkdirs();
        }
/*
        Directory directory = new RAMDirectory();//把索引庫保存到內存中,不推薦
*/
        //建立一個Director對象,指定索引庫保存的位置
        Directory directory = FSDirectory.open(file.toPath());

        //基於Direcotry對象建立一個indexWriter對象
        IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
        //讀取磁盤上的文件,對應每一個文件建立一個文檔對象
        File orgfile = new File("");
        File[] files = orgfile.listFiles();
        for (File f : files){
            //取文件名
            String filename = f.getName();
            //文件的路徑
            String filepath = f.getPath();
            //文件的內容
            String fileContent = FileUtils.readFileToString(f, "utf-8");
            //文件的大小
            long filesize = FileUtils.sizeOfDirectory(f);
            //建立field
            //參數1:域的名稱,參數2:域的內容,參數3:是否存儲
            Field fieldName = new TextField("name",filename,Field.Store.YES);
            Field fieldPath = new TextField("path",filepath,Field.Store.YES);
            Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
            Field fieldsize = new TextField("size",filesize+"",Field.Store.YES);

            //建立文檔對象
            Document document = new Document();
            //向文檔對象中添加域
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldsize);

            //把文檔對象寫入索引庫
            indexWriter.addDocument(document);

        }

        //關閉indexwriter對象
        indexWriter.close();

    }

三、索引庫搜索

public void searchIndex()throws Exception{
    //建立一個Director對象,指定索引庫的位置
    Directory directory=FSDirectory.open(new File("./index").toPath());
    //建立一個indexReader對象
    IndexReader indexReader = DirectoryReader.open(directory);
    //建立一個IndexSearch對象,構造方法中的參數indexReader對象
    IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    //建立一個Query對象,TermQuery
    Query query = new TermQuery(new Term("content","spring"));
    //執行查詢,獲得一個TopDocs對象
    //參數1:查詢對象,參數2:查詢結果返回的最大記錄數
    TopDocs topDocs = indexSearcher.search(query, 10);
    //取查詢結果的總記錄數
    System.out.println(topDocs.totalHits);
    //取文檔列表
    ScoreDoc[] scoreDocs = topDocs.scoreDocs;
    for (ScoreDoc doc : scoreDocs){
        int docId = doc.doc;
        Document document = indexSearcher.doc(docId);
        System.out.println(document.get("name"));
        System.out.println(document.get("path"));
        System.out.println(document.get("size"));
        System.out.println(document.get("content"));
    }

    indexReader.close();

}

步驟:

建立索引庫

一、建立一個Director對象,指定索引庫保存的位置

二、基於Director對象,建立一個IndexWriter對象

三、讀取磁盤文件,對應每一個文件建立一個文檔對象

四、向文檔對象中添加域

五、把文檔對象寫入索引庫

六、關閉indexwriter對象

public void createIndex() throws Exception{

        File file = new File("./index");
        if (!file.exists()){
            file.mkdirs();
        }
/*
        Directory directory = new RAMDirectory();//把索引庫保存到內存中,不推薦
*/
        //建立一個Director對象,指定索引庫保存的位置
        Directory directory = FSDirectory.open(file.toPath());

        //基於Direcotry對象建立一個indexWriter對象
        IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
        //讀取磁盤上的文件,對應每一個文件建立一個文檔對象
        File orgfile = new File();
        File[] files = orgfile.listFiles();
        for (File f : files){
            //取文件名
            String filename = f.getName();
            //文件的路徑
            String filepath = f.getPath();
            //文件的內容
            String fileContent = FileUtils.readFileToString(f, "utf-8");
            //文件的大小
            long filesize = FileUtils.sizeOfDirectory(f);
            //建立field
            //參數1:域的名稱,參數2:域的內容,參數3:是否存儲
            Field fieldName = new TextField("name",filename,Field.Store.YES);
            Field fieldPath = new TextField("path",filepath,Field.Store.YES);
            Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
            Field fieldsize = new TextField("size",filesize+"",Field.Store.YES);

            //建立文檔對象
            Document document = new Document();
            //向文檔對象中添加域
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldsize);

            //把文檔對象寫入索引庫
            indexWriter.addDocument(document);

        }

        //關閉indexwriter對象
        indexWriter.close();

    }

 查看索引庫:使用luke工具

查詢索引庫

一、建立一個Director對象,指定索引庫的位置

二、建立一個IndexReader對象

三、建立一個IndextSearcher對象,構造方法中的參數IndexReader對象

四、建立一個Query對象,termQuery

五、執行查詢,獲得一個TopDocs對象

六、取查詢結果的總記錄數

七、取文檔列表

八、打印文檔中的內容

九、關閉IndexReader對象

public void searchIndex()throws Exception{
        //建立一個Director對象,指定索引庫的位置
        Directory directory=FSDirectory.open(new File("./index").toPath());
        //建立一個indexReader對象
        IndexReader indexReader = DirectoryReader.open(directory);
        //建立一個IndexSearch對象,構造方法中的參數indexReader對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //建立一個Query對象,TermQuery
        Query query = new TermQuery(new Term("content","spring"));
        //執行查詢,獲得一個TopDocs對象
        //參數1:查詢對象,參數2:查詢結果返回的最大記錄數
        TopDocs topDocs = indexSearcher.search(query, 10);
        //取查詢結果的總記錄數
        System.out.println(topDocs.totalHits);
        //取文檔列表
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc doc : scoreDocs){
            int docId = doc.doc;
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
        } 
        
        indexReader.close();
        
        
    }
}

 

分析器:

默認使用的是標準分析器StandardAnalyzer

查看分析器的分析效果:

一、建立一個Analyzer對象,StandardAnayzer對象

二、使用分析器對象的tokenStream方法得到宇哥tokenStream對象

三、向tokenStream對象設置一個引用,至關因而一個指針

四、調用tokenStream對象的rest方法,若是不調用拋異常

五、使用while循環遍歷tokenStream對象

六、關閉tokenStream對象

public void testTokenStream() throws Exception{
    Analyzer analyzer = new StandardAnalyzer();
    TokenStream tokenStream = analyzer.tokenStream("","wo shi yi ge zifuchuang");
    CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
    tokenStream.reset();
    while (tokenStream.incrementToken()){
        System.out.println(charTermAttribute.toString());
    }
    tokenStream.close();
}

 

中文分析器:IKAnalyzer

一、添加IKAnalyzer的jar包

二、把配置文件和擴展詞典添加到工程的classpath下

注意:擴展詞典嚴禁使用windows記事本編輯保證擴展詞典的編碼格式是utf-8

擴展詞典做用:添加一些新詞

停用詞詞典:無心義的詞或者是敏感詞彙

 

索引庫維護

常見的field:

Field類

數據類型

Analyzed

是否分詞

Indexed

是否索引

Stored

是否存儲

說明

StringField(FieldName, FieldValue,Store.YES))

字符串

N

Y

Y或N

這個Field用來構建一個字符串Field,可是不會進行分詞,會將整個串存儲在索引中,好比(訂單號,身份證號等)

是否存儲在文檔中用Store.YES或Store.NO決定

LongField(FieldName, FieldValue,Store.YES)

Long型 

Y

Y

Y或N

這個Field用來構建一個Long數字型Field,進行分詞和索引,好比(價格)

是否存儲在文檔中用Store.YES或Store.NO決定

StoredField(FieldName, FieldValue) 

重載方法,支持多種類型

N

N

Y

這個Field用來構建不一樣類型Field

不分析,不索引,但要Field存儲在文檔中

TextField(FieldName, FieldValue, Store.NO)

TextField(FieldName, reader)

 

字符串

Y

Y

Y或N

若是是一個Reader, lucene猜想內容比較多,會採用Unstored的策略.

添加文檔:

public class IndexManager {
    public void addDoucment() throws  Exception{
        //建立一個IndexWriter對象,須要IKAnalyzer做爲分析器
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("./index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
        //建立一個document對象
        Document document = new Document();
        document.add(new TextField("name","新添加的問",Field.Store.YES));
        //把文檔寫入索引庫
        indexWriter.addDocument(document);
        indexWriter.close();
    }
}

 

刪除索引庫:

public void deleteAllDocument() throws Exception{
    //建立一個IndexWriter對象,須要IKAnalyzer做爲分析器
    IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("./index").toPath()),
            new IndexWriterConfig(new IKAnalyzer()));
    indexWriter.deleteAll();
    indexWriter.close();
}
public void deleteDocumentByTerm()throws Exception{
        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("./index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
        indexWriter.deleteDocuments(new Term("name","內容"));
        indexWriter.close();

}

 

更新索引庫:

public void updateDocument()throws Exception{

        IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("./index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
        Document document = new Document();
        document.add(new TextField("name","更新的內容",Field.Store.YES));
        
        indexWriter.updateDocument(new Term("name","內容"),document);
        indexWriter.close();
   
}

 

索引庫的查詢:

使用Query的子類查詢

public class SearchIndex {
    public void testRangeQuery()throws Exception{
        IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File("./index").toPath()));
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        
        //建立一個query對象
        Query query = LongPoint.newRangeQuery("size", 0l, 10000l);
        TopDocs topDocs = indexSearcher.search(query, 10);
        System.out.println(topDocs.totalHits);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs){
           int docId =  scoreDoc.doc;
            Document document = indexSearcher.doc(docId);
            System.out.println(document.get("name"));
        }
        indexReader.close();
    }
}

 使用QueryPaser進行查詢:能夠對要查詢的內容先分詞,而後基於分詞的結果再查詢

須要添加jar包lucene-queryoarser

public void testQueryParser()throws Exception{
    //建立一個QueryParser對象
    //參數1:默認搜索域,參數2:分析器對象
    QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
    //使用QueryPaser對象建立一個QUery
    Query query = queryParser.parse("這是一個查詢的語句")
    //執行查詢
    printResult(query);
}
相關文章
相關標籤/搜索