【lucence入门系列】初探lucence
什么是lucence
Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写,它是一个全文检索引擎的架构,提供了完整的创建索引和查询索引,以及部分文本分析的引擎,Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎,Lucene在全文检索领域是一个经典的祖先,现在很多检索引擎都是在其基础上创建的,思想是相通的。Lucene是根据关健字来搜索的文本搜索工具,只能在某个网站内部搜索文本内容,不能跨网站搜索。
索引和搜素
索引是现代搜索引擎的核心,建立索引的过程就是把源数据处理成非常方便查询的索引文件的过程。为什么索引这么重要呢,试想你现在要在大量的文档中搜索含有某个关键词的文档,那么如果不建立索引的话你就需要把这些文档顺序的读入内存,然后检查这个文章中是不是含有要查找的关键词,这样的话就会耗费非常多的时间,想想搜索引擎可是在毫秒级的时间内查找出要搜索的结果的。这就是由于建立了索引的原因,你可以把索引想象成这样一种数据结构,他能够使你快速的随机访问存储在索引中的关键词,进而找到该关键词所关联的文档。Lucene 采用的是一种称为反向索引(inverted index)的机制。反向索引就是说我们维护了一个词 / 短语表,对于这个表中的每个词 / 短语,都有一个链表描述了有哪些文档包含了这个词 / 短语。这样在用户输入查询条件的时候,就能非常快的得到搜索结果。
全文检索
将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引。例如:字典。字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果字典没有音节表和部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以一一列举,于是将读音拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到我们的非结构化数据——也即对字的解释。这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search)。
Luncene使用流程
创建索引库
1、创建JavaBean对象
2、创建Docment对象
3、将JavaBean对象所有的属性值,均放到Document对象
4、创建IndexWriter对象
4、将Document对象通过IndexWriter对象写入索引库
5、关闭IndexWriter对象
查询索引库中的内容
1、创建IndexSearcher对象
2、创建QueryParser对象
3、创建Query对象来封装关键字
4、 用IndexSearcher对象去索引库中查询符合条件的前100条记录,不足100条记录的以实际为准
5、获取符合条件的编号
6、用indexSearcher对象去索引库中查询编号对应的Document对象,将Document对象中的所有属性取出,再封装回JavaBean对象中去,并加入到集合中保存
Lucene 软件包分析
Package: org.apache.lucene.document
这个包提供了一些为封装要索引的文档所需要的类,比如 Document, Field。这样,每一个文档最终被封装成了一个 Document 对象。
Package: org.apache.lucene.analysis
这个包主要功能是对文档进行分词,因为文档在建立索引之前必须要进行分词,所以这个包的作用可以看成是为建立索引做准备工作。
Package: org.apache.lucene.index
这个包提供了一些类来协助创建索引以及对创建好的索引进行更新。这里面有两个基础的类:IndexWriter 和 IndexReader,其中 IndexWriter 是用来创建索引并添加文档到索引中的,IndexReader 是用来删除索引中的文档的。
Package: org.apache.lucene.search
这个包提供了对在建立好的索引上进行搜索所需要的类。比如 IndexSearcher 和 Hits, IndexSearcher 定义了在指定的索引上进行搜索的方法,Hits 用来保存搜索得到的结果
快速入门
创建项目,导入相关jar包
public class Article { //编号 private Integer id; //标题 private String title; //内容 private String content; public Article(){} public Article(Integer id, String title, String content) { this.id = id; this.title = title; this.content = content; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "编号:" + id+"\n标题:" + title + "\n内容:" + content; } }
public class FirstApp { /** * 创建索引库 * 将Aritcle对象放入索引库中的原始记录表中,从而形成词汇表 */ @Test public void createIndexDB() throws Exception{ // 创建Article对象 Article article = new Article(1,"培训","传智是一家IT培训机构"); // 创建Document对象 Document document = new Document(); // 将Article对象中的三个属性值分别绑定到Document对象 /* *参数一:document对象中的属性名 *参数二:document对象中的属性值 *参数三:是否将属性值存入由原始记录表中转存入词汇表 * Store.YES表示该属性值会存入词汇表 * Store.NO表示该属性值不会存入词汇表 *参数四:是否将属性值进行分词算法 * Index.ANALYZED表示该属性值会进行词汇拆分 * Index.NOT_ANALYZED表示该属性值不会进行词汇拆分 */ document.add(new Field("xid",article.getId().toString(), Field.Store.YES, Field.Index.ANALYZED)); document.add(new Field("xtitle",article.getTitle(), Field.Store.YES, Field.Index.ANALYZED)); document.add(new Field("xcontent",article.getContent(), Field.Store.YES, Field.Index.ANALYZED)); // 创建IndexWriter字符流对象 /* * 参数一:lucene索引库最终应对于硬盘中的目录,例如:E:/IndexDBDBDB * 参数二:采用什么策略将文本拆分,一个策略就是一个具体的实现类 * 参数三:最多将文本拆分出多少词汇,LIMITED表示1W个,即只取前1W个词汇,如果不足1W个词汇个,以实际为准 */ Directory directory = FSDirectory.open(new File("E:/IndexDBDBDB")); Version version = Version.LUCENE_30; Analyzer analyzer = new StandardAnalyzer(version); IndexWriter.MaxFieldLength maxFieldLength = IndexWriter.MaxFieldLength.LIMITED; IndexWriter indexWriter = new IndexWriter(directory,analyzer,maxFieldLength); // 将document对象写入lucene索引库 indexWriter.addDocument(document); // 关闭IndexWriter字符流对象 indexWriter.close(); } /** * 根据关键字从索引库中搜索符合条件的内容 */ @Test public void findIndexDB() throws Exception{ String keywords = "培"; List<Article> articleList = new ArrayList<>(); Directory directory = FSDirectory.open(new File("E:/IndexDBDBDB")); Version version = Version.LUCENE_30; Analyzer analyzer = new StandardAnalyzer(version); // 创建IndexSearcher字符流对象 IndexSearcher indexSearcher = new IndexSearcher(directory); // 创建查询解析器对象 /* * 参数一:使用分词器的版本,提倡使用该jar包中的最高版本 * 参数二:对document对象中的哪个属性进行搜索 */ QueryParser queryParser = new QueryParser(version,"xcontent",analyzer); // 创建对象,封装查询关键字 Query query = queryParser.parse(keywords); // 根据关键字,去索引库中的词汇表搜索 /* * 参数一:表示封装关键字查询对象,QueryParser表示查询解析器 * 参数二:MAX_RECORD表示如果根据关键字搜索出来的内容较多,只取前MAX_RECORD个内容 * 不足MAX_RECORD个数的话,以实际为准 */ int MAX_RECORD = 100; TopDocs topDocs = indexSearcher.search(query,MAX_RECORD); // 迭代词汇表中符合条件的编号 for(int i=0;i<topDocs.scoreDocs.length;i++){ // 取出封装编号和分数的ScoreDoc对象 ScoreDoc scoreDoc = topDocs.scoreDocs[i]; // 取出每一个编号,例如:0,1,2 int no = scoreDoc.doc; // 根据编号去索引库中的原始记录表中查询对应的document对象 Document document = indexSearcher.doc(no); // 获取document对象中的三个属性值 String xid = document.get("xid"); String xtitle = document.get("xtitle"); String xcontent = document.get("xcontent"); // 封装到artilce对象中 Article article = new Article(Integer.parseInt(xid),xtitle,xcontent); // 将article对象加入到list集合中 articleList.add(article); } // 迭代结果集 for(Article a:articleList){ System.out.println(a); } } }