花了小半天的时间研究了一下Lucene全文检索引擎的使用,看网上的教程动辄十几章着实吓人,想起来N年前学习JDBC的时候买了巨厚的一本专门描写JDBC的书籍,现在想想做数据库编程就那么个套路,其实是很简单的,这个Lucene应该也是一样的,先入了门再关注各个细节的犄角旮旯。
先看项目中要用Lucene的话需要引入哪些jar包,我是用maven自动下载的,依赖包如下:
<dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>4.8.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>4.8.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>4.8.0</version> </dependency> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-highlighter</artifactId> <version>4.8.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency>
注意junit不是必须的,只是便于写单元测试我就没有删除。
如果不会使用maven,也可以去Lucene的官方网站下载完整的压缩包后自己复制jar包使用,我的测试示例代码用到的jar包如下图:
复制需要用的jar包就行,不用乱七八糟啥都复制到项目里面的。
下面是测试代码,我是在c:\txts目录里面放了几个txt文本文件去建立全文索引的,找了几个公司的项目投标方案,主要目的是测试在比较多的文字内容的情况下全文索引的速度及检索正确率。
代码如下:
package com.xxx; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.LongField; import org.apache.lucene.document.StoredField; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 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.search.TopScoreDocCollector; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.QueryScorer; import org.apache.lucene.search.highlight.SimpleHTMLFormatter; import org.apache.lucene.store.Directory; import org.apache.lucene.store.SimpleFSDirectory; import org.apache.lucene.util.Version; import org.junit.Test; import org.wltea.analyzer.lucene.IKAnalyzer; public class TestLucene { // 待索引文件存放路径 private static String source_file_path = "c:\\txts"; // 生成的全文索引文件存储路径 private static String indexDir = "c:\\index_dir"; private static SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 中文分词器IKAnalyzer,从http://code.google.com/p/ik-analyzer/downloads/list 下载最新源码放入项目中使用 private static Analyzer analyzer = new IKAnalyzer(); public void createIndex() { Directory directory = null; try { // 有文件系统或者内存存储方式,这里使用文件系统存储索引数据 directory = new SimpleFSDirectory(new File(indexDir)); // 生成全文索引的配置对象 IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_48,analyzer); // 设置生成全文索引的方式为创建或者追加 config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND); // 创建真正生成全文索引的writer对象 IndexWriter indexWriter = new IndexWriter(directory, config); // 读取待索引文件夹下的所有的.txt文件 File sourceFiles = new File(source_file_path); String txtFiles[] = sourceFiles.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.endsWith(".txt")) { // 这里只对.txt文件创建索引 return true; } return false; } }); // 遍历所有txt文件写入全文索引,如果数据来源是数据库则遍历数据库查询结果集即可 for (String txtFile : txtFiles) { String file = source_file_path + File.separator + txtFile; File input_txt_file = new File(file); System.out.println("开始对" + txtFile + "建立索引"); Document doc = new Document(); // 约等于数据库的一行记录 // 以下生成各个字段的数据值 StringField name = new StringField("filename", input_txt_file.getName(), Store.YES); TextField content = new TextField("content", readFileContent( file, "gbk"), Store.YES); StringField path = new StringField("path",input_txt_file.getAbsolutePath(), Store.YES); StoredField date = new StoredField("date", df.format(new Date())); LongField size = new LongField("size", input_txt_file.length(),Store.YES); // 向Document对象中加入各个字段的值 doc.add(name); doc.add(content); doc.add(path); doc.add(date); doc.add(size); // 向IndexWriter中增加新的一行记录 indexWriter.addDocument(doc); // 提交数据内容 indexWriter.commit(); } indexWriter.close(); directory.close(); } catch (Exception e) { e.printStackTrace(); } } public void search() { String queryString = "test"; //查询条件 String query_fields[]=new String[]{"filename","content"};//对哪几个字段进行查询检索 File file_index_dir = new File(indexDir); try { Directory directory = new SimpleFSDirectory(file_index_dir); IndexReader indexReader = DirectoryReader.open(directory); // 创建搜索类 IndexSearcher indexSearcher = new IndexSearcher(indexReader); QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_48, query_fields,analyzer); queryParser.setDefaultOperator(QueryParser.OR_OPERATOR);//多个关键字时采取 or 操作 Query query = queryParser.parse(queryString); TopDocs topDocs = indexSearcher.search(query, 10000);//查询前多少条满足条件的数据 System.out.println("一共查到:" + topDocs.totalHits + "记录"); ScoreDoc[] scoreDoc = topDocs.scoreDocs; for (int i = 0; i < scoreDoc.length; i++) { // 内部编号 int doc = scoreDoc[i].doc; System.out.println("内部编号:" + doc); // 根据文档id找到文档 Document mydoc = indexSearcher.doc(doc); System.out.println("文档路径:" + mydoc.get("path")); //读取path字段的值 } } catch (Exception e) { e.printStackTrace(); } } @Test public void searchPage() { int pageIndex=1; int pageSize=1; int start = (pageIndex - 1) * pageSize; String queryString = "test"; //查询条件 String query_fields[]=new String[]{"filename","content"};//对哪几个字段进行查询检索 File file_index_dir = new File(indexDir); try { Directory directory = new SimpleFSDirectory(file_index_dir); IndexReader indexReader = DirectoryReader.open(directory); // 创建搜索类 IndexSearcher indexSearcher = new IndexSearcher(indexReader); QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_48, query_fields,analyzer); queryParser.setDefaultOperator(QueryParser.OR_OPERATOR);//多个关键字时采取 or 操作 Query query = queryParser.parse(queryString); int max_result_size = start + pageSize; TopScoreDocCollector topDocs = TopScoreDocCollector.create(max_result_size, false); indexSearcher.search(query, topDocs); int rowCount = topDocs.getTotalHits(); //满足条件的总记录数 int pages = (rowCount - 1) / pageSize + 1; //计算总页数 TopDocs tds = topDocs.topDocs(start, pageSize); ScoreDoc[] scoreDoc = tds.scoreDocs; System.out.println("一共查到:" + topDocs.getTotalHits() + "记录,共"+pages+"页"); // 关键字高亮显示的html标签,需要导入lucene-highlighter-x.x.x.jar SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter( "<font color='red'>", "</font>"); Highlighter highlighter = new Highlighter(simpleHTMLFormatter,new QueryScorer(query)); for (int i = 0; i < scoreDoc.length; i++) { // 内部编号 int doc_id = scoreDoc[i].doc; System.out.println("内部编号:" + doc_id); // 根据文档id找到文档 Document mydoc = indexSearcher.doc(doc_id); // 内容增加高亮显示 TokenStream tokenStream2 = analyzer.tokenStream("content",new StringReader(mydoc.get("content"))); String content = highlighter.getBestFragment(tokenStream2,mydoc.get("content")); System.out.println("文件名称:"+mydoc.get("filename")+" 高亮内容:" + content); } } catch (Exception e) { e.printStackTrace(); } } public static String readFileContent(String FileName, String charset) { InputStream fis=null; BufferedReader reader=null; StringBuffer result = new StringBuffer(); try { fis=new FileInputStream(FileName); reader = new BufferedReader(new InputStreamReader(fis, charset)); String line = new String(); while ((line = reader.readLine()) != null) { result.append(line); } } catch (Exception e) { e.printStackTrace(); }finally{ if (null!=reader){ try { reader.close(); } catch (IOException e) {} } if (null!=fis){ try { fis.close(); } catch (IOException e) {} } } //System.out.println("内容:" + result.toString()); return result.toString(); } }
里面除了Lucene以外还用到了中文分词器IKAnalyzer,下载网址http://code.google.com/p/ik-analyzer/downloads/list,下载源码包IK Analyzer 2012FF_hf1_source.rar使用,下载后把src里面的文件复制到项目里面就可以了。
记不清是谁说过读书是先读薄再读厚了,使用Lucene的读薄就是先确定索引数据是存到磁盘文件系统还是内存,确定好以后去实例化Directory对象,然后通过配置参数对象去实例化IndexWriter去写待索引文档,对比数据库系统的话,待索引文档对象Document类似于一行记录,上面的示例里面是通过读取硬盘内的文本文件去填充的Document对象,如果要对数据库里面的记录建立全文索引的话,Document的数据来源通过jdbc读取数据库内容即可。
查询更简单了,确定好查询什么内容,确定查哪几个字段就可以了。
相关推荐
基于Lucene的全文搜索引擎研究与应用.pdf 详实的介绍Lucene的架构设计分析
lucene4.8学习资料和案例,自己学习整理。整理了各种查询,包括分页、排序等!对100W,500W,1000W三个级别的数据量分别进行了测试!
基于Lucene的全文检索引擎研究与应用
针对已知坐标search一定范围内的其他point,根据分类或名称进行search,源文件为类似"PVID","ELPVID","POINAME" 的UTF-8的csv文件
NULL 博文链接:https://1151461406.iteye.com/blog/2388343
快速有效地索引企业累积的大量的信息资源,是提供高质量检索服务的基础。Lucene 是一个用Java 写的全文索 引引擎工具包,访问索引时间快,支持多用户...Lucene 的索引文件格式,实现了一个基于Lucene 文档检索的应用实例。
lucene全文检索引擎资料包(有项目)lucene全文检索引擎资料包(有项目)lucene全文检索引擎资料包(有项目)
基于lucene 2.4简单的一个索引和搜索实例
Lucene基于Java的全文检索引擎简介Lucene基于Java的全文检索引擎简介
《信息检索》课程大作业3:基于Lucene的全文搜索引擎
建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制,将数据源(比如多篇文章)排序顺序存储的同时,有另外一...从而大大提高了多关键词查询的效率,所以,全文检索问题归结到最后是一个排序问题。
本系统是基于lucene框架开发的全文搜索引擎,能实现txt、doc、pdf、excel、ppt等常见文档的全文检索,可以直接部署到tomcat下即可运行,需要研究lucene或实际应用lucene框架的朋友,欢迎下载。
NULL 博文链接:https://iamyida.iteye.com/blog/2192938
Lucene是apache软件基金会[4] jakarta项目组的一个子项目,是一个开放源代码[5]的全文检索引擎工具包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析...
Lucene实现全文检索
基于Lucene的全文搜索引擎设计.doc
Lucene全文检索框架+Solr搜索引擎(2018版.Java)Lucene实战应用