Coding Poet, Coding Science

Apache的企业搜索工具

企业级的应用相对于普通的软件相比,体现了更多的理性,体现了更多的需求的驱动。对普通的程序员来讲,学习企业级的软件提供了一个从底层的程序员上升到资深架构师的问题。不同层次的程序员,思考问题的方式也不一样。到了架构的层面,更多的时候讨论的是架构的缺点与优点。这个时候,根本不是一味地讨论程序有哪些功能。技术不等于产品,产品不等于市场,市场不等于盈利。但是一般程序员做的,只能是基本的技术。掌握架构师的技能,大概是可以做到高级的技术、产品以及市场。

不多说然它的事情。今天的主要的目的是帮助实现一个全文搜索引擎。全文搜索引擎在企业中的应用可以说是信息检索的一个重要的应用。整个的框架的部分为自然语言处理等奠定了基础。没有人工智能的时候,可以是传统的检索的方式。有人工智能与自然语言处理的时候,可以以插件的形式提供新的功能。但是这个架构是稳定的。

通过查找网络上的资料。比较有名的企业级应用的全文搜索引擎有Apache Solr、Sphinx、Elasticsearch等。其中,除了Sphinx,都是基于Lucene的。Java平台上还有所谓的Hibernate Search。一个技术到达企业级别的应用,一般而言是在编程与布署的层面上做一些更大的问题。比如,Lucene作为信息检索基础库,在编程层面上就可以做,许多软件的帮助文档就是使用Lucene作为索引的。但是到了企业级的应用上,Lucene就只是一个基本的IR(Information Retrival)库了。在Solr上面可能有更多的集成。所以,目前我们把Solr和Lucene的分界面看成是编程与架构的分界线。并以Lucene作为案例分析编程与架构是如何关联的。

本章的计划是,首先介绍Lucene在信息检索中的基础功能与用法,然后讲解Solr等全文搜索引擎在企业搜索中的应用。再然后是讨论一下为什么全文搜索是必要的。以此为机会,了解编程是如何走向商业实践的,掌握怎样的技术才能够把握技术发展趋势。

Lucene软件介绍

Apache Lucene是一个开源的IR软件库,原始版本是由Doug Cutting在Java语言之下写成的。目前以Apache Software License发布。目前Lucene程序已经移植到Delphi、Perl、C#、C++、Python、Ruby与PHP等多种语言。(这种移植可能是按照Lucene的架构在另外的语言中重新设计它,而不是简单提供一个接口那么简单)。

Lucene原始版本写于1999年。当时是一个SourceForge项目。后来加入Apache软件基金会,并于2005年成为Apache的一个顶级的项目。Lucene曾经包括许多的子项目,如Lucene.NET、Mahout、Solr、Nutch。现在Solr已经独立出来,而Mahout、Tika等项目也已经成为独立的顶级项目。4.0版本是2012年至2015年主要的开发版本。而2015年Lucene升级到了5.0版本。

Lucene所满足的应用主要有两个方面。第一个方面是应用程序对于全文检索数据的需求,第二个方面是对于单机或者互联网的个性化的数据集上的搜索的需求。比较典型的应用有,Eclipse的帮助子系统使用Lucene作为全文搜索引擎;Apache的网站使用Lucene作为全文检索的引擎。

Lucene的核心逻辑架色是把一个文档document看成是由fields文本构成的结构。这种结构使用Lucene的API独立于文件格式。因此,从PDF、HTML、Word、Open Document中抽取出来的文本都可以被Lucene所识别。对于许多的格式,只要它们的文本信息能够被抽取出来,Lucene就可以利用它构建全文引擎。

这里,我们对Lucene的功能深入思考一下。首先,音乐、图片、视频等信息基本上与Lucene没有什么关系,因为里面没什么文件信息。其次,Lucene是假设文件信息已经从PDF等文件格式中抽取出来,Lucene项目本身并不设计这样的抽取器。另外,Lucene本身并不包含HTML挖掘与解析的功能。但是有一些上层的库可以弥补。比如Apache Nutch提供了HTML抽取(挖掘)与解析的功能、Apache Solr与Elasticsearch这两个企业级的搜索服务器提供了完整的架构。还有Compass、DocFetcher、Swiftype、Kinosearch、Apache Lucy、Luke等工具也对Lucene做了不同程度的补充。

应当注意的是,信息检索程序虽然与搜索引擎有关,但是两者仍然是不同的概念,不应该混成同一个东西。

Lucene实现的特点

Lucene具有如下的特点:

  1. 索引格式独立于应用平台。Lucene的索引文件格式是内部定义的八字节为基础的格式,不受不同的系统与不同的平台的影响。因此应用程序能够共享建立的索引文件。
  2. 在传统全文检索引擎的倒排索引(不是由记录来确定属性的值,而是由属性值来确定记录的位置。这种索引的索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址,这种方式正好与数据库中的索引相反)的基础上,实现分块索引(针对新的文件建立小文件索引,提升索引速度,然后与原有索引合并)。
  3. 采用面向对象的思想与架构设计,方便扩充新的功能。
  4. 设计了独立于语言与文件格式的分析接口。用户可以通过实现文本分析接口来扩展新的语言和文件格式。
  5. 默认已经有一套强大的查询引擎,实现了布尔操作、模糊查询、分组查询等功能。

面对已经存在的商业的全文检索引擎,Lucene也具有相当的优势。比如开源发行、方便扩展、可以扩展支持中文、扩展支持HTML与PDF等格式。

Lucene的工作的时候采用的是索引检索。因此,可以把任务分割成建立索引与基于索引的查询两个阶段。在建立索引的阶段,做一些指定索引文件位置与索引特性的选项,然后添加字段。在查询索引的阶段,读取索引文件,设置查询格式,然后输出结果。Lucene的操作的对象是很明确的,就是文件系统当中的文件作为索引文件。运行的计算模型是传统的单机模式。

Lucene不是一个看爬虫。如果需要爬虫功能的话,应该参考Nutch;Lucene也不是一个完整的应用,若需要,应该参考PoweredBy;Lucene也不是实现了PageRank等算法的链接分析算法库,若需要后者,应该参考Nutch与Hadoop。它只是一个实现了基于文本的搜索的算法(在其上可以施行PageRank与Arc等链接分析算法)。

Lucene的一个Index是文档的集合Collection;一个文档是Fields的集合;一个Field则由描述文本内容的metadata构成。一个Field里面可以包含多个属性。

Lucene上面的编程任务可以分成两类。第一类是写代码以将Documents添加到index当中;第二类则是写代码将用户的查询转换成Lucene查询,并写Lucene提交查询,然后显示结果。整个的流程可以看成:围绕Index文件,一方面是提取文档元数据,把元数据通过IndexWriter添加到Index文件中。另一方面,通过IndexSearcher查询Index文件。

Lucene本身并不关心文件的格式。因为文件在添加之前,都要构造出一个索引字符串,不论被索引的文档是PDF还是纯文本。这样以来,Lucene必须结合一些好的开源的文档格式抽取器(如Tika)。在搜索的阶段,Lucene Query Parser把字符串转换成一系列的编程对象,由这些对象支持查询。

在从文档构造Index文件的过程中,需要经过“分析”。分析的任务是将原始的文本转化成可被索引的记号流。Lucene使用Analyser、Tokenizer与TokenFilter类负责这些任务。其中TokerFilters就是细节性地修正产生的Token了。

除此之外,Lucene还有一些高亮匹配文本、拼写检查、查找相似页面等功能。Luke是其上的一个前端。可以用它来查看索引文件,并执行相关的查询。

Solr介绍

Solr于2006年成为Apache的一个项目。目前常把Lucene与Solr放在一起讲述。目前的搜索模型有许多种,布尔模型、向量空间模型、语言模型、概率模型等。向量空间模型用得大概是最多的。经典的TF-IDF方法就是其中之一。Lucene的模型是修改过的向量空间模型,采用这样的原理检索文档。

Solr是一个开源的企业搜索服务器。基于Lucene。具有XML/HTTP API。在工作的时候支持cache、replication、web管理等功能。Solr实现中,借鉴了许多Lucene的最佳实践。方便扩展,并且安装与配置都很方便。在客户端方面,还设计了针对许多语言的接口。Solor的安装与启动都很简单。流程是:下载、运行。想要建立索引的话,cd到examples docs目录下,运行 java -jar post.jar *.xml 就可以了。

Solr的schema.xml描述了用户的数据与数据是怎样被处理的。它还允许用户添加除了字符串以外的一些类型(整型、浮点等)。solrconfig.xml文件描述了用户是怎样与数据交互的。配置了数据放置的位置、性能选项、以及允许的操作权限等。(企业级的应用中,许多的可选项一般是必然需要的)。

在Solr中添加索引一般需要客户端通过HTTP发送XML格式的文档表示添加索引的企图。通过HTTP与XML格式请求,客户端也可以完成删除文档、更新文档的操作。同样地,想要搜索文件,也通过客户端连接HTTP的方式请求指定的URL。Solr也有高亮、更多、拼写检查等功能。

Solr的高级功能有支持提高查询效率的副本功能、负载均衡、缓存等。Lucene采用的是嵌入到应用程序里面进行工作,而Solr采用的是作为单独的网络服务进行工作。

想具体测试Solr的性能,可以使用HTMLUNIT(Google)等工具。Solr的典型的使用场景是在Lucene索引文件的基础上,建立一个Solr Search Server。然后把它布署在HTTP Server应用服务器之上,用户通过访问应用服务器,间接使用Solr的服务。

至于传统CMS想使用Solr作为索引,则可以使用Curl与XSTL工具先进行转换,然后以XML的格式存到Solr当中。

在语言支持上面,Scala的Play框架可以异步调用Solr查询引擎。the play.api.libs.ws.WS library to call Solr and use Plays JSON support to extract the result (like in the example below) or is there any easier/faster way。网上有一些已经存在的好的实践方式,自己可以吸纳它们在Scala中是如何与Solr交互的。

Solr与自己的应用集成的时候,总是通过构造XML格式的添加文档、删除文档等命令实现。只要应用程序知道Solr服务器的URL就可以做到了。值得注意的是,Solr应当配置为utf-8编码,返回的结果也要使用utf-8转码。

注:Apache有一个叫OpenEJB的开源项目。似乎也值得一试。http://www.crawl-anywhere.com/上面给出了一个基于Solr的爬虫设计器。Mahout现在是用于机器学习,特别是用于协同过滤。在Mahout上面还有所谓的Giraph,用于图模型。

Apache的Solr官方网站上有Solr的快速入门及参考手册。但是在Solr快速入门中,介绍的最初的方式是纯文本的东西,比如XML、JSON、CSV格式的文档的查询。http://www.ibm.com/developerworks/cn/java/j-solr-lucene/#download上面的教程似乎更具有参考性。IBM上面提到了搜索引擎的新的用法。因为之间我们的理解都是搜索引擎用于搜索文本。但是Solr 4.x开始,发现了新的应用的需求。搜索引擎的核心被定义为关乎快速的、高效的过滤,然后依据某种相似度的概念对数据进行归类。搜索引擎还要能够处理稀疏数据和模糊数据,这此数据是现代数据应用程序的标志性特征。Lucene和Solr实现了各种灵活的相似性算法,还能够分析地理空间问题。这些功能使得搜索应用程序与传统的数据库应用程序(甚至是NoSQL应用)之间的界线。现在的Lucene与Solr可以说是不仅集成了数据后端(因而成为一种NoSQL),还可以自己定义复杂数据类型、数据存储、归类与分析功能。虽然搜索引擎不能解决所有的数据问题,然而我们仍然应该跳出众所周知的思维模式。

其实,很多的数据应用都是借助于搜索与简单的分析可以解决的,并不需要那么严谨的科学研究的模式。

欲使Solr支持PDF,还需要一些配置。参考https://wiki.apache.org/solr/UpdateRichDocuments