Lucene是一个高性能、可伸缩的信息搜索(IR)库,它并不是一个现成的产品。很多刚接触Lucene的人常把它误解为一个现成的程序,类似文件搜索程序或网站的搜索引擎。其实Lucene只是一个软件库,一个开发工具包,它本身只关心文本的索引和搜索。你可以把Lucene认为是一个层,把复杂的索引和搜索实现隐藏在一组简单易用的API之后。
Lucene使你可以为你的应用程序添加索引和搜索能力。Lucene是用Java实现的成熟的、免费的开源项目,是著名的Apache Jakarta项目之一,并且基于Apache软件许可【ASF,License】。Lucene是当前非常流行的免费的Java信息搜索(IR)库,提供了一组简单却足够强大的核心API,你只须学习它的几个类就可以把Lucene集成到你的应用程序中。因为Lucene是一个Java库,它并不限定要索引和搜索的内容,这使得它比其它一些搜索程序更具有优势。
全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找结果反馈给用户的检索方式。
Lucene能做什么
Lucene可以对任何能转换成文本格式的数据进行索引并使得能够被搜索。Lucene并不关心数据的来源、格式等,只要你能将它转换为文本。这就意味着你可经索引并搜索存放于一些文件中的数据,比如文本文件,web网页,word文档或pdf等,理论上只要能提取出文本信息的就可以。
同样,Lucene可以索引存放在数据库中的数据,提供给用户很多数据库没有的全文搜索能力。一旦集成了Lucene,你的应用程序就可以像google、百度那样来进行复杂的搜索。
Lucene的优点
一、索引文件格式独立于应用平台。Lucene定义了一套以8位字节为基础的索引文件格式,使得各类系统或者不同平台的应用能够共享建立的索引文件。
二、在传统全文检索引擎的倒排索引的基础上,实现了分块索引,能够针对新的文件建立小文件索引,提升索引速度。然后通过与原有索引的合并,达到优化的目的。
三、优秀的面向对象的系统架构,使得对于Lucene扩展的学习难度降低,方便扩充新功能。
四、设计了独立于语言和文件格式的文本分析接口,索引器通过接受Token流完成索引文件的创立,用户扩展新的语言和文件格式,只需要实现文本分析的接口。
五、己经默认实现了一套强大的查询引擎,用户无需自己编写代码就可使系统获得强大的查询能力,Lucene的查询实现中默认实现了布尔操作、模糊查询(Fuzzy Search)、分组查询等等。
索引库结构——倒排序索引
我们需要对文档进行预处理,建立一种便于检索的数据结构,以此来提高信息检索的速度,这种数据结构就是索引。目前广泛使用的一种索引方式是倒排序索引。(这里只是用于说明倒排序索引的结构,最终的索引结构要复杂的多(要考虑更多、更复杂的情况)。例如还要存储关键词在文本中的编号位置,或是首字母的字符位置等信息。)倒排序索引的原理就如同查字典。要先查目录,得到数据对应的页码,在直接翻到指定的页码。不是在文章中找词,而是从目录中找词所在的文章。把数据转成指定格式放到索引库中的操作叫做建立索引。建立索引时,在把数据存到索引库后,再更新词汇表。进行搜索时,先从检索词汇表开始,然后找到相对应的文档。如果查询中仅包含一个关键词,则在词汇表中找到该单词,并取出他对应的文档就可了。如果查询中包含多个关键词,则需要将各个单词检索出的记录进行合并再取出相应的文档。
Nutch
Nutch是一个建立在Lucene核心之上的web搜索实现,它是一个真正的应用程序。也就是说,你可以直接下载使用。它在Lucene的基础上加入了网络爬虫和一些web相关的东西。其目的就是想从一个简单的站内索引和搜索推广到全球网络的搜索土,就像google和百度一样。“我们”已经测试过100M的网页,并且它的设计用在超过1B的网页土应该没有问题。当然,让它运行在一台机器上搜索一些服务器,也运行的很好。
简单demo
public void execute() throws Exception { //分词器 Analyzer analyzer = new EnglishAnalyzer(Version.LUCENE_46); //将索引放到内存中 Directory directory = new RAMDirectory(); IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_46, analyzer); IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); Document document = new Document(); String content = "Tom lives in HangZhou,I live in HangZhou too!"; document.add(new Field("content", content, TextField.TYPE_STORED)); indexWriter.addDocument(document); Document document1 = new Document(); String content1 = "He lived in ShangHai"; document1.add(new Field("content", content1, TextField.TYPE_STORED)); indexWriter.addDocument(document1); indexWriter.close(); DirectoryReader reader = DirectoryReader.open(directory); int docsCount = reader.numDocs(); System.out.println("共含:" + docsCount + "篇文档"); for (int i = 0; i < docsCount; i++) { System.out.println("文档" + i + ":" + reader.document(i)); } TermsEnum termsEnum = MultiFields.getTerms(reader, "content").iterator(null); System.out.println("关键词\t文章号[出现频率]\t出现位置"); while (termsEnum.next() != null) { String s = termsEnum.term().utf8ToString(); System.out.print(s + "\t"); DocsAndPositionsEnum docsAndPositionsEnum = termsEnum.docsAndPositions(null, null); int i = 0; while (docsAndPositionsEnum.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { if (i > 0) { System.out.print("\t"); } System.out.print("" + docsAndPositionsEnum.docID() + "[" + docsAndPositionsEnum.freq() + "]\t"); for (int j = 0; j < docsAndPositionsEnum.freq(); j++) { System.out.print("【" + docsAndPositionsEnum.nextPosition() + "】"); } System.out.println(); i++; } } reader.close(); directory.close(); }
输出如下:
关键词 文章号[出现频率] 出现位置 hangzhou 0[2] 【3】【7】 he 1[1] 【0】 i 0[1] 【4】 live 0[2] 【1】【5】 1[1] 【1】 shanghai 1[1] 【3】 tom 0[1] 【0】 too 0[1] 【8】