Lucene介紹與應用

Lucene介紹與應用php

1、全文檢索介紹html

1.數據結構java

結構化數據:mysql

指具備「固定格式」 或「限定長度」的數據; 

例如:數據庫中的數據、元數據……

非結構化數據程序員

指不定長度或無固定格式的數據;

例如:文本、圖片、視頻、圖表……

2.數據的搜索算法

順序掃描法sql

從第一個文件掃描到最後一個文件,把每個文件內容從開頭掃到結尾,直到掃完全部的文件。

全文檢索法數據庫

將非結構化數據中的一部分信息提取出來,從新組織,創建索引,使其變得有必定結構,而後對此有必定結構的數據進行搜索,從而達到搜索相對較快的目的。

3.全文檢索apache

例如:新華字典。字典的拼音表和部首檢字表就至關於字典的索引,咱們能夠經過查找索引從而找到具體的字解釋。若是沒有建立索引,就要從字典的首頁一頁頁地去查找。api

這種先創建索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search) 。

全文檢索的核心

建立索引:將從全部的結構化和非結構化數據提取信息,建立索引的過程。

搜索索引:就是獲得用戶的查詢請求,搜索建立的索引,而後返回結果的過程。

4.倒排索引

倒排索引(英文:InvertedIndex),也稱爲反向索引,是一種索引方法,實現「單詞-文檔矩陣」的一種具體存儲形式,常被用於存儲在全文搜索下某個單詞與文檔的存儲位置的映射,經過倒排索引,能夠根據單詞快速獲取包含這個單詞的文檔列表。

倒排索引的結構主要由兩個部分組成:「單詞詞典」和「倒排表」。

索引方法例子

3個文檔內容爲:

    1.php是過去最流行的語言。

    2.java是如今最流行的語言。

    3. Python是將來流行的語言。

clipboard.png

倒排索引的建立

1.使用分詞系統將文檔切分紅單詞序列,每一個文檔就成了由由單詞序列構成的數據流;

2.給不一樣的單詞賦予惟一的單詞id,記錄下對應的單詞;

3.同時記錄單詞出現的文檔,造成倒排列表。每一個單詞都指向了文檔(Document)鏈表。

clipboard.png

倒排索引的查詢

假如說用戶須要查詢:   「如今流行」

1.將用戶輸入進行分詞,分爲」如今」和」流行」;

2.取出包含字符串「如今」的文檔鏈表;

3.取出包含字符串「流行」的文檔鏈表;

4.經過合併鏈表,找出包含有」如今」或者」流行」的鏈表。

倒排索引原理

固然倒排索引的結構也不是上面說的那麼簡單,索引系統還能夠記錄除此以外的更多信息。詞對應的倒排列表不只記錄了文檔編號還記錄了單詞頻率信息。詞頻信息在搜索結果時,是重要的排序依據。這裏先了解下,後面的評分計算就要用到這個。

索引和搜索流程圖

clipboard.png

2、Lucene入門

• Lucene是一套用於全文檢索和搜尋的開源程序庫,由Apache軟件基金會支持和提供;

• 基於java的全文檢索工具包, Lucene並非現成的搜索引擎產品,但能夠用來製做搜索引擎產品;

• 官網:http://lucene.apache.org/

1.Lucene的整體結構

clipboard.png

從lucene的整體架構圖能夠看出:

1.Lucene庫提供了建立索引和搜索索引的API。

 2.應用程序須要作的就是收集文檔數據,建立索引;經過用戶輸入查詢索引的獲得返回結果。

2.Lucene的幾個基本概念

Index(索引):相似數據庫的表的概念,但它徹底沒有約束,能夠修改和添加里面的文檔,文檔裏的內容能夠任意定義。

Document(文檔):相似數據庫內的行的概念,一個Index內會包含多個Document。

Field(字段):一個Document會由一個或多個Field組成,分詞就是對Field 分詞。

Term(詞語)和Term Dictionary(詞典):Lucene中索引和搜索的最小單位,一個Field會由一個或多個Term組成,Term是由Field通過Analyzer(分詞)產生。Term Dictionary即Term詞典,是根據條件查找Term的基本索引。

clipboard.png

3.Lucene建立索引過程

Lucene建立索引過程以下:

1.建立一個IndexWriter用來寫索引文件,它有幾個參數,INDEX_DIR就是索引文件所存放的位置,Analyzer即是用來 對文檔進行詞法分析和語言處理的。

2.建立一個Document表明咱們要索引的文檔。將不一樣的Field加入到文檔中。不一樣類型的信息用不一樣的Field來表示

3.IndexWriter調用函數addDocument將索引寫到索引文件夾中。

4.Lucene搜索過程

搜索過程以下:

1.IndexReader將磁盤上的索引信息讀入到內存,INDEX_DIR就是索引文件存放的位置。

2.建立IndexSearcher準備進行搜索。

3.建立Analyer用來對查詢語句進行詞法分析和語言處理。

4.建立QueryParser用來對查詢語句進行語法分析。

5.QueryParser調用parser進行語法分析,造成查詢語法樹,放到Query中。

6.IndexSearcher調用search對查詢語法樹Query進行搜索,獲得結果TopScoreDocCollector。

3、Lucene入門案例一

1.案例一代碼

引入lucene的jar包

public class LuceneTest {

    public static void main(String[] args) throws Exception {
        // 1. 準備中文分詞器
        IKAnalyzer analyzer = new IKAnalyzer();
        // 2. 建立索引
        List<String> productNames = new ArrayList<>();
        productNames.add("小天鵝TG100-1420WDXG");
        productNames.add("小天鵝TB80-easy60W 洗漂脫時間自由可調,京東微聯智能APP手機控制");
        productNames.add("小天鵝TG90-1411DG 洗滌容量:9kg 脫水容量:9kg 顯示屏:LED數碼屏顯示");
        productNames.add("小天鵝TP75-V602 流線蝶形波輪,超強噴淋漂洗");
        productNames.add("小天鵝TG100V20WDG 大件洗,無旋鈕外觀,智能WiFi");
        productNames.add("小天鵝TD80-1411DG 洗滌容量:8kg 脫水容量:8kg 顯示屏:LED數碼屏顯示");
        productNames.add("海爾XQB90-BZ828 洗滌容量:9kg 脫水容量:9kg 顯示屏:LED數碼屏顯示");
        productNames.add("海爾G100818HBG 極簡智控面板,V6蒸汽烘乾,深層潔淨");
        productNames.add("海爾G100678HB14SU1 洗滌容量:10kg 脫水容量:10kg 顯示屏:LED數碼屏顯");
        productNames.add("海爾XQB80-KM12688 智能自由洗,超淨洗");
        productNames.add("海爾EG8014HB39GU1 手機智能,一鍵免熨燙,空氣淨化洗");
        productNames.add("海爾G100818BG 琥珀金機身,深層潔淨,輕柔雪紡洗");
        productNames.add("海爾G100728BX12G 安全磁鎖,健康下排水");
        productNames.add("西門子XQG80-WD12G4C01W 衣幹即停,熱風除菌,低噪音");
        productNames.add("西門子XQG80-WD12G4681W 智能烘乾,變速節能,無刷電機");
        productNames.add("西門子XQG100-WM14U568LW 洗滌容量:10kg 脫水容量:10kg 顯示屏:LED");
        productNames.add("西門子XQG80-WM10N1C80W 除菌、洗滌分離,防過敏程序");
        productNames.add("西門子XQG100-WM14U561HW 洗滌容量:10kg 脫水容量:10kg 顯示屏:LED");
        productNames.add("西門子XQG80-WM12L2E88W 洗滌容量:8kg 脫水容量:8kg 顯示屏:LED觸摸");
        Directory index = createIndex(analyzer, productNames);

        // 3. 查詢器
        String keyword = "西門子 LED";
        Query query = new QueryParser("name", analyzer).parse(keyword);
        // 4. 搜索
        IndexReader reader = DirectoryReader.open(index);
        IndexSearcher searcher = new IndexSearcher(reader);
        int numberPerPage = 1000;
        System.out.printf("當前一共有%d條數據%n"+"<br>", productNames.size());
        System.out.printf("查詢關鍵字是:\"%s\"%n"+"<br>", keyword);
        ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;
        // 5. 顯示查詢結果
        showSearchResults(searcher, hits, query, analyzer);
        // 6. 關閉查詢
        reader.close();
    }

    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)
            throws Exception {
        System.out.println("找到 " + hits.length + " 個命中.  <br>");
        System.out.println("序號\t匹配度得分\t結果  <br>");

        SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
        Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

        for (int i = 0; i < hits.length; ++i) {
            ScoreDoc scoreDoc= hits[i];
            int docId = scoreDoc.doc;
            Document d = searcher.doc(docId);
            List<IndexableField> fields = d.getFields();
            System.out.print((i + 1));
            System.out.print("\t" + scoreDoc.score);
            for (IndexableField f : fields) {
                TokenStream tokenStream = analyzer.tokenStream(f.name(), new StringReader(d.get(f.name())));
                String fieldContent = highlighter.getBestFragment(tokenStream, d.get(f.name()));                
                System.out.print("\t" + fieldContent);
            }
            System.out.println("<br>");
        }
    }


    private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {
        //存在內存中,新建一個詞典
        Directory index = new RAMDirectory();
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        IndexWriter writer = new IndexWriter(index, config);
        for (String name : products) {
            addDoc(writer, name);
        }
        writer.close();
        return index;
    }
    
    /**
     * 添加文檔內容
     * @param w
     * @param name
     * @throws IOException
     */
    private static void addDoc(IndexWriter w, String name) throws IOException {
        //建立一個文檔
        Document doc = new Document();
        doc.add(new TextField("name", name, Field.Store.YES));
        w.addDocument(doc);
    }
}

2.代碼解析

建立索引

private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {
    //存在內存中,新建一個詞典
    Directory index = new RAMDirectory();
    IndexWriterConfig config = new IndexWriterConfig(analyzer);
    IndexWriter writer = new IndexWriter(index, config);
    for (String name : products) {
        addDoc(writer, name);
    }
    writer.close();
    return index;
}

private static void addDoc(IndexWriter w, String name) throws IOException {
    //建立一個文檔
    Document doc = new Document();
    doc.add(new TextField("name", name, Field.Store.YES));
    w.addDocument(doc);
}

上面代碼是將List中的內容保存在文檔中,使用analyzer分詞器分詞,建立索引,索引保存在內存中。 IndexWriter 對象用來寫索引的。

查詢索引

// 3. 查詢器
String keyword = "西門子 智能";
Query query = new QueryParser("name", analyzer).parse(keyword);
// 4. 搜索
IndexReader reader = DirectoryReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
int numberPerPage = 1000;
System.out.printf("當前一共有%d條數據%n", productNames.size());
System.out.printf("查詢關鍵字是:\"%s\"%n", keyword);
ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;
// 5. 顯示查詢結果
showSearchResults(searcher, hits, query, analyzer);
// 6. 關閉查詢
reader.close();

上面代碼是查詢代碼,首先對構建查詢條件Query對象,讀取索引,建立IndexSearcher 查詢對象,傳入查詢條件,獲得查詢結果,將結果解析出來,返回。

分詞器

建立索引和查詢都要用到分詞器,在Lucene中分詞主要依靠Analyzer類解析實現。Analyzer類是一個抽象類,分詞的具體規則是由子類實現的,不一樣的語言規則,要有不一樣的分詞器, Lucene默認的StandardAnalyzer是不支持中文的分詞。

代碼中用到了IKAnalyzer分詞器,IKAnalyzer是第三方實現的分詞器,繼承自Lucene的Analyzer類,針對中文文本進行處理的分詞器。

打分機制

從案例返回結果來看,有一列匹配度得分,得分越高的排在越前面,排在前面的查詢結果也越準確。

打分公式:

clipboard.png

Lucene庫也實現了上面的打分算法,查詢結果也會根據分數進行排序。

高亮顯示

SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));

將查詢結果放到html頁面,就會發現查詢結果裏關鍵字被標記爲紅色。在 Lucene庫的org.apache.lucene.search.highlight包中提供了關於高亮顯示檢索關鍵字的方法,能夠對返回的結果中出現了的關鍵字進行標記。

4、Lucene入門案例二

1.案例介紹

1.將14萬條商品詳細信息到mysql數據庫;

2.使用Lucene庫建立索引;

3.使用Luncene查詢索引,並作分頁操做,獲得返回查詢到的數據,並記錄查詢時長;

4.使用JDBC鏈接mysql數據庫,採用like查詢,對商品進行分頁操做,返回查詢到的數據,記錄查詢時長;

5.比較mysql的模糊查詢與Lucene全文檢索查詢。

2.案例二代碼

引入lucene的jar包,和mysql的驅動包,建立數據庫product表,插入數據.

/**
 * 商品bean類
 * @author yizl
 *
 */
public class Product {
    /**
     * 商品id
     */
    private int id;
    /**
     * 商品名稱
     */
    private String name;
    /**
     * 商品類型
     */
    private String category;
    /**
     * 商品價格
     */
    private float price;
    /**
     * 商品產地
     */
    private String place;
    /**
     * 商品條形碼
     */
    private String code;
    
    ......    
    
}


public class TestLucene {

    private static ProductDao dao = new ProductDao();

    public static void main(String[] args) throws Exception {
        // 1. 準備中文分詞器
        IKAnalyzer analyzer = new IKAnalyzer();
        // 2. 索引
        Directory index = createIndex(analyzer);
        // 3. 查詢器
        Scanner s = new Scanner(System.in);

        while (true) {
            System.out.print("請輸入查詢關鍵字:");
            String keyword = s.nextLine();
            System.out.println("當前關鍵字是:" + keyword);
            long start = System.currentTimeMillis();
            // 查詢名字字段
            Query query = new QueryParser("name", analyzer).parse(keyword);
            // 4. 搜索
            IndexReader reader = DirectoryReader.open(index);
            IndexSearcher searcher = new IndexSearcher(reader);
            ScoreDoc[] hits = pageSearch(query, searcher, 1, 10);
            // 5. 顯示查詢結果
            showSearchResults(searcher, hits, query, analyzer);
            // 6. 關閉查詢
            reader.close();
            System.out.println("使用Lucene查詢索引,耗時:" + (System.currentTimeMillis() - start) + "毫秒");

            System.out.println("-----------------------分割線-------------------------------");
            // 7.經過數據庫進行模糊查詢
            selectProductOfName(keyword);
        }
    }

    /**
     * 經過mysql商品名查詢
     */
    private static void selectProductOfName(String str) {
        long start = System.currentTimeMillis();
        ResultBean<List<Product>> resultBean = dao.selectProductOfName(str, 1, 10);
        PageBean pageBean = resultBean.getPageBean();
        List<Product> products = resultBean.getData();
        System.out.println("查詢出的總條數\t:" + pageBean.getTotal() + "條");
        System.out.println("當前第" + pageBean.getPageNow() + "頁,每頁顯示" + pageBean.getPageSize() + "條數據");
        System.out.println("序號\t結果");
        for (int i = 0; i < products.size(); i++) {
            Product product = products.get(i);
            System.out.print((i + 1));
            System.out.print("\t" + product.getId());
            System.out.print("\t" + product.getName());
            System.out.print("\t" + product.getPrice());
            System.out.print("\t" + product.getPlace());
            System.out.print("\t" + product.getCode());
            System.out.println("<br>");
        }

        System.out.println("使用mysql查詢,耗時:" + (System.currentTimeMillis() - start) + "毫秒");
    }

    /**
     * 顯示找到的結果
     * 
     * @param searcher
     * @param hits
     * @param query
     * @param analyzer
     * @throws Exception
     */
    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)
            throws Exception {
        System.out.println("序號\t匹配度得分\t結果");
        for (int i = 0; i < hits.length; ++i) {
            ScoreDoc scoreDoc = hits[i];
            int docId = scoreDoc.doc;
            Document d = searcher.doc(docId);
            List<IndexableField> fields = d.getFields();
            System.out.print((i + 1));
            System.out.print("\t" + scoreDoc.score);
            for (IndexableField f : fields) {
                System.out.print("\t" + d.get(f.name()));
            }
            System.out.println("<br>");
        }
    }

    /**
     * 分頁查詢
     * 
     * @param query
     * @param searcher
     * @param pageNow
     *            當前第幾頁
     * @param pageSize
     *            每頁顯示條數
     * @return
     * @throws IOException
     */
    private static ScoreDoc[] pageSearch(Query query, IndexSearcher searcher, int pageNow, int pageSize)
            throws IOException {
        TopDocs topDocs = searcher.search(query, pageNow * pageSize);
        System.out.println("查詢到的總條數\t" + topDocs.totalHits);
        System.out.println("當前第" + pageNow + "頁,每頁顯示" + pageSize + "條數據");
        ScoreDoc[] alllScores = topDocs.scoreDocs;
        List<ScoreDoc> hitScores = new ArrayList<>();

        int start = (pageNow - 1) * pageSize;
        int end = pageSize * pageNow;
        for (int i = start; i < end; i++)
            hitScores.add(alllScores[i]);

        ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[] {});
        return hits;
    }

    /**
     * 建立Index,將數據存入內存中
     * 
     * @param analyzer
     * @return
     * @throws IOException
     */
    private static Directory createIndex(IKAnalyzer analyzer) throws IOException {
        long start = System.currentTimeMillis();
        Directory index = new RAMDirectory();
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        IndexWriter writer = new IndexWriter(index, config);
        List<Product> products = dao.selectAllProduct();
        int total = products.size();
        int count = 0;
        int per = 0;
        int oldPer = 0;
        for (Product p : products) {
            addDoc(writer, p);
            count++;
            per = count * 100 / total;
            if (per != oldPer) {
                oldPer = per;
                System.out.printf("索引中,總共要添加 %d 條記錄,當前添加進度是: %d%% %n", total, per);
            }

        }
        System.out.println("索引建立耗時:" + (System.currentTimeMillis() - start) + "毫秒");
        writer.close();
        return index;
    }

    /**
     * 往lucene中添加字段
     * 
     * @param w
     * @param p
     * @throws IOException
     */
    private static void addDoc(IndexWriter w, Product p) throws IOException {
        Document doc = new Document();
        doc.add(new TextField("id", String.valueOf(p.getId()), Field.Store.YES));
        doc.add(new TextField("name", p.getName(), Field.Store.YES));
        doc.add(new TextField("category", p.getCategory(), Field.Store.YES));
        doc.add(new TextField("price", String.valueOf(p.getPrice()), Field.Store.YES));
        doc.add(new TextField("place", p.getPlace(), Field.Store.YES));
        doc.add(new TextField("code", p.getCode(), Field.Store.YES));
        w.addDocument(doc);
    }
}



public class ProductDao {

    private static String url = "jdbc:mysql://localhost:3306/lucene?useUnicode=true&characterEncoding=utf8";
    private static String user = "root";
    private static String password = "root";
    
    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        Connection conn = null;
        // 經過工具類獲取鏈接對象
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection(url, user, password);
        return conn;
    }

    /**
     * 批量增長商品
     * @param pList
     */
    public void insertProduct(List<Product> pList) {
        String insertProductTop="INSERT INTO `product` (`id`, `name`, "
                + "`category`, `price`, `place`, `code`) VALUES ";
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            // 3.建立Statement對象
            stmt = conn.createStatement();
            int count=0;
            // 4.sql語句
            StringBuffer sb = new StringBuffer();
            for (int i = 0,len=pList.size(); i < len; i++) {
                Product product = pList.get(i);
                sb.append("(" + product.getId() + ",'" + product.getName() 
                + "','" + product.getCategory()+ "'," + product.getPrice()
                + ",'" + product.getPlace() + "','" + product.getCode() + "')");
                if (i==len-1) {
                    sb.append(";");
                    break;
                }else {
                    sb.append(",");
                }
                //數據量太大會致使一次執行不了,一次最多執行20000條
                if(i%20000==0&&i!=0) {
                    sb.deleteCharAt(sb.length()-1);
                    sb.append(";");
                    String sql = insertProductTop+sb;
                    count += stmt.executeUpdate(sql);
                    //將sb清空
                    sb.delete(0, sb.length());
                }
            }
            
            String sql = insertProductTop+sb;
            // 5.執行sql
            count += stmt.executeUpdate(sql);
            System.out.println("影響了" + count + "行");
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            close(conn, stmt);
        }
    }
    
    /**
     * 關閉資源
     * @param conn
     * @param stmt
     */
    private void close(Connection conn, Statement stmt) {
        // 關閉資源
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }

    // 
    public void deleteAllProduct() {
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            // 3.建立Statement對象
            stmt = conn.createStatement();
            // 4.sql語句
            String sql = "delete from product";
            // 5.執行sql
            int count = stmt.executeUpdate(sql);
            System.out.println("影響了" + count + "行");

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            // 關閉資源
            close(conn, stmt);
        }
    }

    /**
     * 查詢全部商品
     */
    public List<Product> selectAllProduct() {
        List<Product> pList=new ArrayList<>();
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getConnection();
            // 3.建立Statement對象
            stmt = conn.createStatement();
            // 4.sql語句
            String sql = "select * from product";
            // 5.執行sql
            ResultSet rs = stmt.executeQuery(sql);
            while (rs.next()) {
                Product product=new Product();
                product.setId(rs.getInt("id"));
                product.setName(rs.getString("name"));
                product.setCategory(rs.getString("category"));
                product.setPlace(rs.getString("place"));
                product.setPrice(rs.getFloat("price"));
                product.setCode(rs.getString("code"));
                pList.add(product);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            // 關閉資源
            close(conn, stmt);
        }
        return pList;
    }
    /**
     * 經過商品名模糊匹配商品
     * @param strName
     * @param pageNow
     * @param pageSize
     * @return
     */
    public ResultBean<List<Product>> selectProductOfName(String strName, int pageNow, int pageSize) {
        ResultBean<List<Product>> resultBean=new ResultBean<List<Product>>();
        PageBean pageBean =new PageBean();
        pageBean.setPageNow(pageNow);
        pageBean.setPageSize(pageSize);
        List<Product> pList=new ArrayList<>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        try {
            conn = getConnection();
            // sql語句
            String sql = "SELECT id,name,category,place,price,code FROM product"
                    + " where name like ? limit "+(pageNow-1)*pageSize+","+pageSize; 
            // 3.建立PreparedStatement對象,sql預編譯
            pstmt = conn.prepareStatement(sql);
            // 4.設定參數
            pstmt.setString(1, "%" + strName + "%" );                  
            // 5.執行sql,獲取查詢的結果集  
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                Product product=new Product();
                product.setId(rs.getInt("id"));
                product.setName(rs.getString("name"));
                product.setCategory(rs.getString("category"));
                product.setPlace(rs.getString("place"));
                product.setPrice(rs.getFloat("price"));
                product.setCode(rs.getString("code"));
                pList.add(product);
            }
            
            String selectCount = "SELECT count(1) c FROM product"
                    + " where name like ? ";
            pstmt = conn.prepareStatement(selectCount);
            pstmt.setString(1, "%" + strName + "%" ); 
            ResultSet rs1 = pstmt.executeQuery();
            int count=0;
            while (rs1.next()) {
                count = rs1.getInt("c");
            }
            pageBean.setTotal(count);
            resultBean.setPageBean(pageBean);
            resultBean.setData(pList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            // 關閉資源
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }
        return resultBean;
    
    }
}

/**
 * 返回結果bean
 * @author yizl
 *
 * @param <T>
 */
public class ResultBean<T> {
    
    /**
     * 分頁信息
     */
    private PageBean pageBean;
    
    /**
     * 狀態碼
     */
    private Integer code;
    /**
     * 提示信息
     */
    private String msg;
    /**
     * 返回數據
     */
    private T data;
    

/**
 * 分頁bean
 * @author yizl
 *
 */
public class PageBean {
    /**
     * 當前頁數
     */
    private Integer pageNow;

    /**
     * 每頁條數
     */
    private Integer pageSize;
    /**
     * 總數
     */
    private Integer total;

4.Lucene的分頁查詢

private static ScoreDoc[] pageSearch(Query query, IndexSearcher searcher, int pageNow, int pageSize)
throws IOException {
TopDocs topDocs = searcher.search(query, pageNow * pageSize);
System.out.println("查詢到的總條數\t" + topDocs.totalHits);
System.out.println("當前第" + pageNow + "頁,每頁顯示" + pageSize + "條數據");
ScoreDoc[] alllScores = topDocs.scoreDocs;
List<ScoreDoc> hitScores = new ArrayList<>();

int start = (pageNow - 1) * pageSize;
int end = pageSize * pageNow;
for (int i = start; i < end; i++)
hitScores.add(alllScores[i]);
ScoreDoc[] hits = hitScores.toArray(new ScoreDoc[] {});
return hits;
}

先把全部的命中數查詢出來,在進行分頁,有點是查詢快,缺點是內存消耗大。

5.結果比較分析

1.14萬條數據,從建立lucene索引耗時:11678毫秒,建立索引仍是比較耗時的,可是索引只用建立一次,後面都查詢均可以使用;
2.從查詢時間來看,使用Lucene查詢,基本都在10ms左右,mysql查詢耗時在150ms以上,查詢速度方面有很大的提高,特別是數據量大的時候更加明顯;

3.從查詢精準度來講,輸入單個的詞語可能都能查詢到結果,輸入組合詞語,mysql能夠匹配不了,Lucene依然能夠查詢出來,將匹配度高的結果排在前面,更精準。

6.Lucene索引與mysql數據庫對比

<IMG src="file:///C:UsersyizlAppDataRoamingfeiqRichOle3864897237.bmp">
clipboard.png

5、總結

首先咱們瞭解全文檢索方法,全文檢索搜索非結構化數據速度快等優勢,倒排索引是如今最經常使用的全文檢索方法,索引的核心就是怎麼建立索引和查詢索引。至於怎麼實現建立和查詢,Apache軟件基金會很貼心的爲咱們Java程序員提供了Lucene開源庫,它爲咱們提供了建立和查詢索引的api,這就是咱們學習Lucene的目的。

相關文章
相關標籤/搜索