使用Lucene對數據庫搜索

         前面介紹了Lucene對於文件怎麼進行索引和搜索,這次就來說說lucene怎麼對數據庫進行索引和搜索。衆所周知,數據庫數據可以通過SQL來查詢,Java EE系統中也可以通過Hbiernate,Ibatis,Spring JdbcTemplate或者直接通過JDBC API來訪問,但是當數據庫很大時,執行SQL語句的代價就很大,速度也會很忙,尤其是在大量用戶高併發量訪問的情況下尤爲明顯,就算是給數據庫創建索引後提高也有限,而且並不是所有的數據庫都支持全文檢索。所以通過lucene創建數據庫索引能夠大大提高系統的性能,其優點主要體現在以下幾個方面:

  • 通過創建唯一索引,能保證數據的唯一性;
  • 大大提高了數據檢索的速度,這是創建數據庫索引的重要原因;
  • 加快了表與表之間的鏈接,對實現數據參考完整性具有重大的意義;
  • 通過創建索引,在查詢過程中採用優化策略,能夠提高系統的性能;
  • 使用分組和排序字句進行數據檢索時,通過創建索引能夠減少查詢中分組和排序的時間;
  • 可特別針對經常需要檢索的列創建索引,能夠減少盲目搜索的時間,大大提高搜索效率;


下面看一下對於數據庫創建索引的例子,我有一個mysql數據庫test,其中有一張University的表,記錄了大學信息:

package com.seiya;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * Created by dell on 2015/8/10.
 * 對數據庫數據做索引
 */
public class DataBaseIndex {

    public static final String INDEXPATH = "D://indexFile//dbIndex";

    private static String url="jdbc:mysql://localhost:3306/test?user=root&password=root&useUnicode=true&characterEncoding=UTF-8";

    public static boolean createIndex() {
        try {
            Directory dir = FSDirectory.open(Paths.get(INDEXPATH)); // 使用了nio,存儲索引的路徑
            Analyzer analyzer = new StandardAnalyzer(); // 無參構造函數
            IndexWriterConfig iwc = new IndexWriterConfig(analyzer); // 新的IndexWriter配置類
            iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE); // 創建模式打開
            //iwc.setRAMBufferSizeMB(256.0); // 設置內存緩存的大小,提高索引效率,不過如果修改過大的值,需要修改JVM的內存值
            IndexWriter writer = new IndexWriter(dir, iwc); // 創建IndexWriter

            Class.forName("com.mysql.jdbc.Driver");
            Connection con = DriverManager.getConnection(url);
            Statement stmt = con.createStatement();
            String query = "select * from University";
            System.out.println("query==="+query);
            ResultSet rs=stmt.executeQuery(query);
            while(rs.next()) {
                Document doc = new Document();
                String cName = rs.getString("UniversityName");
                System.out.println("cName--------------->"+cName);
                String eName = rs.getString("E_Name");
                System.out.println("eName--------------->"+eName);
                String type = rs.getString("Type");
                String location = rs.getString("Location");
                doc.add(new TextField("cName",cName,Field.Store.YES));
                doc.add(new TextField("eName",eName,Field.Store.YES)); // 做analyze
                doc.add(new StringField("type",type,Field.Store.YES)); // 不做analyze
                doc.add(new StringField("location",location,Field.Store.YES));
                writer.addDocument(doc);
            }
            writer.close();
        }catch(Exception ex) {
            ex.printStackTrace();
        }
        return true;
    }

    public static void main(String[] args) {
        createIndex();
    }

}

這裏要注意的是,對於lucene5,StringField是不做analyze的,TextField是做了分詞的。

再來看一下查詢數據庫數據的例子:

package com.seiya;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.IOException;
import java.nio.file.Paths;


/**
 * Created by dell on 2015/8/11.
 */
public class DataBaseSearcher {

    public static final String INDEXPATH = "D://indexFile//dbIndex";
    private static int TOP_NUM = 100; // 顯示記錄數

    public static void searchData(String queryStr) {
        Directory dir = null;
        try {
            dir = FSDirectory.open(Paths.get(INDEXPATH));
            IndexReader reader = DirectoryReader.open(dir);
            IndexSearcher searcher = new IndexSearcher(reader);
            Analyzer analyzer = new StandardAnalyzer();
            String fieldString = "cName";
            QueryParser parser = new QueryParser(fieldString, analyzer);
            parser.setDefaultOperator(QueryParser.AND_OPERATOR);
            Query query = parser.parse(queryStr);
            TopDocs hits = searcher.search(query,5); // 查找操作
            for(ScoreDoc scoreDoc : hits.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc); // 根據文檔打分得到文檔的內容
                System.out.println(doc.get("cName")); // 找到文件後,輸出路徑
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParseException ex) {
            ex.printStackTrace();
        }
    }

    public static void main(String[] args) {
        searchData("大學");
    }

}

由於IKAnalyzer目前還沒有支持lucene5,所以對於需要用到IK之類的中文分詞器的程序,最好還是使用穩定的Lucene4,當然也可以自己修改IKAnalyzer的源代碼進行適配。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章