放牧代码和思想
专注自然语言处理、机器学习算法
    This thing called love. Know I would've. Thrown it all away. Wouldn't hesitate.

WordPress集成Lucene全文搜索

目录

终于将Lucene集成到中文WordPress中了,实现了中文博客的全文搜索。作为一个搞NLP的博主,如果还在用SQL的LIKE做搜索,那也太不上档次了。这次集成主要难点在于

①PHP平台上并没有一款成熟的分词套件。

②Lucene的PHP Port只有Zend_Search_Lucene一家,其对中文的支持非常糟糕,默认编码是ANSII;而且代码内部有不少错误,比如静态方法调用非静态成员。

③BAE对程序执行时间有限制,因此长文章必须忍痛割爱。

下面谈谈问题的解决以及整个实现过程。

一、Lucene的集成

wpsearch插件是WordPress上的Lucene集成插件,不过不要高兴得太早。wpsearch已经2年多没有更新,并且不支持中文。解决思路是去Zend的发行包里提取下列文件:

Zend
│  Exception.php
│  tree.txt
│  Version.txt
│  
├─Search
│  │  Exception.php
│  │  Lucene.php
│  │  
│  └─Lucene
│      │  Document.php
│      │  Exception.php
│      │  Field.php
│      │  FSM.php
│      │  FSMAction.php
│      │  Interface.php
│      │  LockManager.php
│      │  MultiSearcher.php
│      │  PriorityQueue.php
│      │  Proxy.php
│      │  TermStreamsPriorityQueue.php
│      │  
│      ├─Analysis
│      │  │  Analyzer.php
│      │  │  Token.php
│      │  │  TokenFilter.php
│      │  │  
│      │  ├─Analyzer
│      │  │  │  Common.php
│      │  │  │  
│      │  │  └─Common
│      │  │      │  Text.php
│      │  │      │  TextNum.php
│      │  │      │  Utf8.php
│      │  │      │  Utf8Num.php
│      │  │      │  
│      │  │      ├─Text
│      │  │      │      CaseInsensitive.php
│      │  │      │      
│      │  │      ├─TextNum
│      │  │      │      CaseInsensitive.php
│      │  │      │      
│      │  │      ├─Utf8
│      │  │      │      CaseInsensitive.php
│      │  │      │      
│      │  │      └─Utf8Num
│      │  │              CaseInsensitive.php
│      │  │              
│      │  └─TokenFilter
│      │          LowerCase.php
│      │          LowerCaseUtf8.php
│      │          ShortWords.php
│      │          StopWords.php
│      │          
│      ├─Document
│      │      Docx.php
│      │      Exception.php
│      │      Html.php
│      │      OpenXml.php
│      │      Pptx.php
│      │      Xlsx.php
│      │      
│      ├─Index
│      │  │  DictionaryLoader.php
│      │  │  DocsFilter.php
│      │  │  FieldInfo.php
│      │  │  SegmentInfo.php
│      │  │  SegmentInfoPriorityQueue.php
│      │  │  SegmentMerger.php
│      │  │  SegmentWriter.php
│      │  │  Term.php
│      │  │  TermInfo.php
│      │  │  TermsPriorityQueue.php
│      │  │  Writer.php
│      │  │  
│      │  ├─SegmentWriter
│      │  │      DocumentWriter.php
│      │  │      StreamWriter.php
│      │  │      
│      │  └─TermsStream
│      │          Interface.php
│      │          
│      ├─Interface
│      │      MultiSearcher.php
│      │      
│      ├─Search
│      │  │  BooleanExpressionRecognizer.php
│      │  │  Query.php
│      │  │  QueryEntry.php
│      │  │  QueryHit.php
│      │  │  QueryLexer.php
│      │  │  QueryParser.php
│      │  │  QueryParserContext.php
│      │  │  QueryParserException.php
│      │  │  QueryToken.php
│      │  │  Similarity.php
│      │  │  Weight.php
│      │  │  
│      │  ├─Highlighter
│      │  │      Default.php
│      │  │      Interface.php
│      │  │      
│      │  ├─Query
│      │  │  │  Boolean.php
│      │  │  │  Empty.php
│      │  │  │  Fuzzy.php
│      │  │  │  Insignificant.php
│      │  │  │  MultiTerm.php
│      │  │  │  Phrase.php
│      │  │  │  Preprocessing.php
│      │  │  │  Range.php
│      │  │  │  Term.php
│      │  │  │  Wildcard.php
│      │  │  │  
│      │  │  └─Preprocessing
│      │  │          Fuzzy.php
│      │  │          Phrase.php
│      │  │          Term.php
│      │  │          
│      │  ├─QueryEntry
│      │  │      Phrase.php
│      │  │      Subquery.php
│      │  │      Term.php
│      │  │      
│      │  ├─Similarity
│      │  │      Default.php
│      │  │      
│      │  └─Weight
│      │          Boolean.php
│      │          Empty.php
│      │          MultiTerm.php
│      │          Phrase.php
│      │          Term.php
│      │          
│      └─Storage
│          │  Directory.php
│          │  File.php
│          │  
│          ├─Directory
│          │      Filesystem.php
│          │      
│          └─File
│                  Filesystem.php
│                  Memory.php
│                  
└─Xml
        Exception.php
        Security.php

替换wpsearch的Zend文件夹,这还没完注意Zend的引用路径需要修改。这样我们就让一款2年前的插件获得了新的生命力。

二、PHP中文分词

PHP本来就不方便做高级算法,所以纯PHP的分词包极其难找,我只发现了一款PHPAnalysis分词系统。PHPAnalysis只支持词典分词,也就是说分词并不精准。不过我暂时也不能要求那么高,我倒是想用CRF分词,但问题是PHP的执行效率能满足要求吗?BAE的时间限制够用吗?如果是自己的主机的话,我当然可以选择Java Bridge或者其他的C接口。但是博客目前托管在BAE云平台上,没有那么高的自由度。

仿照StandardAnalyzer_Analyzer_Standard_English自己实现一个中文Analyzer,等以后有机会再用高级的分词吧。

(注:此分词实在无法胜任,码农场已经放弃全文索引。)

三、长文章截取

由于BAE开启了FastCGI,所以必须考虑时间耗费。对于长度过大的文章,只截取前10000个字做索引,总算在BAE上面跑了起来。

看看WordPress全文搜索的效果吧

我以“算法的文章”作为查询条件,很明显,任何文章里都没有直接写这句话,SQL的LIKE也就无能为力了,但是Lucene能够给出正确的答案:

效果还是蛮不错的,有时间加一点复杂的功能,比如同义词和指代消解,想想还是挺有趣的。

知识共享许可协议 知识共享署名-非商业性使用-相同方式共享码农场 » WordPress集成Lucene全文搜索

评论 6

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #4

    lucene的相关搜索怎么做的,求指教? [给力]

    电瓶10年前 (2015-02-02)回复
  2. #3

    是改每个文件的require吧,工作量还是很大的。还有个问题,关于速度的。我在本地测试了一下速度,3Wpost的WP,使用自带搜索,耗时0.6S左右。如果使用wpsearch,生成index后测试,要1S左右,感觉效率上自带搜索还快些啊,按理应该是lucene快些的,不知道怎么回事

    andy10年前 (2014-08-07)回复
    • 我觉得Lucene本来就应该慢一些,index储存在文件系统中,每次搜索都要加载进来;搜索语句每次都要分词,每个term每个document都要评分排序;以上都是用php完成的,而MySQL用C实现,有自己的缓存。

      hankcs10年前 (2014-08-07)回复
  3. #2

    是改每个文件的require吧,工作量还是很大的。还有个问题,关于速度的。我在本地测试了一下速度,3Wpost的WP,使用自带搜索,耗时0.6S左右。如果使用wpsearch,生成index后测试,要1S左右,感觉效率上自带搜索还快些啊,按理应该是lucene快些的,不知道怎么回事

    andy10年前 (2014-08-07)回复
  4. #1

    你好,博主,我想启用这个插件,我下wpsearch插件,但不知道怎么修改。我主要是用于英文搜索,没有中文,而且post量比较多,有好几万吧。文中说到的“Zend的发行包里提取下列文件”,是指Zend Framework2吗,怎么再出那么多文件?“注意Zend的引用路径需要修改”,这个又怎么改呢?希望不吝赐教,谢谢

    andy10年前 (2014-08-06)回复

我的作品

HanLP自然语言处理包《自然语言处理入门》