2008年5月4日星期日

人肉搜索引擎

实际上,这里所说的人肉搜索引擎就是指更多的利用人工参与来提纯搜索引擎提供的信息的一种机制。猫扑的人肉搜索引擎就是其中一个比较成功的例子。后面我们会根据猫扑的人肉搜索引擎给出一个更加具体的描述。

针对搜索引擎的Link Farm和Spam也许永远不会停止,因为他们能够从他们的作恶中得到利益。我们知道得到利益不是作恶的唯一原因,然而大规模的工业化的作恶唯一的原因当然是那样可以得到利益。

所以,这是一场永远无法结束的战争,只要搜索引擎还是应用最广泛的一种互联网应用,只要搜索引擎还是用户用户找到合适服务的最主要手段,这就是一场永远无法结束的战争。很多人把问题归结于PageRank技术,历史是何其相似啊,当年人们也是这样地抱怨META关键字和描述技术。也许Google或者其他的公司能够研究出来更加伟大的技术(注一)来取代PageRank,但是谁知道这新的技术什么时候又会变成明日黄花。

搜索引擎是利用机器自动化的收集网络信息并有序化的一种技术。搜索引擎高效快速,然而因为现在人工智能并不完善,搜索引擎还很难甄别刻意的修饰过的信息,所以SEO
Spam才会那么猖獗。所以,有些人想从搜索引擎的反面来解决问题,那就是更多的利用人工把搜索引擎得到的信息进一步萃取,进一步有序化,我们称之为人肉搜索引擎。

很多人知道的中国有一个叫做猫扑的论坛,人气非常旺。Tiny在2001年到2004年期间是这个论坛的忠实用户。在我的印象中,人肉搜索引擎的概念应该出自于猫扑。

跟很多论坛一样,猫扑上面也经常有人问这个问题那个问题。同时猫扑有种虚拟货币叫做Mp,问问题的人往往会用Mp来奖励可以帮助他们的人。虽然Mp不能吃不能喝,但是还是很多人醉心于挣取更多的Mp(注二)。那些惯于通过回答问题挣取Mp的人在猫扑一般叫做赏金猎人。

于是很有趣味的一幕出现了,某人需要解决一个问题,就在猫扑发贴并许与一定数量的Mp作为酬谢。很快,就会有赏金猎人看到这个贴子,他们就会去用搜索引擎来寻找问题的答案,然后争先恐后地把找到的答案回在帖子里面邀功。最后,提问题的人得到了答案,赏金猎人得到了Mp,皆大欢喜。这也就形成了所谓的人肉搜索引擎的机制。有趣的是,我们可以发现人肉搜索引擎的搜索流程和搜索引擎在不同服务器之间的分布式搜索流程看起来是那么的相似,区别仅仅在于发动和执行流程的是人,还是机器。

人肉搜索引擎的质量怎样保证呢?这是一个非常有趣的问题,从刚才的流程上大家可以发现,如果有足够多的赏金猎人,他们之间就会产生竞争,寻找资料速度的竞争,资料准确性的竞争。竞争的结果是,一个速度又快资料又准确全面的赏金猎人会有越来越多的Mp,这会给他带来更多的成就感(注三),同时他也会更加的醉心于赏金猎人的工作。甚至,据我所知很多赏金猎人会形成自己的职业道德和行为准则,这样可以给他们更大的自我实现,同时提高了用户的满意程度。这就形成可一个正反馈,一个良性循环,前提是有足够的激励(Mp)投入和足够产生竞争的参与者(赏金猎人)。

同时很多人也为自己是一个人肉搜索引擎的用户感到自豪,因为这确实太好用了,不需要担心分词不准确,不用担心电脑错误的理解了你的话,不用担心搜索引擎收录范围不同造成的风险(注四),甚至不需要学习搜索引擎怎么用(注五),而且答案也不会仅仅局限于搜索引擎能找到的信息,虽然绝大部分问题是通过搜索引擎就可以找到的。

然而,人肉搜索引擎几年来仅仅是一个概念,没有成为一种产品,在猫扑,人肉搜索引擎只能帮助猫扑提升人气,并不能成为一个产品给猫扑带来效益,同时也给更多的人服务。这是为什么呢?我想可能有4点原因:
1、猫扑人肉搜索引擎和论坛的差异不够明显。或者说在猫扑里,人肉搜索引擎只是论坛的一种应用而已,任何需要人肉搜索引擎服务的人必须是论坛的用户,这降低了用户普及度(注六)。
2、Mp过于游戏化。Mp是虚拟货币,是游戏化的货币,这也给猫扑的人肉搜索引擎加上了游戏性的标签。做赏金猎人的人仅仅是喜欢这个游戏化氛围的人,或者说是能被Mp这样的虚拟货币激励的人,这影响了赏金猎人队伍的扩大,从而影响了人肉搜索引擎的扩大。
3、人肉搜索引擎的反映比较慢。Google可以在0.00001秒内给你一个答案,然而人肉搜索引擎需要等待有人看到你的帖子,并找出结果才会有所反应。
4、猫扑人肉搜索引擎不能保留历史纪录,每次有一个问题出现,就会引发一次分布式搜索。即使问题是重复的,也会同样引发这个流程,而且相同的问题在不同的时间问,答案可能迥异(注七),不能对相同答案给予一致性的结果。

或者说,我觉得以上4点如果能够解决,人肉搜索引擎将能帮助我们更快更好的找到有用的信息。
-------------------
注一:按照某些人的猜测,新的搜索引擎页面估值革命性技术也许是利用Google加速器记录的用户访问行为来评估网站的流量,这个方案可以解决PageRank的动态性不足问题。然而这个方案和Alexa何其的类似,一旦出台新一轮的作弊风潮一定出现,可以想象每个商业网站都会要求员工安装Google加速器了,就像他们现在要求员工安装Alexa一样。当然Google的技术实力远非Alexa可比,一切也还仅仅是某些人的猜测。题外话,如果Google真的有一天声称会用Google加速器的访问记录来评估网站,那么Google加速器在中国安装量一定会非常的惊人,看起来这倒可以作为一个不错的推广方法。

注二:这实际上就是Mp机制带来的游戏性造成的,在分析猫扑的时候,Mp是一个关键,它造成了猫扑的游戏性,而猫扑的游戏性应该是它一直以来人气旺盛的主要原因。有人甚至说猫扑就是最好玩的网游,这还是有一定道理的。

注三:我猜测这是Mp机制的动力,也是其他的游戏机制的动力。

注四:传统搜索引擎用户一般情况下只用一种习惯的搜索引擎,但你需要的某资料恰好只被你不用的那种搜索引擎收录的时候,你的习惯就会让你和你需要的资料擦肩而过。然而人肉搜索引擎略有不同,在赏金猎人足够多的情况下,他们各自不同的习惯正好弥补了这种问题带来的风险。

注五:有些信息必须利用高级的搜索引擎技巧才能找到,然而这些技巧很难被最大数量的用户所掌握。这里我们突然发现,人肉搜索引擎的另一个优点,因为它促进了分工,赏金猎人如果想成功一定会比较了解这些高级的搜索引擎技巧的。

注六:作为中国最有人气的论坛,猫扑的用户量也就是十万级的,而搜索引擎的用户量往往是千万级的。

注七:因为时间不同所以前来解决问题的赏金猎人也不同,所以答案也往往不同。

搜索引擎

新华网北京5月2日电(冯晓芳、周丹丹)根据中国互联网数据中心统计表明,中国综合搜索引擎格局正在发生深刻变化:搜索引擎市场第一阵营与第二阵营之间差距正在缩小,第二阵营两大竞争对手谷歌和搜搜不相伯仲。虽然谷歌仍以微弱优势领先,但腾讯搜搜以其较强大的用户群为基础,结合各种有效的网络推广方式,正在有力扩张用户市场份额。

今年第一季度,百度以较为领先的优势占领搜索引擎市场,前3个月的访问率(实际使用了搜索功能与到达其页面的比例)均在85%以上,其中3月份的访问率高达88.7%。

谷歌和搜搜紧随其上,成为市场第二阵营。谷歌在今年第一季度的月度访问率呈现持续上升态势,从1月份的49.3%上升到3月份的54.4%;搜搜从40.8%上升到42.3%,作为腾讯公司推出不久的搜索服务,搜搜的表现值得关注。

雅虎搜索前3个月的平均月度访问率为14.9%和其他搜索引擎网站一起,构成中国搜索引擎市场的第三阵营。

谷歌董事长埃里克・施密特曾表示,谷歌进入中国市场较晚,但正在迎头赶上。在搜索方面,谷歌的服务和质量提高都很快,而且在向领先地位前进。

同为第二阵营的腾讯搜搜,拥有不容小视的潜在竞争实力。2007年,腾讯拥有超过2.3亿QQ即时通讯用户、QQ游戏同时在线人数最高达271万。在腾讯独有的"搜吧"服务中,用户可直接用QQ号登录。这笔资源,正是腾讯搜搜与百度、谷歌正面争夺网络广告市场的本钱。

日前备受关注的微软收购雅虎案日前陷入"拉锯战"。毅联汇业(ICAP)公司经济分析师萨钦・沙阿说,寄望于联手雅虎抗衡谷歌,微软不会"放走"雅虎。2005年,雅虎公司与中国电子商务公司阿里巴巴实现战略性合作,合作双方期望共同打造中国最具优势的互联网搜索平台。从目前的数据看,尽管雅虎搜索在中国的优势并不明显,但以其固有基础,如若微软成功收购雅虎,雅虎搜索极可能会有大的动作以抗衡其固有竞争对手谷歌,这极可能会波及雅虎搜索在中国的业务。

目前,百度仍然是中国搜索引擎市场的行业老大,但对于变化中的互联网市场而言,究竟谁最终能执掌行业牛耳,人们只能拭目以待。

php java 比较

PHP的主要语言开发者之一、Zend公司的创始人之一Andi
Gutmans最近在blog中直言不讳地批评了Java语言。他指出,目前Java厂商试图在JVM上提供动态语言实现的路子根本不对,Java已经输掉了Web开发语言的战争,PHP是事实上的标准和胜利者。

  Gutmans的这篇文章在技术界引发了强烈争议,很对人对此有不同的意见,我在这里就阐述一下我对PHP和Java两种语言的看法。

  我早先曾经在《Perl、PHP、ASP、JSP技术比较》一文中对比过PHP和Java的异同,简而言之,PHP是一种解释执行的脚本语言,语法和C语言类似,易学易用,不懂电脑的非专业人员稍经学习也能使用PHP.而Java要先编译成Class文件,然后在Java虚拟机上执行,Java开发需要熟悉Java语法以及一些核心的架构,从而实现一种可复用的、跨平台的软件,Java比PHP要难学的多。

  实际上,从技术架构来说,Java语言相比PHP有明显的优势,Java使用的是面向对象的系统设计方法,而PHP还是采用面向过程的开发方法。PHP只能实现简单的分布式两层或三层的架构,而JAVA可以实现多层架构。数据库层(持久化层)、应用(业务)逻辑层、表示逻辑层彼此分开,而且现在不同的层都已经有一些成熟的开发框架的支持。例如Struts就是利用Java的web开发技术实现了MVC的设计模式,而在业务逻辑层也有Spring框架,数据库持久化层有Hibernate等框架。这些框架可以方便开发者高效、合理、科学得架构多层的商业应用。从数学运算和数据库访问速度来讲,Java的性能也优于PHP.实际上,对于跨平台的大型的企业应用系统来讲,Java几乎已经成为唯一的选择(微软.NET不支持跨平台),但是在于Web网站应用开发来讲,Java却面临着被PHP边缘化的危险,几乎所有的虚拟主机都支持PHP+MySQL,而支持Java的却少之又少,在资源上,网上有不计其数的PHP资源,很多著名的大型网站(例如Facebook、Mediawiki等)都是基于PHP的,而成功的Java网站却寥寥无几,这又是什么原因呢?

  Java的理念是"一次编写,到处运行",Java在应用框架底下的架构是无与伦比的,远胜过其他任何语言,Java的框架利于大型的协同编程开发,系统易维护、可复用性较好。而PHP很容易自学,让热备能快速简洁地编写代码,适合于快速开发,中小型应用系统,开发成本低。在调试、发布上,PHP也较Java简单。

  理念上的不同导致了Java和PHP在Web应用开发上显示了不同的结果,尽管Java的数学计算和数据库访问都有优势,架构也相当完美,但是PHP却可以简单轻松地支持高强度Web访问,能够快速开发应用,支持PHP的虚拟主机多如牛毛,使得用PHP开发一个网站比用Java开发一个网站要快得多,容易的多。Java所拥有的优点只适合传统的以软件项目为核心的开发模式,而PHP更适合于以客户为核心的SaaS的开发模式,因此,PHP目前在Web网站开发的优势完全是因为Web网站开发的特殊性而导致的,并非编程语言特性所决定。

  因此,PHP在Web网站开发语言的战争中已经慢慢将Java抛在了后面,Java要想在Web网站开发上奋起直追,的确应该多听听Andi
Gutmans的意见,彻底改变一下思路才行,否则两者的差距只能是越来越远。

  另外,虽然在国际上LAMP(Linux+Apache+Mysql+Php)架构已经占领了统治地位,但是在中国国内还有一个怪胎,就是基于微软的Windows
Server+IIS+SqlServer+ASP/ASP.NET的架构,从理论上说,微软的这一套架构也能实现不错的性能,只可惜Windows和SqlServer价格不菲,整个架构在性能上比起LAMP不仅没有什么优势,反而还有不少劣势,因此微软的这一套家伙在国际市场上吃不开。而国内使用微软盗版则可以忽略成本,而ASP的易学易用性不逊于PHP,甚至连IT外行都能够使用,因此在国内也有不小的市场。但是随着中国在保护知识产权方面的日趋完善,免费的LAMP依旧是未来的趋势。

基本搜索引擎优化策略

为什么有的网站能在搜索引擎上排名很好,而有的却连找到找不到呢?这个秘密何在呢?正如免费搜索引擎上的指导方针所说,有五个因素你是必须铭记于心的:

1、 你网站的内容与主题。
2、 每页的关键字数。
3、 关键字放置的位置。
4、 点击量。
5、 链接数量。
1、 搜索引擎优化策略:网站内容

网站的实际内容是你网络优化策略的一个重要的因素。如果你想你的网站能在搜索结果中排得靠前,在你的网站中必须有实际的内容。搜索引擎的蜘蛛基本上是一个瞎子。他们只能对你网页内容进行判断你网站的质量,而不能从图片、flash动画上判断。在所有的页面中有充足的内容给搜索引擎进行索引是一个成功搜索引擎优化策略的基本需要。很容易明白,为什么一个没什么内容的网站很难排上去。人们在查找信息的时候,总是希望找到一个包括很多重要信息的网站。很自然,网页内容丰富的网站要比那些网页内容还那么丰富的网站排名要好得多。每个为他们的网站进行优化的站主牢记。不要忘记更新你的网站。无论是搜索引擎还是访问者都希望看到比较新的信息。这是什么意思呢?这就要求你要收集大量的信息,专注于这领域的变化。

2、搜索引擎优化策略:关键字密度

网页上通常会有数以百计的词语,那么搜索引擎怎样去分辨哪些是描述你的网站的重要的词语呢?搜索引擎会统计你一个页面的字数。那些重复出现的词或短语被认为比较重要些。搜索引擎利用自身的算法来统计你页面中每个字的重要程度。关键字数与该页面字数的比例称为关键字密度,这是一个搜索引擎优化策略最重要的一个因素。

为了得到更好的排名,你的关键字必须在页面中出现若干次,或者在搜索引擎允许的范围内。
怎样才能知道关键字的密度是多少才能得到较好的排名呢?很简单,只要你在搜索引擎中搜索你要优化的关键字,然后统计一下排在前面几个网站该关键字的密度就可以了。当然也有很多统计关键字密度的工具(你可以在本站上找到)。

3、 索引擎优化策略:突出关键字

在有价值的地方放置关键字,当你统计完你的页面需要多少个关键字后,接下来就是考虑把你的关键字放在网页的什么地方。突出关键字是吸引搜索引擎注意的一个最重要的因素。搜索引擎将会专注于你网页中某一部分的内容,处于这一关注部分的词语显得比其他部分的词语要重要得多。这就是所谓的"突出关键字"。

A: Title 和meta 标签:在上一节已经提到,title
标签是网页中最重要的标签。所以在title中放置关键字显得非常重要。有一些搜索引擎会额外的注意"描述"与"关键字"标签。

B: 标题(headings)

2008年5月3日星期六

西藏獒

西藏獒是举世公认的最古老而仅存于世的稀有犬种,在古老的东方有关藏獒神奇的传说已被神话为英勇护主事迹的化身。他不仅漂亮、雄壮、威武、另人*的气势更使的宵小及凶残*动物如狮豹、野狼不寒而栗,而且他忠心护主的天性,不仅是游牧民族的最佳保护犬,同时也被认定是国王、部落首长的最佳护卫犬。
  目前品相最好的上品藏獒,出于西藏的河曲地区。这种藏獒有典型的喜马拉雅山地犬的原始特征:茂密的鬃毛像非洲雄狮一样,前胸宽阔,目光炯炯有神,含蓄而深邃。喜马拉雅山脉的严酷环境赋予了藏獒一种粗犷、剽悍美、刚毅的心理承受能力,同时也赋予藏獒王者的气质,高贵、典雅、沉稳、勇敢。还有一种藏獒出于青海地区。这种藏獒几乎没有鬃毛,身上的毛也比较短,体型却更大!但是它的性格没有带鬃毛的藏獒凶猛、沉稳。
  藏獒的性格和气质与中国古典哲学相吻合:大智若愚、大勇若怯。藏獒的目光很典型:总是微微闭着双眼,眼神之中含有一种蔑视的神态,那种处变不惊的沉稳气度颇具王者风范。举个例子,如果有人敲门,藏獒只是机警地把头朝向门的方向,像往常一样眯着眼睛,一种蔑视的目光等待着事态进一步发展。开门后,如果进来的人非常高声地一边说话一边往里走,藏獒才会用低沉的、浑厚的、具有无穷穿透力的、轻轻的呜呜声,警告来人到别人家里说话声音不要太高!就是这种轻轻的声音,完全可以让那些冒失的人突然变得有礼貌。
  藏獒是出色的护卫犬,几乎不用进行任何训练就具有很好的护卫功能,而且自然就懂得要攻击颈动脉。而且藏獒还具有一副令人望而生畏的身材,它头颈部茂密的鬃毛,使它的头显得很大,陌生人很难不惧怕这种具有原始野性的"狮子"!

2008年4月29日星期二

Lucene 简介

Lucene 简介

Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene
目前是 Apache Jakarta 家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具包。

目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能。Lucene
能够为文本类型的数据建立索引,所以你只要能把你要索引的数据格式转化的文本的,Lucene 就能对你的文档进行索引和搜索。比如你要对一些
HTML 文档,PDF 文档进行索引的话你就首先需要把 HTML 文档和 PDF 文档转化成文本格式的,然后将转化后的内容交给 Lucene
进行索引,然后把创建好的索引文件保存到磁盘或者内存中,最后根据用户输入的查询条件在索引文件上进行查询。不指定要索引的文档的格式也使
Lucene 能够几乎适用于所有的搜索应用程序。

图 1 表示了搜索应用程序和 Lucene 之间的关系,也反映了利用 Lucene 构建搜索应用程序的流程:


图1. 搜索应用程序和 Lucene 之间的关系


回页首


索引和搜索

索引是现代搜索引擎的核心,建立索引的过程就是把源数据处理成非常方便查询的索引文件的过程。为什么索引这么重要呢,试想你现在要在大量的文档中搜索含有某个关键词的文档,那么如果不建立索引的话你就需要把这些文档顺序的读入内存,然后检查这个文章中是不是含有要查找的关键词,这样的话就会耗费非常多的时间,想想搜索引擎可是在毫秒级的时间内查找出要搜索的结果的。这就是由于建立了索引的原因,你可以把索引想象成这样一种数据结构,他能够使你快速的随机访问存储在索引中的关键词,进而找到该关键词所关联的文档。Lucene
采用的是一种称为反向索引(inverted
index)的机制。反向索引就是说我们维护了一个词/短语表,对于这个表中的每个词/短语,都有一个链表描述了有哪些文档包含了这个词/短语。这样在用户输入查询条件的时候,就能非常快的得到搜索结果。我们将在本系列文章的第二部分详细介绍
Lucene 的索引机制,由于 Lucene 提供了简单易用的
API,所以即使读者刚开始对全文本进行索引的机制并不太了解,也可以非常容易的使用 Lucene 对你的文档实现索引。

对文档建立好索引后,就可以在这些索引上面进行搜索了。搜索引擎首先会对搜索的关键词进行解析,然后再在建立好的索引上面进行查找,最终返回和用户输入的关键词相关联的文档。


回页首


Lucene 软件包分析

Lucene 软件包的发布形式是一个 JAR 文件,下面我们分析一下这个 JAR 文件里面的主要的 JAVA 包,使读者对之有个初步的了解。

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 用来保存搜索得到的结果。


回页首


一个简单的搜索应用程序

假设我们的电脑的目录中含有很多文本文档,我们需要查找哪些文档含有某个关键词。为了实现这种功能,我们首先利用 Lucene
对这个目录中的文档建立索引,然后在建立好的索引中搜索我们所要查找的文档。通过这个例子读者会对如何利用 Lucene
构建自己的搜索应用程序有个比较清楚的认识。


回页首


建立索引

为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter,
Analyzer, Directory。下面我们分别介绍一下这五个类的用途:

Document

Document 是用来描述文档的,这里的文档可以指一个 HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document
对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。

Field

Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。

Analyzer

在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer
类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给
IndexWriter 来建立索引。

IndexWriter

IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。

Directory

这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现,第一个是
FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。

熟悉了建立索引所需要的这些类后,我们就开始对某个目录下面的文本文件建立索引了,清单1给出了对某个目录下的文本文件建立索引的源代码。


清单 1. 对文本文件建立索引
package TestLucene;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
import java.util.Date;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
/**
* This class demonstrate the process of creating index with Lucene
* for text files
*/
public class TxtFileIndexer {
public static void main(String[] args) throws Exception{
//indexDir is the directory that hosts Lucene's index files
File indexDir = new File("D:\\luceneIndex");
//dataDir is the directory that hosts the text files that to be indexed
File dataDir = new File("D:\\luceneData");
Analyzer luceneAnalyzer = new StandardAnalyzer();
File[] dataFiles = dataDir.listFiles();
IndexWriter indexWriter = new IndexWriter(indexDir,luceneAnalyzer,true);
long startTime = new Date().getTime();
for(int i = 0; i < dataFiles.length; i++){
if(dataFiles[i].isFile() && dataFiles[i].getName().endsWith(".txt")){
System.out.println("Indexing file " +
dataFiles[i].getCanonicalPath());
Document document = new Document();
Reader txtReader = new FileReader(dataFiles[i]);
document.add(Field.Text("path",dataFiles[i].getCanonicalPath()));
document.add(Field.Text("contents",txtReader));
indexWriter.addDocument(document);
}
}
indexWriter.optimize();
indexWriter.close();
long endTime = new Date().getTime();

System.out.println("It takes " + (endTime - startTime)
+ " milliseconds to create index for the
files in directory "
+ dataDir.getPath());
}
}

在清单1中,我们注意到类 IndexWriter 的构造函数需要三个参数,第一个参数指定了所创建的索引要存放的位置,他可以是一个 File
对象,也可以是一个 FSDirectory 对象或者 RAMDirectory 对象。第二个参数指定了 Analyzer
类的一个实现,也就是指定这个索引是用哪个分词器对文挡内容进行分词。第三个参数是一个布尔型的变量,如果为 true
的话就代表创建一个新的索引,为 false
的话就代表在原来索引的基础上进行操作。接着程序遍历了目录下面的所有文本文档,并为每一个文本文档创建了一个 Document
对象。然后把文本文档的两个属性:路径和内容加入到了两个 Field 对象中,接着在把这两个 Field 对象加入到 Document
对象中,最后把这个文档用 IndexWriter 类的 add
方法加入到索引中去。这样我们便完成了索引的创建。接下来我们进入在建立好的索引上进行搜索的部分。


回页首


搜索文档

利用Lucene进行搜索就像建立索引一样也是非常方便的。在上面一部分中,我们已经为一个目录下的文本文档建立好了索引,现在我们就要在这个索引上进行搜索以找到包含某个关键词或短语的文档。Lucene提供了几个基础的类来完成这个过程,它们分别是呢IndexSearcher,
Term, Query, TermQuery, Hits. 下面我们分别介绍这几个类的功能。

Query

这是一个抽象类,他有多个实现,比如TermQuery, BooleanQuery, PrefixQuery.
这个类的目的是把用户输入的查询字符串封装成Lucene能够识别的Query。

Term

Term是搜索的基本单位,一个Term对象有两个String类型的域组成。生成一个Term对象可以有如下一条语句来完成:Term term
= new Term("fieldName","queryWord");
其中第一个参数代表了要在文档的哪一个Field上进行查找,第二个参数代表了要查询的关键词。

TermQuery

TermQuery是抽象类Query的一个子类,它同时也是Lucene支持的最为基本的一个查询类。生成一个TermQuery对象由如下语句完成:
TermQuery termQuery = new TermQuery(new
Term("fieldName","queryWord")); 它的构造函数只接受一个参数,那就是一个Term对象。

IndexSearcher

IndexSearcher是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个IndexSearcher的实例在一个索引上进行操作。

Hits

Hits是用来保存搜索的结果的。

介绍完这些搜索所必须的类之后,我们就开始在之前所建立的索引上进行搜索了,清单2给出了完成搜索功能所需要的代码。


清单2 :在建立好的索引上进行搜索
package TestLucene;
import java.io.File;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.FSDirectory;
/**
* This class is used to demonstrate the
* process of searching on an existing
* Lucene index
*
*/
public class TxtFileSearcher {
public static void main(String[] args) throws Exception{
String queryStr = "lucene";
//This is the directory that hosts the Lucene index
File indexDir = new File("D:\\luceneIndex");
FSDirectory directory = FSDirectory.getDirectory(indexDir,false);
IndexSearcher searcher = new IndexSearcher(directory);
if(!indexDir.exists()){
System.out.println("The Lucene index is not exist");
return;
}
Term term = new Term("contents",queryStr.toLowerCase());
TermQuery luceneQuery = new TermQuery(term);
Hits hits = searcher.search(luceneQuery);
for(int i = 0; i < hits.length(); i++){
Document document = hits.doc(i);
System.out.println("File: " + document.get("path"));
}
}
}

在清单2中,类IndexSearcher的构造函数接受一个类型为Directory的对象,Directory是一个抽象类,它目前有两个子类:FSDirctory和RAMDirectory.
我们的程序中传入了一个FSDirctory对象作为其参数,代表了一个存储在磁盘上的索引的位置。构造函数执行完成后,代表了这个IndexSearcher以只读的方式打开了一个索引。然后我们程序构造了一个Term对象,通过这个Term对象,我们指定了要在文档的内容中搜索包含关键词"lucene"的文档。接着利用这个Term对象构造出TermQuery对象并把这个TermQuery对象传入到IndexSearcher的search方法中进行查询,返回的结果保存在Hits对象中。最后我们用了一个循环语句把搜索到的文档的路径都打印了出来。好了,我们的搜索应用程序已经开发完毕,怎么样,利用Lucene开发搜索应用程序是不是很简单。

Solr

Solr 是一个可供企业使用的、基于 Lucene 的开箱即用的搜索服务器。对Lucene不熟?那么建议先看看下面两篇文档:

  实战Lucene,第 1 部分: 初识
Lucene:http://www.ibm.com/developerworks/cn/java/j-lo-lucene1/

  用Lucene加速Web搜索应用程序的开发:http://www.ibm.com/developerworks/cn/web/wa-lucene2/

  一、 solr介绍

  solr是基于Lucene
Java搜索库的企业级全文搜索引擎,目前是apache的一个项目。它的官方网址在http://lucene.apache.org/solr/
.solr需要运行在一个servlet 容器里,例如tomcat5.5.solr在lucene的上层提供了一个基于HTTP/XML的Web
Services,我们的应用需要通过这个服务与solr进行交互。

  二、 solr安装和配置

  关于solr的安装和配置,这里也有两篇非常好的文档,作者同时也是 Lucene Java 项目的提交人和发言人:

  使用Apache Solr实现更加灵巧的搜索:http://www.ibm.com/developerworks/cn/java/j-solr1/index.html

  http://www.ibm.com/developerworks/cn/java/j-solr2/index.html

  下面主要说说需要注意的地方。

  Solr的安装非常简单,下载solr的zip包后解压缩将dist目录下的war文件改名为solr.war直接复制到tomcat5.5的webapps目录即可。注意一定要设置solr的主位置。有三种方法。我采用的是在tomcat里配置java:comp/env/solr/home的一个JNDI指向solr的主目录(example目录下),建立/tomcat55/conf/Catalina/localhost/solr.xml文件。

value="D:/solr/solr" override="true" />

  观察这个指定的solr主位置,里面存在两个文件夹:conf和data.其中conf里存放了对solr而言最为重要的两个配置文件schema.xml和solrconfig.xml.data则用于存放索引文件。

  schema.xml主要包括types、fields和其他的一些缺省设置。

  solrconfig.xml用来配置Solr的一些系统属性,例如与索引和查询处理有关的一些常见的配置选项,以及缓存、扩展等等。

  上面的文档对这两个文件有比较详细的说明,非常容易上手。注意到schema.xml里有一个

url

  的配置,这里将url字段作为索引文档的唯一标识符,非常重要。

  三、 加入中文分词

  对全文检索而言,中文分词非常的重要,这里采用了qieqie庖丁分词(非常不错:))。集成非常的容易,我下载的是2.0.4-alpha2版本,其中它支持最多切分和按最大切分。创建自己的一个中文TokenizerFactory继承自solr的BaseTokenizerFactory.

/**

* Created by IntelliJ IDEA.

* User: ronghao

* Date: 2007-11-3

* Time: 14:40:59

* 中文切词 对庖丁切词的封装

*/

public class ChineseTokenizerFactory extends BaseTokenizerFactory {

/**

* 最多切分 默认模式

*/

public static final String MOST_WORDS_MODE = "most-words".

/**

* 按最大切分

*/

public static final String MAX_WORD_LENGTH_MODE = "max-word-length".

private String mode = null.

public void setMode(String mode) {

if (mode==null||MOST_WORDS_MODE.equalsIgnoreCase(mode)

|| "default".equalsIgnoreCase(mode)) {

this.mode=MOST_WORDS_MODE.

} else if (MAX_WORD_LENGTH_MODE.equalsIgnoreCase(mode)) {

this.mode=MAX_WORD_LENGTH_MODE.

}

else {

throw new IllegalArgumentException("不合法的分析器Mode
参数设置:" mode).

}

}

@Override

public void init(Map args) {

super.init(args).

setMode(args.get("mode")).

}

public TokenStream create(Reader input) {

return new PaodingTokenizer(input, PaodingMaker.make(),

createTokenCollector()).

}

private TokenCollector createTokenCollector() {

if( MOST_WORDS_MODE.equals(mode))

return new MostWordsTokenCollector().

if( MAX_WORD_LENGTH_MODE.equals(mode))

return new MaxWordLengthTokenCollector().

throw new Error("never happened").

}

}