基于开源搜索引擎的架构设计和J2EE实现(二)

xruidlw 2009-06-23

第四章 基于lucene的索引与搜索

4.1什么是Lucene全文检索

Lucene是Jakarta Apache的开源项目。它是一个用Java写的全文索引引擎工具包,可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能。

4.2 Lucene的原理分析

4.2.1全文检索的实现机制

Lucene的API接口设计的比较通用,输入输出结构都很像数据库的表==>记录==>字段,所以很多传统的应用的文件、数据库等都可以比较方便的映射到Lucene的存储结构和接口中。

总体上看:可以先把Lucene当成一个支持全文索引的数据库系统。

索引数据源:doc(field1,field2...) doc(field1,field2...)

\ indexer /

_____________

| Lucene Index|

--------------

/ searcher 结果输出:Hits(doc(field1,field2) doc(field1...))

Document:一个需要进行索引的“单元”,一个Document由多个字段组成

Field:字段

Hits:查询结果集,由匹配的Document组成

4.2.2 Lucene的索引效率

通常书籍后面常常附关键词索引表(比如:北京:12, 34页,上海:3,77 页……),它能够帮助读者比较快地找到相关内容的页码。而数据库索引能够大大提高查询的速度原理也是一样,想像一下通过书后面的索引查找的速度要比一页一 页地翻内容高多少倍……而索引之所以效率高,另外一个原因是它是排好序的。对于检索系统来说核心是一个排序问题。

由于数据库索引不是为全文索引设计的,因此,使用like "%keyword%"时,数据库索引是不起作用的,在使用like查询时,搜索过程又变成类似于一页页翻书的遍历过程了,所以对于含有模糊查询的数据库 服务来说,LIKE对性能的危害是极大的。如果是需要对多个关键词进行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。所以建立一个高效检索系统的关键是建立一个类似于科技索引一样的反向索引机制,将数据源(比如多篇文章)排序顺序存储的同 时,有另外一个排好序的关键词列表,用于存储关键词==>文章映射关系,利用这样的映射关系索引:[关键词==>出现关键词的文章编号,出现 次数(甚至包括位置:起始偏移量,结束偏移量),出现频率],检索过程就是把模糊查询变成多个可以利用索引的精确查询的逻辑组合的过程。从而大大提高了多 关键词查询的效率,所以,全文检索问题归结到最后是一个排序问题。

由此可以看出模糊查询相对数据库的精确查询是一个非常不确定的问题,这也是大部分数据库对全文检索支持有限的原因。Lucene最核心的特征是通过特殊的索引结构实现了传统数据库不擅长的全文索引机制,并提供了扩展接口,以方便针对不同应用的定制。

可以通过一下表格对比一下数据库的模糊查询:

  Lucene全文索引引擎 数据库

索引 将数据源中的数据都通过全文索引一一建立反向索引 对于LIKE查询来说,数据传统的索引是根本用不上的。数据需要逐个便利记录进行GREP式的模糊匹配,比有索引的搜索速度要有多个数量级的下降。

匹配效果 通过词元(term)进行匹配,通过语言分析接口的实现,可以实现对中文等非英语的支持。 使用:like "%net%" 会把netherlands也匹配出来,

多个关键词的模糊匹配:使用like "%com%net%":就不能匹配词序颠倒的xxx.net..xxx.com

匹配度 有匹配度算法,将匹配程度(相似度)比较高的结果排在前面。 没有匹配程度的控制:比如有记录中net出现5词和出现1次的,结果是一样的。

结果输出 通过特别的算法,将最匹配度最高的头100条结果输出,结果集是缓冲式的小批量读取的。 返回所有的结果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放这些临时结果集。

可定制性 通过不同的语言分析接口实现,可以方便的定制出符合应用需要的索引规则(包括对中文的支持) 没有接口或接口复杂,无法定制

结论 高负载的模糊查询应用,需要负责的模糊查询的规则,索引的资料量比较大 使用率低,模糊匹配规则简单或者需要模糊查询的资料量少

4.2.3 中文切分词机制

对于中文来说,全文索引首先还要解决一个语言分析的问题,对于英文来说,语句中单词之间是天然通过空格分开的,但亚洲语言的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行索引的话,这个词如何切分出来就是一个很大的问题。

首先,肯定不能用单个字符作(si-gram)为索引单元,否则查“上海”时,不能让含有“海上”也匹配。但一句话:“北京天安门”,计算机如何按照中文的语言习惯进行切分呢?“北京 天安门” 还是“北 京 天安门”?让计算机能够按照语言习惯进行切分,往往需要机器有一个比较丰富的词库才能够比较准确的识别出语句中的单词。另外一个解决的办法是采用自动切分算法:将单词按照2元语法(bigram)方式切分出来,比如:"北京天安门" ==> "北京 京天 天安 安门"。这样,在查询的时候,无论是查询"北京" 还是查询"天安门",将查询词组按同样的规则进行切分:"北京","天安安门",多个关键词之间按与"and"的关系组合,同样能够正确地映射到相应的索引中。这种方式对于其他亚洲语言:韩文,日文都是通用的。

基于自动切分的最大优点是没有词表维护成本,实现简单,缺点是索引效率低,但对于中小型应用来说,基于2元语法的切分还是够用的。基于2元切分后的索引一般大小和源文件差不多,而对于英文,索引文件一般只有原文件的30%-40%不同,

自动切分 词表切分

实现 实现非常简单 实现复杂

查询 增加了查询分析的复杂程度, 适于实现比较复杂的查询语法规则

存储效率 索引冗余大,索引几乎和原文一样大 索引效率高,为原文大小的30%左右

维护成本 无词表维护成本 词表维护成本非常高:中日韩等语言需要分别维护。

还需要包括词频统计等内容

适用领域 嵌入式系统:运行环境资源有限

分布式系统:无词表同步问题

多语言环境:无词表维护成本 对查询和存储效率要求高的专业搜索引擎

4.3 Lucene与Spider的结合

首先构造一个Index类用来实现对内容进行索引。

代码分析如下:

package news;   



/**   


* 新闻搜索引擎   


* 计算机99630 沈晨   


* 版本1.0   


*/   



import java.io.IOException;   




import org.apache.lucene.analysis.cn.ChineseAnalyzer;   




import org.apache.lucene.document.Document;   




import org.apache.lucene.document.Field;   




import org.apache.lucene.index.IndexWriter;   




public class Index {   




IndexWriter _writer = null;   




Index() throws Exception {   




_writer = new IndexWriter("c:\\News\\index",   




new ChineseAnalyzer(), true);   



}   



/**   


* 把每条新闻加入索引中   


* @param url 新闻的url   


* @param title 新闻的标题   


* @throws java.lang.Exception   


*/   



void AddNews(String url, String title) throws Exception {   




Document _doc = new Document();   




_doc.add(Field.Text("title", title));   




_doc.add(Field.UnIndexed("url", url));   



_writer.addDocument(_doc);   


}   



/**   


* 优化并且清理资源   


* @throws java.lang.Exception   


*/   



void close() throws Exception {   



_writer.optimize();   


_writer.close();   


}   


}   

然后构造一个HTML解析类,把通过bot程序收集的新闻内容进行索引。

代码分析如下:

package news;   



/**   


* 新闻搜索引擎   


* 计算机99630 沈晨   


* 版本1.0   


*/   



import java.util.Iterator;   




import java.util.Vector;   




import com.heaton.bot.HTMLPage;   




import com.heaton.bot.HTTP;   




import com.heaton.bot.Link;   




public class HTMLParse {   




HTTP _http = null;   




public HTMLParse(HTTP http) {   



_http = http;   


}   



/**   


* 对Web页面进行解析后建立索引   


*/   



public void start() {   




try {   




HTMLPage _page = new HTMLPage(_http);   




_page.open(_http.getURL(), null);   



Vector _links = _page.getLinks();   



Index _index = new Index();   



Iterator _it = _links.iterator();   



int n = 0;   




while (_it.hasNext()) {   



Link _link = (Link) _it.next();   


String _herf = input(_link.getHREF().trim());   


String _title = input(_link.getPrompt().trim());   


_index.AddNews(_herf, _title);   


n++;   


}   



System.out.println("共扫描到" + n + "条新闻");   



_index.close();   


}   



catch (Exception ex) {   



System.out.println(ex);   


}   


}   



/**   


* 解决java中的中文问题   


* @param str 输入的中文   


* @return 经过解码的中文   


*/   



public static String input(String str) {   




String temp = null;   




if (str != null) {   




try {   




temp = new String(str.getBytes("ISO8859_1"));   



}   



catch (Exception e) {   



}   


}   



return temp;   



}   


}   

4.4小节

在进行海量数据搜索时,如果使用单纯的数据库技术,那将是非常痛苦的。速度将是极大的瓶颈。所以本章提出了使用全文搜索引擎Lucene进行索引、搜索。

最后,还结合了具体代码说明了如何把Lucene全文搜索引擎和Spider程序互相集合来实现新闻搜索的功能。

第五章 基于Tomcat的Web服务器

5.1什么是基于Tomcat的Web服务器

Web服务器是在网络中为实现信息发布、资料查询、数据处理等诸多应用搭建基本平台的服务器。Web服务器如何工作:在Web页面处理中大致可分为三个步 骤,第一步,Web浏览器向一个特定的服务器发出Web页面请求;第二步,Web服务器接收到Web页面请求后,寻找所请求的Web页面,并将所请求的 Web页面传送给Web浏览器;第三步,Web服务器接收到所请求的Web页面,并将它显示出来。

Tomcat是一个开放源代码、运行servlet和JSP Web应用软件的基于Java的Web应用软件容器。Tomcat由Apache-Jakarta子项目支持并由来自开放性源代码Java社区的志愿者进行维护。Tomcat Server是根据servlet和JSP规范进行执行的,因此我们就可以说Tomcat Server也实行了Apache-Jakarta规范且比绝大多数商业应用软件服务器要好。

5.2用户接口设计

5.3.1客户端设计

一个良好的查询界面非常重要,例如Googl就以她简洁的查询界面而闻名。我在设计的时候也充分考虑了实用性和简洁性。

5.3.2服务端设计

主要利用JavaTM Servlet技术实现,用户通过GET方法从客户端向服务端提交查询条件,服务端通过Tomcat的Servlet容器接受并分析提交参数,再调用lucene的开发包进行搜索操作。最后把搜索的结果以HTTP消息包的形式发送至客户端,从而完成一次搜索操作。

服务端Servlet程序的结构如下:

实现的关键代码如下:

public void Search(String qc, PrintWriter out) throws Exception {   



// 从索引目录创建索引   




IndexSearcher _searcher = new IndexSearcher("c:\\news\\index");   




// 创建标准分析器   




Analyzer analyzer = new ChineseAnalyzer();   




// 查询条件   



String line = qc;   



// Query是一个抽象类   




Query query = QueryParser.parse(line, "title", analyzer);   




out.println("< html>");   




out.println("< head>< title>搜索结果< /title>< /head>");   




out.println("< body bgcolor=#ffffff>");   




out.println("< center>" +   




"< form action='/NewsServer/results' method='get'>" +   




"< font face='华文中宋' color='#3399FF'>新闻搜索引擎< /font>:" +   




"< input type='text' name='QueryContent' size='20'>" +   




"< input type='submit' name='submit' value='开始搜索'>" +   




"< /form>< /center>"   



);   



out.println("< p>搜索关键字:< font color=red>" + query.toString("title") +   




"< /font>< /p>");   



Hits hits = _searcher.search(query);   



out.println(" 总共找到< font color=red>" + hits.length() +   




"< /font>条新闻< br>");   




final int HITS_PER_PAGE = 10;   




for (int start = 0; start <  hits.length(); start += HITS_PER_PAGE) {   




int end = Math.min(hits.length(), start + HITS_PER_PAGE);   




for (int i = start; i <  end; i++) {   



Document doc = hits.doc(i);   



String url = doc.get("url");   




if (url != null) {   




out.println( (i + 1) + " < a href='" + url + "'>" +   




replace(doc.get("title"), qc) +   




"< /a>< br>");}   




else {   




System.out.println("没有找到!");}   



}}   



out.println("< /body>< /html>");   



_searcher.close();   


};  

5.3在Tomcat上部署项目

Tomcat中的应用程序是一个WAR(Web Archive)文件。WAR是Sun提出的一种Web应用程序格式,与JAR类似,也是许多文件的一个压缩包。这个包中的文件按一定目录结构来组织:通 常其根目录下包含有Html和Jsp文件或者包含这两种文件的目录,另外还会有一个WEB-INF目录,这个目录很重要。通常在WEB-INF目录下有一 个web.xml文件和一个classes目录,web.xml是这个应用的配置文件,而classes目录下则包含编译好的Servlet类和Jsp或 Servlet所依赖的其它类(如JavaBean)。通常这些所依赖的类也可以打包成JAR放到WEB-INF下的lib目录下,当然也可以放到系统的 CLASSPATH中。

在Tomcat中,应用程序的部署很简单,你只需将你的WAR放到Tomcat的webapp目录下,Tomcat会自动检测到这个文件,并将其解压。你 在浏览器中访问这个应用的Jsp时,通常第一次会很慢,因为Tomcat要将Jsp转化为Servlet文件,然后编译。编译以后,访问将会很快。

5.4小节

本章中详细介绍了如何构架基于Tomcat的Web服务器,使得用户通过浏览器进行新闻的搜索,最后还对Tomcat如何部署进行了说明。

第六章 搜索引擎策略

6.1简介

随着信息多元化的增长,千篇一律的给所有用户同一个入口显然已经不能满足特定用户更深入的查询需求。同时,这样的通用搜索引擎在目前的硬件条件下,要及时更新以得到互联网上较全面的信息是不太可能的。针对这种情况,我们需要一个分类细致精确、数据全面深入、更新及时的面向主题的搜索引擎。

由于主题搜索运用了人工分类以及特征提取等智能化策略,因此它比上面提到的前三代的搜索引擎将更加有效和准确,我们将这类完善的主题搜索引擎称为第四代搜索引擎。

6.2面向主题的搜索策略

6.2.1导向词

导向词就是一组关键词,它们会引导搜索器按照一定顺序搜索整个网络,使得搜索引擎可以在最短的时间里面得到最全面的跟某一个主题相关的信息。通过设置导向 词以及它们对应的不同权值,所有标题、作者、正文或超连接文本中含有某一导向词的网页都会被赋予较高的权值,在搜索的时候会优先考虑。搜索器在向主控程序 获得URL的时候也是按照权值由高到低的顺序。反之,搜索器在向主控程序提交新的URL和它的权值的时候,主控程序会按照权值预先排序,以便下一次有序的 发给搜索器。

6.2.2网页评级

在考虑一个网页被另一个网页的引用时候,不是单纯的将被引用网页的Hit Number加一,而是将引用网页的连接数作为权,同时将该引用网页的重要性也考虑进来(看看上面提到的例子,Yahoo!引用的网页显然比个人网站引用的网页重要,因为Yahoo!本身很重要),就可以得到扩展后的网页评分。

最早提出网页评分的计算方法是Google。它们提出了一个“随机冲浪”模型来描述网络用户对网页的访问行为。模型假设如下:

1) 用户随机的选择一个网页作为上网的起始网页;

2) 看完这个网页后,从该网页内所含的超链内随机的选择一个页面继续进行浏览;

3) 沿着超链前进了一定数目的网页后,用户对这个主题感到厌倦,重新随机选择一个网页进行浏览,并重复2和3。

按照以上的用户行为模型,每个网页可能被访问到的次数就是该网页的链接权值。如何计算这个权值呢?PageRank采用以下公式进行计算:

其中Wj代表第j个网页的权值;lij只取0、1值,代表从网页i到网页j是否存在链接;ni代表网页i有多少个链向其它网页的链接;d代表“随机冲浪” 中沿着链接访问网页的平均次数。选择合适的数值,递归的使用以上公式,即可得到理想的网页链接权值。该方法能够大幅度的提高简单检索返回结果的质量,同时 能够有效的防止网页编写者对搜索引擎的欺骗。因此可以将其广泛的应用在检索器提供给用户的网页排序上,对于网页评分越高的网页,就排的越前。

6.2.3权威网页和中心网页

权威网页

顾名思义,是给定主题底下的一系列重要的权威的网页。其重要性和权威性主要体现在以下两点:

1) 从单个网页来看,它的网页内容本身对于这个给定主题来说是重要的;

2) 从这个网页在整个互联网重的地位来看,这个网页是被其他网页承认为权威的,这主要体现在跟这个主题相关的很多网页都有链接指向这个网页。

由此可见,权威网页对于主题搜索引擎的实现有很重大的意义。主题搜索引擎一个很关键的任务就是从互联网上无数的网页之中最快最准的找出这些可数的权威网页,并为他们建立索引。这也是有效区别主题搜索引擎和前三代传统通用搜索引擎的重要特征。

中心网页

是包含很多指向权威网页的超链接的网页。最典型中心网页的一个例子是Yahoo!,它的目录结构指向了很多主题的权威网页,使得它兼任了很多主题的中心网页。由中心网页出发,轻而易举的就会到达大量的权威网页。因此,它对于主题搜索引擎的实现也起了很大的意义。

权威网页和中心网页之间是一种互相促进的关系:一个好的中心网页必然要有超链接指向多个权威网页;一个好的权威网页反过来也必然被多个中心网页所链接。

6.3小节

本章介绍了面向主题的搜索策略,并作了详细阐述。虽然在新闻搜索中并没有应用到搜索策略,但是对于WWW搜索引擎来说,搜索策略是极其重要的。他直接关系到搜索的质量以及匹配度等性能。

  1. 学习J2EE必须掌握的36个Package
  2. solaris上开发J2EE应用中文问题的解决
  3. J2EE框架的种类一览
  4. 基于开源搜索引擎的架构设计和J2EE实现(一)
  5. J2EE架构的6个最佳实践


为帮助企业应对各种性能困扰,提升IT架构性能,Riverbed提供了最全面的平台,确保理想的应用性能,持续的数据可用性,并主动监测和解决性能问题。Riverbed助力混合型企业将应用性能转化为竞争优势,最大化员工生产率,借助IT创造新型运维灵活性。

相关推荐