Lucene簡介

1.Lucene簡介

Lucene是apache下的一個(gè)開源的全文檢索引擎工具包。

1.1.全文檢索(Full-text Search)

1.1.1.定義

全文檢索就是先分詞創(chuàng)建索引踩验,再執(zhí)行搜索的過程宫莱。

分詞:就是將一段文字分成一個(gè)個(gè)單詞

全文檢索就將一段文字分成一個(gè)個(gè)單詞去查詢數(shù)據(jù)!!纽什!

1.1.2.應(yīng)用場(chǎng)景

1.1.2.1.搜索引擎(了解)

搜索引擎是一個(gè)基于全文檢索纪岁、能獨(dú)立運(yùn)行、提供搜索服務(wù)的軟件系統(tǒng)庇谆。

image.png

1.1.2.2.電商站內(nèi)搜索(重點(diǎn))

思考:電商網(wǎng)站內(nèi),我們都是通過輸入關(guān)鍵詞來搜索商品的署辉。如果我們根據(jù)關(guān)鍵詞族铆,直接查詢數(shù)據(jù)庫,會(huì)有什么后果哭尝?

答:我們只能使用模糊搜索哥攘,來進(jìn)行匹配,會(huì)導(dǎo)致很多數(shù)據(jù)匹配不到材鹦。所以逝淹,我們必須使用全文檢索。

image.png

1.2.Lucene實(shí)現(xiàn)全文檢索的流程

[圖片上傳失敗...(image-a40a64-1563276766589)]

image.png

全文檢索的流程分為兩大部分:索引流程桶唐、搜索流程栅葡。

索引流程:采集數(shù)據(jù)--->構(gòu)建文檔對(duì)象--->創(chuàng)建索引(將文檔寫入索引庫)。

搜索流程:創(chuàng)建查詢--->執(zhí)行搜索--->渲染搜索結(jié)果尤泽。

2.入門示例

2.1.需求

使用Lucene實(shí)現(xiàn)電商項(xiàng)目中圖書類商品的索引和搜索功能欣簇。

2.2.配置步驟說明

(1)搭建環(huán)境(先下載Lucene)

(2)創(chuàng)建索引庫

(3)搜索索引庫

2.3.配置步驟

2.3.1.第一部分:搭建環(huán)境(創(chuàng)建項(xiàng)目,導(dǎo)入包)

前提:已經(jīng)創(chuàng)建好了數(shù)據(jù)庫(直接導(dǎo)入book.sql文件)

image.png

2.3.1.1.第一步:下載Lucene

Lucene是開發(fā)全文檢索功能的工具包坯约,使用時(shí)從官方網(wǎng)站下載熊咽,并解壓。

官方網(wǎng)站:http://lucene.apache.org/

下載地址:http://archive.apache.org/dist/lucene/java/

下載版本:4.10.3(要求:jdk1.7及以上)

核心包lucene-core-4.10.3.jar(附常用API)

image.png

2.3.1.2.第二步:創(chuàng)建項(xiàng)目闹丐,導(dǎo)入包

mysql5.1驅(qū)動(dòng)包:mysql-connector-java-5.1.7-bin.jar

核心包:lucene-core-4.10.3.jar

分析器通用包:lucene-analyzers-common-4.10.3.jar

查詢解析器包:lucene-queryparser-4.10.3.jar

項(xiàng)目結(jié)構(gòu)如下:

image.png

2.3.2.第二部分:創(chuàng)建索引

步驟說明:

(1)采集數(shù)據(jù)

(2)將數(shù)據(jù)轉(zhuǎn)換成Lucene文檔

(3)將文檔寫入索引庫横殴,創(chuàng)建索引

2.3.2.1.第一步:采集數(shù)據(jù)

Lucene全文檢索,不是直接查詢數(shù)據(jù)庫卿拴,所以需要先將數(shù)據(jù)采集出來衫仑。

(1)創(chuàng)建Book類

public class Book {

    private Integer bookId;  // 圖書ID

    private String name;   // 圖書名稱

    private Float price;    // 圖書價(jià)格

    private String pic;    // 圖書圖片

    private String description; // 圖書描述

    // 補(bǔ)全get\set方法

}

(2)創(chuàng)建一個(gè)BookDao類

package cn.gzsxt.lucene.dao;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.util.ArrayList;

import java.util.List;

import cn.gzsxt.lucene.pojo.Book;

public class BookDao {

    public List<Book> getAll() {

        // 數(shù)據(jù)庫鏈接

        Connection connection = null;

        // 預(yù)編譯statement

        PreparedStatement preparedStatement = null;

        // 結(jié)果集

        ResultSet resultSet = null;

        // 圖書列表

        List<Book> list = new ArrayList<Book>();

        try {

            // 加載數(shù)據(jù)庫驅(qū)動(dòng)

            Class.forName("com.mysql.jdbc.Driver");

            // 連接數(shù)據(jù)庫

            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lucene", "root", "gzsxt");

            // SQL語句

            String sql = "SELECT * FROM book";

            // 創(chuàng)建preparedStatement

            preparedStatement = connection.prepareStatement(sql);

            // 獲取結(jié)果集

            resultSet = preparedStatement.executeQuery();

            // 結(jié)果集解析

            while (resultSet.next()) {

                Book book = new Book();

                book.setBookId(resultSet.getInt("id"));

                book.setName(resultSet.getString("name"));

                book.setPrice(resultSet.getFloat("price"));

                book.setPic(resultSet.getString("pic"));

                book.setDescription(resultSet.getString("description"));

                list.add(book);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }finally {

            if(null!=resultSet){

                try {

                    resultSet.close();

                } catch (SQLException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

            }

            if(null!=preparedStatement){

                try {

                    preparedStatement.close();

                } catch (SQLException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

            }

            if(null!=connection){

                try {

                    connection.close();

                } catch (SQLException e) {

                    // TODO Auto-generated catch block

                    e.printStackTrace();

                }

            }

        }

        return list;

    }

}

(3)創(chuàng)建一個(gè)測(cè)試類BookDaoTest

package cn.gzsxt.lucene.test;     

import java.util.List;

import org.junit.Test;

import cn.gzsxt.lucene.dao.BookDao;

import cn.gzsxt.lucene.pojo.Book;

public class BookDaoTest {

    @Test

    public void getAll(){

       BookDao dao = new BookDao();

       List<Book> books = dao.getAll();

       for (Book book : books) {

           System.out.println("圖書id:"+book.getBookId()+",圖書名稱:"+book.getName());

       }

    }

}

(4)測(cè)試結(jié)果,采集數(shù)據(jù)成功

2.3.2.2.第二步:將數(shù)據(jù)轉(zhuǎn)換成Lucene文檔

Lucene是使用文檔類型來封裝數(shù)據(jù)的堕花,所有需要先將采集的數(shù)據(jù)轉(zhuǎn)換成文檔類型文狱。其格式為:

修改BookDao,新增一個(gè)方法缘挽,轉(zhuǎn)換數(shù)據(jù)

public List<Document> getDocuments(List<Book> books){     

       // Document對(duì)象集合

       List<Document> docList = new ArrayList<Document>();

       // Document對(duì)象

       Document doc = null;

       for (Book book : books) {

           // 創(chuàng)建Document對(duì)象如贷,同時(shí)要?jiǎng)?chuàng)建field對(duì)象

           doc = new Document();

           // 根據(jù)需求創(chuàng)建不同的Field

           Field id = new TextField("id",      book.getBookId().toString(),    Store.YES);

           Field name = new TextField("name", book.getName(),      Store.YES);

           Field price = new TextField("price",      book.getPrice().toString(),Store.YES);

           Field pic = new TextField("pic", book.getPic(),      Store.YES);

           Field desc = new TextField("description", book.getDescription(), Store.YES);

           // 把域(Field)添加到文檔(Document)中

           doc.add(id);

           doc.add(name);

           doc.add(price);

           doc.add(pic);

           doc.add(desc);

           docList.add(doc);

    }

    return docList;

}

2.3.2.3.第三步:創(chuàng)建索引庫

說明:Lucene是在將文檔寫入索引庫的過程中陷虎,自動(dòng)完成分詞到踏、創(chuàng)建索引的杠袱。因此創(chuàng)建索引庫,從形式上看窝稿,就是將文檔寫入索引庫楣富!

修改測(cè)試類,新增createIndex方法

@Test

    public void createIndex(){

       try {

           BookDao dao = new BookDao();

           // 分析文檔伴榔,對(duì)文檔中的field域進(jìn)行分詞

           Analyzer analyzer = new StandardAnalyzer();

           // 創(chuàng)建索引

           // 1) 創(chuàng)建索引庫目錄

           Directory directory = FSDirectory.open(new      File("F:\\lucene\\0719"));

           // 2) 創(chuàng)建IndexWriterConfig對(duì)象

           IndexWriterConfig cfg = new      IndexWriterConfig(Version.LATEST, analyzer);

           // 3) 創(chuàng)建IndexWriter對(duì)象

           IndexWriter writer = new IndexWriter(directory, cfg);

           // 4) 通過IndexWriter對(duì)象添加文檔對(duì)象(document)

           writer.addDocuments(dao.getDocuments(dao.getAll()));

           // 5) 關(guān)閉IndexWriter

           writer.close();

           System.out.println("創(chuàng)建索引庫成功");

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

測(cè)試結(jié)果纹蝴,創(chuàng)建成功!W偕佟塘安!

image.png

2.3.3.第三部分:搜索索引

2.3.3.1.說明

搜索的時(shí)候,需要指定搜索哪一個(gè)域(也就是字段)援奢,并且兼犯,還要對(duì)搜索的關(guān)鍵詞做分詞處理。

2.3.3.2.執(zhí)行搜索

修改測(cè)試類集漾,新增searchDocumentByIndex方法

@Test

    public void searchDocumentByIndex(){

       try {

           // 1切黔、 創(chuàng)建查詢(Query對(duì)象)

           // 創(chuàng)建分析器

           Analyzer analyzer = new StandardAnalyzer();

           QueryParser queryParser = new QueryParser("name",      analyzer);

           Query query = queryParser.parse("name:java教程");

           // 2、 執(zhí)行搜索

           // a) 指定索引庫目錄

           Directory directory = FSDirectory.open(new      File("F:\\lucene\\0719"));

           // b) 創(chuàng)建IndexReader對(duì)象

           IndexReader reader = DirectoryReader.open(directory);

           // c) 創(chuàng)建IndexSearcher對(duì)象

           IndexSearcher searcher = new IndexSearcher(reader);

           // d) 通過IndexSearcher對(duì)象執(zhí)行查詢索引庫具篇,返回TopDocs對(duì)象

           // 第一個(gè)參數(shù):查詢對(duì)象

           // 第二個(gè)參數(shù):最大的n條記錄

           TopDocs topDocs = searcher.search(query, 10);

           // e) 提取TopDocs對(duì)象中前n條記錄

           ScoreDoc[] scoreDocs = topDocs.scoreDocs;

           System.out.println("查詢出文檔個(gè)數(shù)為:" +      topDocs.totalHits);

           for (ScoreDoc scoreDoc : scoreDocs) {

              // 文檔對(duì)象ID

              int docId = scoreDoc.doc;

              Document doc = searcher.doc(docId);

              // f) 輸出文檔內(nèi)容

              System.out.println("===============================");

              System.out.println("文檔id:" + docId);

              System.out.println("圖書id:" + doc.get("id"));

              System.out.println("圖書name:" + doc.get("name"));

              System.out.println("圖書price:" + doc.get("price"));

              System.out.println("圖書pic:" + doc.get("pic"));

              System.out.println("圖書description:" +      doc.get("description"));

           }

           // g) 關(guān)閉IndexReader

           reader.close();

       } catch (Exception e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

    }

測(cè)試結(jié)果纬霞,非常成功!G浴诗芜!

image.png

2.4.小結(jié)

Lucene全文檢索,確實(shí)可以實(shí)現(xiàn)對(duì)關(guān)鍵詞做分詞埃疫、再執(zhí)行搜索功能伏恐。并且結(jié)果更精確。

3.分詞

3.1.重要性

分詞是全文檢索的核心熔恢。

所謂的分詞脐湾,就是將一段文本,根據(jù)一定的規(guī)則叙淌,拆分成一個(gè)一個(gè)詞秤掌。

Lucene是根據(jù)分析器實(shí)現(xiàn)分詞的。針對(duì)不同的語言提供了不同的分析器鹰霍。并且提供了一個(gè)通用的標(biāo)準(zhǔn)分析器StandardAnalyzer

3.2.分詞過程

--說明:我們通過分析StandardAnalyzer核心源碼來分析分詞過程

@Override

  protected TokenStreamComponents createComponents(final String      fieldName, final Reader reader) {

    final StandardTokenizer src = new StandardTokenizer(getVersion(),      reader);

    src.setMaxTokenLength(maxTokenLength);

    TokenStream tok = new StandardFilter(getVersion(), src);

    tok = new LowerCaseFilter(getVersion(), tok);

    tok = new StopFilter(getVersion(), tok, stopwords);

    return new TokenStreamComponents(src, tok) {

      @Override

      protected void setReader(final Reader reader) throws      IOException {

        src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength);

        super.setReader(reader);

      }

    };

  }

對(duì)應(yīng)Lucene分詞的過程闻鉴,我們可以做如下總結(jié):

(1)分詞的時(shí)候,是以域?yàn)閱挝坏拿鳌2煌挠蛎系海嗷オ?dú)立。

   同一個(gè)域中,拆分出來相同的詞渠羞,視為同一個(gè)詞(Term)

   不同的域中斤贰,拆分出來相同的詞,不是同一個(gè)詞次询。

   其中荧恍,Term是Lucene最小的語匯單元,不可再細(xì)分屯吊。

(2)分詞的時(shí)候經(jīng)歷了一系列的過濾器送巡。如大小寫轉(zhuǎn)換、去除停用詞等盒卸。

3.3.分詞后索引庫結(jié)構(gòu)

我們這里借助前面的示例來說明

image.png

從上圖中骗爆,我們發(fā)現(xiàn):

(1)索引庫中有兩個(gè)區(qū)域:索引區(qū)、文檔區(qū)蔽介。

(2)文檔區(qū)存放的是文檔摘投。Lucene給每一個(gè)文檔自動(dòng)加上一個(gè)文檔編號(hào)docID。

(3)索引區(qū)存放的是索引屉佳。注意:

索引是以域?yàn)閱挝坏墓瘸煌挠颍舜讼嗷オ?dú)立武花。

索引是根據(jù)分詞規(guī)則創(chuàng)建出來的圆凰,根據(jù)索引就能找到對(duì)應(yīng)的文檔。

3.4.Luke客戶端連接索引庫

Luke作為Lucene工具包中的一個(gè)工具(http://www.getopt.org/luke/)体箕,可以通過可視化界面专钉,連接操作索引庫。

3.4.1.啟動(dòng)方法

(1)雙擊start.bat啟動(dòng)累铅!

image.png

(2)連接索引庫

image.png

3.4.2.驗(yàn)證分詞效果

image.png

4.Field域

問題:我們已經(jīng)知道跃须,Lucene是在寫入文檔時(shí),完成分詞娃兽、索引的菇民。那Lucene是怎么知道的呢?

答:Lucene是根據(jù)文檔中的域的屬性投储,來確定是否要分詞第练、創(chuàng)建索引的。所以玛荞,我們必須搞清楚域有哪些屬性娇掏。

4.1.域的屬性

4.1.1.三大屬性

4.1.1.1.是否分詞(tokenized)

只有設(shè)置了分詞屬性為true,lucene才會(huì)對(duì)這個(gè)域進(jìn)行分詞處理勋眯。

在實(shí)際的開發(fā)中婴梧,有一些字段是不需要分詞的下梢,比如商品id,商品圖片等塞蹭。

而有一些字段是必須分詞的孽江,比如商品名稱,描述信息等浮还。

4.1.1.2.是否索引(indexed)

只有設(shè)置了索引屬性為true竟坛,lucene才為這個(gè)域的Term詞創(chuàng)建索引。

在實(shí)際的開發(fā)中钧舌,有一些字段是不需要?jiǎng)?chuàng)建索引的,比如商品的圖片等涎跨。我們只需要對(duì)參與搜索的字段做索引處理洼冻。

4.1.1.3.是否存儲(chǔ)(stored)

只有設(shè)置了存儲(chǔ)屬性為true,在查找的時(shí)候隅很,才能從文檔中獲取這個(gè)域的值撞牢。

在實(shí)際開發(fā)中,有一些字段是不需要存儲(chǔ)的叔营。比如:商品的描述信息屋彪。

因?yàn)樯唐访枋鲂畔ⅲǔ6际谴笪谋緮?shù)據(jù)绒尊,讀的時(shí)候會(huì)造成巨大的IO開銷畜挥。而描述信息是不需要經(jīng)常查詢的字段,這樣的話就白白浪費(fèi)了cpu的資源了婴谱。

因此蟹但,像這種不需要經(jīng)常查詢,又是大文本的字段谭羔,通常不會(huì)存儲(chǔ)到索引庫华糖。

4.1.2.特點(diǎn)

(1)三大屬性彼此獨(dú)立。

(2)通常分詞是為了創(chuàng)建索引瘟裸。

(3)不存儲(chǔ)這個(gè)域文本內(nèi)容客叉,也可以對(duì)這個(gè)域先分詞、創(chuàng)建索引话告。

4.2.Field常用類型

域的常用類型有很多兼搏,每一個(gè)類都有自己默認(rèn)的三大屬性。如下:


image.png

4.3.改造入門示例中的域類型

4.3.1.分析

(1)圖書id:

是否分詞:不用分詞超棺,因?yàn)椴粫?huì)根據(jù)商品id來搜索商品

是否索引:不索引向族,因?yàn)椴恍枰鶕?jù)圖書ID進(jìn)行搜索

是否存儲(chǔ):要存儲(chǔ),因?yàn)椴樵兘Y(jié)果頁面需要使用id這個(gè)值棠绘。

(2)圖書名稱:

是否分詞:要分詞件相,因?yàn)橐獙D書的名稱內(nèi)容分詞索引再扭,根據(jù)關(guān)鍵搜索圖書名稱抽取的詞。

是否索引:要索引夜矗。

是否存儲(chǔ):要存儲(chǔ)泛范。

(3)圖書價(jià)格:

是否分詞:要分詞,lucene對(duì)數(shù)字型的值只要有搜索需求的都要分詞和索引紊撕,因?yàn)閘ucene對(duì)數(shù)字型的內(nèi)容要特殊分詞處理罢荡,本例子可能要根據(jù)價(jià)格范圍搜索, 需要分詞和索引对扶。

是否索引:要索引

是否存儲(chǔ):要存儲(chǔ)

(4)圖書圖片地址:

是否分詞:不分詞

是否索引:不索引

是否存儲(chǔ):要存儲(chǔ)

(5)圖書描述:

是否分詞:要分詞

是否索引:要索引

是否存儲(chǔ):因?yàn)閳D書描述內(nèi)容量大区赵,不在查詢結(jié)果頁面直接顯示,不存儲(chǔ)浪南。

不存儲(chǔ)是來不在lucene的索引文件中記錄笼才,節(jié)省lucene的索引文件空間,如果要在詳情頁面顯示描述络凿,思路:

從lucene中取出圖書的id骡送,根據(jù)圖書的id查詢關(guān)系數(shù)據(jù)庫中book表得到描述信息。

4.3.2.代碼修改

修改BookDao的getDocument方法

public List<Document> getDocuments(List<Book> books){     

       // Document對(duì)象集合

       List<Document> docList = new ArrayList<Document>();

       // Document對(duì)象

       Document doc = null;

       for (Book book : books) {

           // 創(chuàng)建Document對(duì)象絮记,同時(shí)要?jiǎng)?chuàng)建field對(duì)象

           doc = new Document();

           // 圖書ID

           // 參數(shù):域名摔踱、域中存儲(chǔ)的內(nèi)容、是否存儲(chǔ)

           // 不分詞怨愤、索引派敷、要存儲(chǔ)

           // Field id = new TextField("id",      book.getId().toString(),Store.YES);

           Field id = new StoredField("id",      book.getBookId().toString());

           // 圖書名稱

           // 分詞、索引憔四、存儲(chǔ)

           Field name = new TextField("name",      book.getName(),Store.YES);

           // 圖書價(jià)格

           // 分詞膀息、索引、存儲(chǔ)

           Field price = new FloatField("price", book.getPrice(),      Store.YES);

           // 圖書圖片

           // 不分詞了赵、不索引潜支、要存儲(chǔ)

           Field pic = new StoredField("pic", book.getPic());

           // 圖書描述

           // 分詞、索引柿汛、不存儲(chǔ)

           Field desc = new      TextField("description",book.getDescription(), Store.NO);

           // 把域(Field)添加到文檔(Document)中

           doc.add(id);

           doc.add(name);

           doc.add(price);

           doc.add(pic);

           doc.add(desc);

           docList.add(doc);

       }

       return docList;

    }

4.3.3.測(cè)試

(1)去索引庫目錄中冗酿,手動(dòng)清空索引庫。

(2)重新創(chuàng)建索引庫络断。

(3)使用Luke驗(yàn)證分詞裁替、索引效果。

image.png

改造成功C脖俊H跖小!

5.索引庫維護(hù)

在第4節(jié)锥惋,我們需要重新創(chuàng)建索引的時(shí)候昌腰,是去索引庫目錄下开伏,手動(dòng)刪除的。

而在實(shí)際的開發(fā)中遭商,我們可能壓根就不知道索引庫在哪固灵,就算知道,我們也不可能每次都去手動(dòng)刪除劫流,非常之麻煩N撞!!祠汇!

所以仍秤,我們必須學(xué)習(xí)如何維護(hù)索引庫,使用程序來操作索引庫座哩。

需要注意的是徒扶,索引是與文檔緊密相連的,因此對(duì)索引的維護(hù)根穷,實(shí)際上就是對(duì)文檔的增刪改。

5.1.添加索引(文檔)

5.1.1.需求

數(shù)據(jù)庫中新上架了圖書导坟,必須把這些圖書也添加到索引庫中屿良,不然就搜不到該新上架的圖書了。

5.1.2.代碼實(shí)現(xiàn)

調(diào)用 indexWriter.addDocument(doc)添加索引惫周。

參考入門示例中的創(chuàng)建索引尘惧。

5.2.刪除索引(文檔)

5.2.1.需求

某些圖書不再出版銷售了,我們需要從索引庫中移除該圖書递递。

5.2.1.代碼實(shí)現(xiàn)

    @Test     

    public void deleteIndex() throws Exception {

       // 1喷橙、指定索引庫目錄

       Directory directory = FSDirectory.open(new      File("F:\\lucene\\0719"));

       // 2、創(chuàng)建IndexWriterConfig

       IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

              new StandardAnalyzer());

       // 3登舞、 創(chuàng)建IndexWriter

       IndexWriter writer = new IndexWriter(directory, cfg);

       // 4贰逾、通過IndexWriter來刪除索引

       // 刪除指定索引

       writer.deleteDocuments(new Term("name", "apache"));

       // 5、關(guān)閉IndexWriter

       writer.close();

       System.out.println("刪除成功");

    }

5.2.3.清空索引庫

@Test

    public void deleteIndex() throws Exception {

       // 1菠秒、指定索引庫目錄

       Directory directory = FSDirectory.open(new      File("F:\\lucene\\0719"));

       // 2疙剑、創(chuàng)建IndexWriterConfig

       IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

              new StandardAnalyzer());

       // 3、 創(chuàng)建IndexWriter

       IndexWriter writer = new IndexWriter(directory, cfg);

       // 4践叠、通過IndexWriter來刪除索引

       // 刪除指定索引

       writer.deleteAll();

       // 5言缤、關(guān)閉IndexWriter

       writer.close();

       System.out.println("清空索引庫成功");

    }

5.3.更新索引(文檔)

5.3.1.說明

Lucene更新索引比較特殊,是先刪除滿足條件的索引禁灼,再添加新的索引管挟。

5.3.2.代碼實(shí)現(xiàn)

// 修改索引

    @Test

    public void updateIndex() throws Exception {

       // 1、指定索引庫目錄

       Directory directory = FSDirectory.open(new      File("F:\\lucene\\0719"));

       // 2弄捕、創(chuàng)建IndexWriterConfig

       IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST,

              new StandardAnalyzer());

       // 3僻孝、 創(chuàng)建IndexWriter

       IndexWriter writer = new IndexWriter(directory, cfg);

       // 4导帝、通過IndexWriter來修改索引

       // a)、創(chuàng)建修改后的文檔對(duì)象

       Document document = new Document();

       // 文件名稱

       Field filenameField = new StringField("name", "updateIndex",      Store.YES);

       document.add(filenameField);

       // 修改指定索引為新的索引

       writer.updateDocument(new Term("name", "apache"), document);

       // 5皮璧、關(guān)閉IndexWriter

       writer.close();

       System.out.println("更新成功");

    }

6.搜索

問題:我們?cè)谌腴T示例中舟扎,已經(jīng)知道Lucene是通過IndexSearcher對(duì)象,來執(zhí)行搜索的悴务。那我們?yōu)槭裁催€要繼續(xù)學(xué)習(xí)Lucene呢睹限?
答:因?yàn)樵趯?shí)際的開發(fā)中,我們的查詢的業(yè)務(wù)是相對(duì)復(fù)雜的讯檐,比如我們?cè)谕ㄟ^關(guān)鍵詞查找的時(shí)候羡疗,往往進(jìn)行價(jià)格、商品類別的過濾别洪。
而Lucene提供了一套查詢方案叨恨,供我們實(shí)現(xiàn)復(fù)雜的查詢。

6.1.創(chuàng)建查詢的兩種方法

執(zhí)行查詢之前挖垛,必須創(chuàng)建一個(gè)查詢Query查詢對(duì)象痒钝。

Query自身是一個(gè)抽象類,不能實(shí)例化痢毒,必須通過其它的方式來實(shí)現(xiàn)初始化送矩。

在這里,Lucene提供了兩種初始化Query查詢對(duì)象的方式哪替。

6.1.1.使用Lucene提供Query子類

Query是一個(gè)抽象類栋荸,lucene提供了很多查詢對(duì)象,比如TermQuery項(xiàng)精確查詢凭舶,NumericRangeQuery數(shù)字范圍查詢等晌块。

使用TermQuery實(shí)例化

Query query = new TermQuery(new Term("name", "lucene"));

6.1.2.使用QueryParse解析查詢表達(dá)式

QueryParser會(huì)將用戶輸入的查詢表達(dá)式解析成Query對(duì)象實(shí)例。如下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());     

Query query = queryParser.parse("name:lucene");

6.2.常用的Query子類搜索

6.2.1.TermQuery

特點(diǎn):查詢的關(guān)鍵詞不會(huì)再做分詞處理帅霜,作為整體來搜索匆背。代碼如下:

/**

     * Query子類查詢之 TermQuery

     *  

     * 特點(diǎn):不會(huì)再對(duì)查詢的關(guān)鍵詞做分詞處理。

     *

     * 需要:查詢書名與java教程相關(guān)書义屏。

     */

@Test

    public void queryByTermQuery(){

       //1靠汁、獲取一個(gè)查詢對(duì)象

       Query query = new TermQuery(new Term("name", "編程思想"));

       doSearch(query);

    }

   private void doSearch(Query query) {

       try {

           //2、創(chuàng)建一個(gè)查詢的執(zhí)行對(duì)象

           //指定索引庫的目錄

           Directory d = FSDirectory.open(new      File("F:\\lucene\\0719"));

           //創(chuàng)建流對(duì)象

           IndexReader reader = DirectoryReader.open(d);

           //創(chuàng)建搜索執(zhí)行對(duì)象

           IndexSearcher searcher = new IndexSearcher(reader);

           //3闽铐、執(zhí)行搜索

           TopDocs result = searcher.search(query, 10);

           //4蝶怔、提出結(jié)果集,獲取圖書的信息

           int totalHits = result.totalHits;

           System.out.println("共查詢到"+totalHits+"條滿足條件的數(shù)據(jù)!");

           System.out.println("-----------------------------------------");

           //提取圖書信息兄墅。

           //score即相關(guān)度踢星。即搜索的關(guān)鍵詞和 圖書名稱的相關(guān)度,用來做排序處理

           ScoreDoc[] scoreDocs = result.scoreDocs;

           for (ScoreDoc scoreDoc : scoreDocs) {

              /**

               * scoreDoc.doc的返回值隙咸,是文檔的id沐悦, 即 將文檔寫入索引庫的時(shí)候成洗,lucene自動(dòng)給這份文檔做的一個(gè)編號(hào)。

               *

               * 獲取到這個(gè)文檔id之后藏否,即可以根據(jù)這個(gè)id瓶殃,找到這份文檔。

               */

              int docId = scoreDoc.doc;

              System.out.println("文檔在索引庫中的編號(hào):"+docId);

              //從文檔中提取圖書的信息

              Document doc = searcher.doc(docId);

              System.out.println("圖書id:"+doc.get("id"));

              System.out.println("圖書name:"+doc.get("name"));

              System.out.println("圖書price:"+doc.get("price"));

              System.out.println("圖書pic:"+doc.get("pic"));

              System.out.println("圖書description:"+doc.get("description"));

              System.out.println();

              System.out.println("------------------------------------");

           }

           //關(guān)閉連接副签,釋放資源

           if(null!=reader){

              reader.close();

           }

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

6.2.2.NumericRangeQuery

指定數(shù)字范圍查詢.(創(chuàng)建field類型時(shí)遥椿,注意與之對(duì)應(yīng))

/**

     * Query子類查詢  之  NumricRangeQuery

     * 需求:查詢所有價(jià)格在[60,80)之間的書

     * @param query

     */

@Test

    public void queryByNumricRangeQuery(){

       /**

        * 第一個(gè)參數(shù):要搜索的域

        * 第二個(gè)參數(shù):最小值

        * 第三個(gè)參數(shù):最大值

        * 第四個(gè)參數(shù):是否包含最小值

        * 第五個(gè)參數(shù):是否包含最大值

        */

       Query query = NumericRangeQuery.newFloatRange("price",      60.0f, 80.0f, true, false);

       doSearch(query);

    }

###6.2.3.BooleanQuery

BooleanQuery,布爾查詢淆储,實(shí)現(xiàn)組合條件查詢冠场。

/**

     * Query子類查詢  之  BooelanQuery查詢   組合條件查詢

     *

     * 需求:查詢書名包含java,并且價(jià)格區(qū)間在[60,80)之間的書本砰。

     */

    @Test

    public void queryBooleanQuery(){

       //1碴裙、要使用BooelanQuery查詢,首先要把單個(gè)創(chuàng)建出來点额,然后再通過BooelanQuery組合

       Query price = NumericRangeQuery.newFloatRange("price",      60.0f, 80.0f, true, false);

       Query name = new TermQuery(new Term("name", "java"));

       //2舔株、創(chuàng)建BooleanQuery實(shí)例對(duì)象

       BooleanQuery query = new BooleanQuery();

       query.add(name, Occur.MUST_NOT);

       query.add(price, Occur.MUST);

       /**

        * MSUT  表示必須滿足                          對(duì)應(yīng)的是  +

        * MSUT_NOT  必須不滿足                   應(yīng)對(duì)的是  -

        * SHOULD  可以滿足也可以不滿足     沒有符號(hào)

        *

        * SHOULD 與MUST、MUST_NOT組合的時(shí)候还棱,SHOULD就沒有意義了督笆。

        */

       doSearch(query);

    }

6.3.通過QueryParser搜索

6.3.1.特點(diǎn)

對(duì)搜索的關(guān)鍵詞,做分詞處理诱贿。

6.3.2.語法

6.3.2.1.基礎(chǔ)語法

域名:關(guān)鍵字

實(shí)例:name:java

6.3.2.2.組合條件語法

條件1 AND 條件2

條件1 OR 條件2

條件1 NOT 條件2

6.3.3.QueryParser

6.3.3.1.代碼實(shí)現(xiàn)

/**

     * 查詢解析器查詢  之  QueryParser查詢

     */

@Test

    public void queryByQueryParser(){

       try {

           //1、加載分詞器

           Analyzer analyzer = new StandardAnalyzer();

           /**

            * 2咕缎、創(chuàng)建查詢解析器實(shí)例對(duì)象

            * 第一個(gè)參數(shù):默認(rèn)搜索的域珠十。

            *          如果在搜索的時(shí)候,沒有特別指定搜索的域凭豪,則按照默認(rèn)的域進(jìn)行搜索

            *          如何在搜索的時(shí)候指定搜索域呢焙蹭?

            *          答:格式  域名:關(guān)鍵詞        即   name:java教程

            *

            * 第二個(gè)參數(shù):分詞器   ,對(duì)關(guān)鍵詞做分詞處理

            */

           QueryParser parser = new QueryParser("description",      analyzer);

           Query query = parser.parse("name:java教程");

           doSearch(query);

       } catch (Exception e) {

           e.printStackTrace();

       }

    }

6.3.4.MultiFieldQueryParser

通過MulitFieldQueryParse對(duì)多個(gè)域查詢嫂伞。

/**

     * 查詢解析器查詢  之  MultiFieldQueryParser查詢

     *  

     *     特點(diǎn):同時(shí)指定多個(gè)搜索域孔厉,并且對(duì)關(guān)鍵做分詞處理

     */

    @Test

    public void queryByMultiFieldQueryParser(){

       try {

           //1、定義多個(gè)搜索的  name帖努、description

           String[] fields = {"name","description"};

           //2撰豺、加載分詞器

           Analyzer analyzer = new StandardAnalyzer();

           //3、創(chuàng)建 MultiFieldQueryParser實(shí)例對(duì)象

           MultiFieldQueryParser mParser = new      MultiFieldQueryParser(fields, analyzer);

           Query query = mParser.parse("lucene教程");

           doSearch(query);

       } catch (Exception e) {

           e.printStackTrace();

       }

    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拼余,一起剝皮案震驚了整個(gè)濱河市污桦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匙监,老刑警劉巖凡橱,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件小作,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡稼钩,警方通過查閱死者的電腦和手機(jī)顾稀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坝撑,“玉大人静秆,你說我怎么就攤上這事∩茉兀” “怎么了诡宗?”我有些...
    開封第一講書人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長击儡。 經(jīng)常有香客問我塔沃,道長,這世上最難降的妖魔是什么阳谍? 我笑而不...
    開封第一講書人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任蛀柴,我火速辦了婚禮,結(jié)果婚禮上矫夯,老公的妹妹穿的比我還像新娘鸽疾。我一直安慰自己,他們只是感情好训貌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開白布制肮。 她就那樣靜靜地躺著,像睡著了一般递沪。 火紅的嫁衣襯著肌膚如雪豺鼻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評(píng)論 1 305
  • 那天款慨,我揣著相機(jī)與錄音儒飒,去河邊找鬼。 笑死檩奠,一個(gè)胖子當(dāng)著我的面吹牛桩了,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播埠戳,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼井誉,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了乞而?” 一聲冷哼從身側(cè)響起送悔,我...
    開封第一講書人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后欠啤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荚藻,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年洁段,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了应狱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祠丝,死狀恐怖疾呻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情写半,我是刑警寧澤岸蜗,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站叠蝇,受9級(jí)特大地震影響璃岳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悔捶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一铃慷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蜕该,春花似錦犁柜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绢淀,卻和暖如春股囊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背更啄。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留居灯,地道東北人祭务。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像怪嫌,于是被迫代替她去往敵國和親义锥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355