现在的位置: 首页 > 综合 > 正文

全文检索Lucene的第一次尝试

2013年10月08日 ⁄ 综合 ⁄ 共 4922字 ⁄ 字号 评论关闭

由于系统搜索速度一直不理想,今天决定用Lucene进行 索引。然后全表检索 核心代码如下

 
package net.java2000.forum.util;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
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.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.WildcardQuery;
/**
 * 
 * @author 赵学庆 www.java2000.net
 *
 */
public class Lucene {
  private static final String indexPath = "d:/index/www.java2000.net";

  /**
   * @param args
   * @throws Exception
   */
  public static void main(String[] args) throws Exception {
    // create();
    Lucene l = new Lucene();
    System.out.println("模糊搜索/n------------------------------");
    l.seacherWildcard("域名");
    System.out.println("索引搜索/n------------------------------");
    l.seacherIndex("域名 AND 系统");
  }

  public static void rebuildAll() {
    synchronized (indexPath) {
      Lucene l = new Lucene();
      DataSource ds = (DataSource) Factory.getBean("dataSource");
      Connection con = null;
      Statement stat = null;
      ResultSet rs = null;
      try {
        con = ds.getConnection();
        stat = con.createStatement();
        rs = stat.executeQuery("select id,subject,content from t_post");
        if (rs != null) {
          l.Index(rs);
        }
      } catch (Exception ex) {
        ex.printStackTrace();
      } finally {
        if (rs != null) {
          try {
            rs.close();
          } catch (Exception ex) {}
        }
        if (stat != null) {
          try {
            stat.close();
          } catch (Exception ex) {}
        }
        if (con != null) {
          try {
            con.close();
          } catch (Exception ex) {}
        }
      }
    }
  }

  public synchronized Analyzer getAnalyzer() {
    return new StandardAnalyzer();
  }

  private synchronized void Index(ResultSet rs) {// 通过结果集就可以获得数据源了
    try {
      IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), true);
      Date start = new Date();
      while (rs.next()) {
        Document doc = new Document();// 一个文档相当与表的一条记录
        doc.add(new Field("id", rs.getString("id"), Field.Store.YES, Field.Index.UN_TOKENIZED));// 字段id放的是数据库表中的id,lucene的一条记录的一个字段下的数据可以放多个值,这点与数据库表不同
        doc.add(new Field("subject", rs.getString("subject"), Field.Store.YES, Field.Index.TOKENIZED));
        doc.add(new Field("content", rs.getString("content"), Field.Store.YES, Field.Index.TOKENIZED));
        writer.addDocument(doc);
      }
      writer.optimize();// 优化
      writer.close();// 一定要关闭,否则不能把内存中的数据写到文件
      Date end = new Date();
      System.out.println("重建索引成功!!!!" + "用时" + (end.getTime() - start.getTime()) + "毫秒");
    } catch (IOException e) {
      System.out.println(e);
    } catch (SQLException e) {
      System.out.println(e);
    }
  }

  public void IndexSingle(long id, String subject, String content) {// 通过结果集就可以获得数据源了
    synchronized (indexPath) {
      try {
        IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), false);
        Date start = new Date();
        Document doc = new Document();// 一个文档相当与表的一条记录
        doc.add(new Field("id", Long.toString(id), Field.Store.YES, Field.Index.UN_TOKENIZED));// 字段id放的是数据库表中的id,lucene的一条记录的一个字段下的数据可以放多个值,这点与数据库表不同
        doc.add(new Field("subject", subject, Field.Store.YES, Field.Index.TOKENIZED));
        doc.add(new Field("content", content, Field.Store.YES, Field.Index.TOKENIZED));
        writer.addDocument(doc);
//        writer.optimize();// 优化
        writer.close();// 一定要关闭,否则不能把内存中的数据写到文件
        Date end = new Date();
        System.out.println("索引建立成功!!!!" + "用时" + (end.getTime() - start.getTime()) + "毫秒");
      } catch (IOException e) {
        System.out.println(e);
      }
    }
  }

  public Hits seacherWildcard(String queryString) {// 根据关键字搜索
    Hits hits = null;
    try {
      Query query = new WildcardQuery(new Term("subject", "*" + queryString + "*"));// 模糊查询一下
      Searcher searcher = new IndexSearcher(indexPath);
      hits = searcher.search(query);
      for (int i = 0; i < hits.length(); i++) {
        Document doc = hits.doc(i);
      }
      searcher.close();
    } catch (Exception e) {
      System.out.print(e);
    }
    return hits;
  }

  public List seacherIndex(String queryString) {// 根据关键字搜索
    System.out.println(queryString);
    Hits hits = null;
    try {
      IndexSearcher isearcher = new IndexSearcher(indexPath);
      // Parse a simple query that searches for "text":
      MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[]{"subject","content"},getAnalyzer());
//      QueryParser parser = new QueryParser("subject", getAnalyzer());
      Query query = parser.parse(queryString);
      hits = isearcher.search(query);
      List rtn = new ArrayList();
      for (int i = 0; i < hits.length(); i++) {
        Document doc = hits.doc(i);
        rtn.add(Long.parseLong(doc.get("id").trim()));
      }
      isearcher.close();
      return rtn;
    } catch (Exception e) {
      System.out.print(e);
      return null;
    }
  }
}

说明

1 StandardAnalyzer 支持中文,所以不用再找其它的了

2 Index方法里

IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), true); 

最后面的这个true,是重新建立索引。 在第一运行时,必须重新所有所有。

 Field.Index.UN_TOKENIZED 是不进行单词拆分   Field.Index.TOKENIZED 是进行拆分,

3 seacherWildcard 里面可以进行模糊搜索

4 seacherIndex 里面   MultiFieldQueryParser 支持多个字段的联合查询

5 我测试的结果,我的网站的11M的索引(应该算很一般的数据量,6000多个帖子),检索竟然几乎不需要时间?或者10毫秒以内。

6  l.seacherIndex("域名 AND 系统"); AND OR NOT 用在单词之间进行组合查询, + 代表 AND  -代表 NOT

7 每次有新的数据时,比如发言或者回复则调用

public void IndexSingle(long id, String subject, String content)

进行索引更新,注意里面的参数是false;

IndexWriter writer = new IndexWriter(indexPath, getAnalyzer(), false);

就这些,第一次使用,效果很好。

抱歉!评论已关闭.