Apache Lucy的全文检索引擎的使用

一、前言

  给定个需求:如果当客户提的问题在问题库中没有精确检索到,怎么推荐出最相近的问题呢?
  其实,本来想用之前的那个LSI来实现的,就是当SVD分解后得到特征矩阵和特征值,那么任何一个新的问题输入,就可以先计算得到降维的特征表示,然后根据向量相似距离就可以找出来了。用gensim的计算库工程是可以实现的,因为具有增量计算方法,可以不断新加新文本,淘汰文本就直接把相关问题的降维向量剔除就可以了。不过其主要遇到的问题还是SVD的计算量很复杂,而且当文本短而term极为稀疏的时候,如果要让检索有效,topic_num值必须设置很大才行。
  然后曲折另外一个解决方案:使用现有的全文检索引擎,将待选答案建立索引,有新查询语句,将其分词后在索引中查找即可,这等于把搜索全部外包出去了。不过这样也有缺点:没有了语义检的特性,不过语料有限情况下这种特性并不明显,明文检索已经可以;没有降纬减噪的功能,可以通过去除停留词达到部分减噪效果。
  当前开源的全文检索引擎很多,比如Lucene、Solr、Sphinx、Xapian等等。考虑到语言、项目集成等因素,最终选择了Apache Lucy,这个家伙算是KinoSearch的继任者,算是对Lucene的C/Perl语言的简洁移植版本,所以C语言可以很方便的在现有的项目中调用。

Apache Lucy

二、使用方法和注意事项

  Lucy的使用方法、接口感觉跟之前我使用的Whoosh(一个全Python实现的全文检索引擎)十分的相似,由于接触到的还不是很多,不知道是不是大多数的搜索引擎思路都是一致的。

2.1 使用步骤

  (1) 创建检索信息的表(Schema)结构(主要是各个索引列的名字、类型等信息);
  (2) 指明文件系统存储路径,然后调用Indexer_new创建索引(Indexer);
  (3) 针对每条记录/文档,创建成一个Doc对象,然后通过Indexer_Add_Doc将文档添加到索引中
  (4) Indexer_Commit提交计算生成索引数据;
  (5) 如果需要检索数据,使用IxSearcher_new方法创建IndexSearcher对象,然后使用IxSearcher_Hits检索遍历结果,而分别调用HitDoc_Get_Doc_ID和HitDoc_Get_Score可以得到文档ID和Rank Score;
  (6) 如果需要修改数据(比如增加或者删除Doc),就需要用Indexer_new创建索引,然后使用Indexer_Add_Doc、Indexer_Delete_By_Doc_ID等方法来实现。

2.2 注意事项:

  (1) Lucy的索引是基于文件系统的,所以使用Indexer更新、修改索引的时候会有锁结构。所以如果数据可以分类并且各类之间独立性比较强的话,建议按类别分别建立索引,这样可以提高并发量;
  (2) Lucy的字段有FullTextType类型和StringType类型,前者可以被检索,而后者只能全文匹配,对于需要精确查找和删除的需求的话,可以针对每个文档分别建立这两个类型的列,以方便精确查找操作。
  (3) 长时间的修改索引会导致分片,从而影响到后续检索的性能。Background merging的机制还是不错的,不过我还没看明白到底怎么个使用方法。

2.3 槽点

  (1) 不知道Lucy挂着ClownFish的原因是什么,这让代码十分晦涩,连个字符串也要创建和DECREF。本来想瞄一眼内部原理的,一瞅底层代码——巨坑,放弃了。
  (2) Lucy官方支持十几种语言,但中文不支持,不过也不能怪人家老外,国人当自强。不支持中文不会影响到使用,只是Snowball功能没法使用,这个Snowball就是将同个字进行合并的,比如将horse、horses、horsing变成hors,fishing、fished、fisher变成fish,argue、argued、argues、arguing、argus变成argu,其实中文也没有这么个概念吧,但是如果有人能把中文的同义词归类做进去,会是个刻骨铭心的贡献啊!(别找我,我没时间~)
  (3) 你说他是C API但也不是纯粹的C,当有些函数调用出错的时候,不是返回而是给你抛出和异常(Clownfish是不是就搞的跟C++似的),你还必须注意和捕获这些异常,否则程序就直接挂掉了。据称异常的捕获还是用的setjmp/longjmp来实现的,较为的不安全,可以参见附录的Trapping errors in C.

三、操作效果

  对数据库11773条语句建立索引,包括数据库查询的消耗,总共耗费墙上时间296ms,很不错了吧,如果用LSI没个七八分钟就不要想,而且还需要不断调优topic_num参数。

  同时,我在搜索资料的时候,发现一则消息:dmitri使用KinoSearch和Lucy近十年,然后用Lucy去索引PDF/HTML/DOC各类文件,然后结合Linux::Inotify2使用cron计划每隔几个小时更新索引,结果是”Surely impressed my $boss”。
  所以有些东西虽然简单,但是使用合理,也可达到不可思议的效果哦!不过Lucy确实比较的小众,三年多的maillist邮件列表才几百条。。。

参考