精华帖 (0) :: 良好帖 (13) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-03-15
最后修改:2011-03-17
全文分两部分: 一:Lucene简介 Lucene版本:3.0.2 全文检索大体分两个部分:索引创建(Indexing)和搜索索引(Search) 1. 索引过程: 1) 有一系列被索引文件(此处所指即数据库数据) 2) 被索引文件经过语法分析和语言处理形成一系列词(Term)。 3) 经过索引创建形成词典和反向索引表。 4) 通过索引存储将索引写入硬盘。 2. 搜索过程: a) 用户输入查询语句。 b) 对查询语句经过语法分析和语言分析得到一系列词(Term)。 c) 通过语法分析得到一个查询树。 d) 通过索引存储将索引读入到内存。 e) 利用查询树搜索索引,从而得到每个词(Term)的文档链表,对文档链表进行交,差,并得到结果文档。 f) 将搜索到的结果文档对查询的相关性进行排序。 g) 返回查询结果给用户。
• 索引过程如下: ◦ 创建一个IndexWriter用来写索引文件,它有几个参数,INDEX_DIR就是索引文件所存放的位置,Analyzer便是用来对文档进行词法分析和语言处理的。 ◦ 创建一个Document代表我们要索引的文档。 ◦ 将不同的Field加入到文档中。我们知道,一篇文档有多种信息,如题目,作者,修改时间,内容等。不同类型的信息用不同的Field来表示,在本例子中,一共有两类信息进行了索引,一个是文件路径,一个是文件内容。其中FileReader的SRC_FILE就表示要索引的源文件。 ◦ IndexWriter调用函数addDocument将索引写到索引文件夹中。 • 搜索过程如下: ◦ IndexReader将磁盘上的索引信息读入到内存,INDEX_DIR就是索引文件存放的位置。 ◦ 创建IndexSearcher准备进行搜索。 ◦ 创建Analyer用来对查询语句进行词法分析和语言处理。 ◦ 创建QueryParser用来对查询语句进行语法分析。 ◦ QueryParser调用parser进行语法分析,形成查询语法树,放到Query中。 ◦ IndexSearcher调用search对查询语法树Query进行搜索,得到结果TopScoreDocCollector。 二:代码示例(本文重点部分) 1) 首先是连接数据库的jdbc配置文件信息以及存放索引文件的路径配置信息 jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://192.168.0.1/dbname?autoReconnect=true&characterEncoding=utf8 jdbc.username = root jdbc.password = password jdbc.maxIdle = 2 jdbc.maxActive = 4 jdbc.maxWait = 5000 jdbc.validationQuery = select 0 res.index.indexPath = D\:\\apache-tomcat-6.0.18\\webapps\\test\\testHome\\search\\res\\index1 res.index.mainDirectory = D\:\\apache-tomcat-6.0.18\\webapps\\test\\testHome\\search\\res 2) 读取资源文件的工具类: package com.test.common; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * PropertiesUtil.java * @version 1.0 * @createTime 读取配置文件信息类 */ public class PropertiesUtil { private static String defaultPropertyFilePath = "/test.properties"; private static Map<String,Properties> ppsMap = new HashMap<String,Properties>(); /** * 读取默认文件的配置信息,读key返回value * @param key * @return value */ public static final String getPropertyValue(String key) { Properties pps = getPropertyFile(defaultPropertyFilePath); return pps == null ? null : pps.getProperty(key); } /** * 传入filePath读取指定property文件,读key返回value * @param propertyFilePath * @param key * @return value */ public static String getPropertyValue(String propertyFilePath,String key) { if(propertyFilePath == null) { propertyFilePath = defaultPropertyFilePath; } Properties pps = getPropertyFile(propertyFilePath); return pps == null ? null : pps.getProperty(key); } /** * 根据path返回property文件,并保存到HashMap中,提高效率 * @param propertyFilePath * @return */ public static Properties getPropertyFile(String propertyFilePath) { if(propertyFilePath == null) { return null; } Properties pps = ppsMap.get(propertyFilePath); if(pps == null) { InputStream in = PropertiesUtil.class.getResourceAsStream(propertyFilePath); pps = new Properties(); try { pps.load(in); } catch (IOException e) { e.printStackTrace(); } ppsMap.put(propertyFilePath, pps); } return pps; } } 3) Jdbc连接数据库获取Connection工具类,不做分析,直接上代码 package com.test.common; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; /** * JdbcUtil.java * @version 1.0 * @createTime JDBC获取Connection工具类 */ public class JdbcUtil { private static Connection conn = null; private static final String URL; private static final String JDBC_DRIVER; private static final String USER_NAME; private static final String PASSWORD; static { URL = PropertiesUtil.getPropertyValue("jdbc.url"); JDBC_DRIVER = PropertiesUtil.getPropertyValue("jdbc.driverClassName"); USER_NAME = PropertiesUtil.getPropertyValue("jdbc.username"); PASSWORD = PropertiesUtil.getPropertyValue("jdbc.password"); } public static Connection getConnection() { try { Class.forName(JDBC_DRIVER); conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } return conn; } } 4) 万事具备,只欠东风了,下面是核心部分,方法都有注释,不多说了,一步步来,我想肯定是没有问题的。代码如下: package com.test.lucene.logic; import java.io.File; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.Field.TermVector; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Searcher; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.util.Version; import org.wltea.analyzer.lucene.IKAnalyzer; import org.wltea.analyzer.lucene.IKSimilarity; import com.test.common.JdbcUtil; import com.test.common.PropertiesUtil; import com.test.lucene.model.SearchBean; /** * SearchLogic.java * @version 1.0 * @createTime Lucene数据库检索 */ public class SearchLogic { private static Connection conn = null; private static Statement stmt = null; private static ResultSet rs = null; private String searchDir = PropertiesUtil.getPropertyValue("res.index.indexPath"); private static File indexFile = null; private static Searcher searcher = null; private static Analyzer analyzer = null; /** 索引页面缓冲 */ private int maxBufferedDocs = 500; /** * 获取数据库数据 * @return ResultSet * @throws Exception */ public List<SearchBean> getResult(String queryStr) throws Exception { List<SearchBean> result = null; conn = JdbcUtil.getConnection(); if(conn == null) { throw new Exception("数据库连接失败!"); } String sql = "select articleid,title_en,title_cn,abstract_en,abstract_cn from p2p_jour_article"; try { stmt = conn.createStatement(); rs = stmt.executeQuery(sql); this.createIndex(rs); //给数据库创建索引,此处执行一次,不要每次运行都创建索引,以后数据有更新可以后台调用更新索引 TopDocs topDocs = this.search(queryStr); ScoreDoc[] scoreDocs = topDocs.scoreDocs; result = this.addHits2List(scoreDocs); } catch(Exception e) { e.printStackTrace(); throw new Exception("数据库查询sql出错! sql : " + sql); } finally { if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(conn != null) conn.close(); } return result; } /** * 为数据库检索数据创建索引 * @param rs * @throws Exception */ private void createIndex(ResultSet rs) throws Exception { Directory directory = null; IndexWriter indexWriter = null; try { indexFile = new File(searchDir); if(!indexFile.exists()) { indexFile.mkdir(); } directory = FSDirectory.open(indexFile); analyzer = new IKAnalyzer(); indexWriter = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED); indexWriter.setMaxBufferedDocs(maxBufferedDocs); Document doc = null; while(rs.next()) { doc = new Document(); Field articleid = new Field("articleid", String.valueOf(rs .getInt("articleid")), Field.Store.YES, Field.Index.NOT_ANALYZED, TermVector.NO); Field abstract_cn = new Field("abstract_cn", rs .getString("abstract_cn") == null ? "" : rs .getString("abstract_cn"), Field.Store.YES, Field.Index.ANALYZED, TermVector.NO); doc.add(articleid); doc.add(abstract_cn); indexWriter.addDocument(doc); } indexWriter.optimize(); indexWriter.close(); } catch(Exception e) { e.printStackTrace(); } } /** * 搜索索引 * @param queryStr * @return * @throws Exception */ private TopDocs search(String queryStr) throws Exception { if(searcher == null) { indexFile = new File(searchDir); searcher = new IndexSearcher(FSDirectory.open(indexFile)); } searcher.setSimilarity(new IKSimilarity()); QueryParser parser = new QueryParser(Version.LUCENE_30,"abstract_cn",new IKAnalyzer()); Query query = parser.parse(queryStr); TopDocs topDocs = searcher.search(query, searcher.maxDoc()); return topDocs; } /** * 返回结果并添加到List中 * @param scoreDocs * @return * @throws Exception */ private List<SearchBean> addHits2List(ScoreDoc[] scoreDocs ) throws Exception { List<SearchBean> listBean = new ArrayList<SearchBean>(); SearchBean bean = null; for(int i=0 ; i<scoreDocs.length; i++) { int docId = scoreDocs[i].doc; Document doc = searcher.doc(docId); bean = new SearchBean(); bean.setArticleid(doc.get("articleid")); bean.setAbstract_cn(doc.get("abstract_cn")); listBean.add(bean); } return listBean; } public static void main(String[] args) { SearchLogic logic = new SearchLogic(); try { Long startTime = System.currentTimeMillis(); List<SearchBean> result = logic.getResult("急性 肺 潮气量 临床试验"); int i = 0; for(SearchBean bean : result) { if(i == 10) break; System.out.println("bean.name " + bean.getClass().getName() + " : bean.articleid " + bean.getArticleid() + " : bean.abstract_cn " + bean.getAbstract_cn()); i++; } System.out.println("searchBean.result.size : " + result.size()); Long endTime = System.currentTimeMillis(); System.out.println("查询所花费的时间为:" + (endTime-startTime)/1000); } catch (Exception e) { e.printStackTrace(); System.out.println(e.getMessage()); } } } 5) 第四步用到了一个SearchBean,其实这就是一个javabean文件,包含两个String类型(articleid和abstract_cn)的set、get方法,自己回去补上吧。 顺便说下数据库表结构: tablename随便取,但要跟上面查询中的表对应 表字段:articleid int 类型 abstract_cn varchar类型 对表字段没有太严格的定义,本来就是用来测试的。 6) 最后给大家提供所使用到的jar包列表: IKAnalyzer3.2.5Stable.jar(一位好心人建议我升级一下jar包,呵呵,最新的jar包名为:IKAnalyzer3.2.8.jar ,谢谢了!) lucene-analyzers-3.0.2 lucene-core-3.0.2 lucene-highlighter-3.0.2 lucene-memory-3.0.2 lucene-queries-3.0.2 mysql-connector-java-5.0.8-bin 好了,有兴趣的人可以试试吧,代码都经过我测试过了,虽然有些地方代码结构不是特别完整,但只是入门用而已。先有了一个成功能够运行的例子之后才有更多的心情去接着学下去,~_~ 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-03-15
这个里面好像有一个自带的词库吧?有没有看过能不能用别的词库替换啊
|
|
返回顶楼 | |
发表时间:2011-03-16
楼主是正在学习吗?正好我也想学学搜索,你能多更新点然后我跟着学。 。
|
|
返回顶楼 | |
发表时间:2011-03-16
虽然是入门,但还是不错的,另外jdbc的操作 用apache的dbutils,本人觉得 很爽
|
|
返回顶楼 | |
发表时间:2011-03-16
想问几个问题
(1) 如何区相同数据库中的不同的表,简历索引库的时候,可不可以把根据不同的表,创建不同的索引库,查询的时候,可不可以根据不同的需求查询不同的索引库 (2)不同数据库情况下存放不同的表,需求同上 |
|
返回顶楼 | |
发表时间:2011-03-16
最后修改:2011-03-16
|
|
返回顶楼 | |
发表时间:2011-03-17
大概意思: 取DB数据 -----> Lucene建索引 ------>Lucene检索 整个流程Lucene跟DB没半毛钱关系,更谈不上检索数据库了,跟正常的Lucene应用有甚区别。 没找到“Lucene检索数据库”的核心内容 |
|
返回顶楼 | |
发表时间:2011-03-17
Foxswily 写道 大概意思: 取DB数据 -----> Lucene建索引 ------>Lucene检索 整个流程Lucene跟DB没半毛钱关系,更谈不上检索数据库了,跟正常的Lucene应用有甚区别。 没找到“Lucene检索数据库”的核心内容 不知道 兄弟说的 “Lucene检索数据库”的核心内容 具体指什么? |
|
返回顶楼 | |
发表时间:2011-03-17
晨星★~雨泪 写道 Foxswily 写道 大概意思: 取DB数据 -----> Lucene建索引 ------>Lucene检索 整个流程Lucene跟DB没半毛钱关系,更谈不上检索数据库了,跟正常的Lucene应用有甚区别。 没找到“Lucene检索数据库”的核心内容 不知道 兄弟说的 “Lucene检索数据库”的核心内容 具体指什么? 乍看“Lucene检索数据库”这标题,第一感觉是Lucene和DB融合,尤其是index融合,有nosql倾向。实际内容相去甚远呐,DB检索跟Lucene没什么联系,名不副实。 |
|
返回顶楼 | |
发表时间:2011-03-17
程序新手 写道 想问几个问题
(1) 如何区相同数据库中的不同的表,简历索引库的时候,可不可以把根据不同的表,创建不同的索引库,查询的时候,可不可以根据不同的需求查询不同的索引库 (2)不同数据库情况下存放不同的表,需求同上 1)Lucene创建索引是由用户自己定义存储索引路径,所以不同的表数据可以分别创建不同的索引文件以区分,自然在查询的时候便可逻辑控制查询哪些数据。 2)只要数据能查出来,就可以满足需求。 Foxswily 写道 晨星★~雨泪 写道 Foxswily 写道 大概意思: 取DB数据 -----> Lucene建索引 ------>Lucene检索 整个流程Lucene跟DB没半毛钱关系,更谈不上检索数据库了,跟正常的Lucene应用有甚区别。 没找到“Lucene检索数据库”的核心内容 不知道 兄弟说的 “Lucene检索数据库”的核心内容 具体指什么? 乍看“Lucene检索数据库”这标题,第一感觉是Lucene和DB融合,尤其是index融合,有nosql倾向。实际内容相去甚远呐,DB检索跟Lucene没什么联系,名不副实。 标题很清晰的写着“入门篇”,index融合问题不在本文范围之内。 Lucene检索数据库的含义指的是用户界面输入关键字,从数据库中的表数据进行检索相关包含用户输入关键字的数据,并返回给用户,该检索过程包含两步 1.将相关的表数据根据一定的条件过滤后生产结果集,然后由Lucene创建索引,生成索引文件 2.根据用户提供的关键字从索引文件中查询数据,这个返回的数据便是用户需要查询的结果。 我认为很切合题意。 |
|
返回顶楼 | |