问题
WordPress的搜索功能非常简陋,如果用户搜索“日语综合教程+第六册”,WordPress会机械地构造一个wp_posts.post_title LIKE '%日语综合教程+第六册%'的SQL查询送到数据库里,结果十有八九找不到任何文章。因为我的文章里并没有直接出现这种句子,我一般给“日语综合教程”加了书名号,并且“第六册”前面一般有空格。
解决思路
解决思路是对用户的查询字串进行分词,然后用空格隔开,最后送到数据库里查询。
中文分词
以前我一直抱怨PHP平台并没有一款出色的分词套件,不过现在SAE提供了一款:http://apidoc.sinaapp.com/sae/SaeSegment.html
调用方法
/** * 分词 * @param $s 原始句子 * @return string 用空格隔开的分词结果 * @author hankcs */ function sae_seg($s) { $seg = new SaeSegment(); $ret = $seg->segment($s);//$s是原始查询串 if ($ret) { $phrase = ''; foreach ($ret as $word) { $phrase .= $word['word'] . ' '; } $phrase = substr($phrase, 0, strlen($phrase) - 1); } return $phrase; }
这样就可以将“日语综合教程+第六册”拆分为“日语 综合 教程 第六册”。
整合WordPress
目前有多种整合方式:
通过filter实现
function rebuild_search_query($request_vars) { if (!empty($request_vars['s'])) { $request_vars['s'] = sae_seg($request_vars['s']); } return $request_vars; } add_filter('request', 'rebuild_search_query');
这种方式在最大限度上优化了用户体验,但是每个页面都会触发rebuild_search_query,在效率上稍微有点浪费。毕竟不是每个页面都是搜索,也不是每个人都要搜索,更何况大部分搜索都有结果。
通过主题search.php实现
由此,我的想法是,当没有搜索结果的时候,就将搜索语句分词,通过js重定向,发起一次新的搜索:
if(!isset($_GET['seg'])) { $ret = sae_seg($request_vars['s']); if ($ret) { ?> <script type="text/javascript"> <!-- location.replace("<?php echo get_option('siteurl') . '/?s=' . $phrase .'&seg=done'; ?>"); --> </script> <?php } }
这样最大限度保证了效率,用户会看到屏幕闪烁了一下才出结果。
效果
就算你用再奇怪的句子,也能搜索到结果:
那搜索的不还是like %aa bb cc dd ee ff%
对,稍有改善。真正意义上的全文搜索:http://www.hankcs.com/program/php/wordpress-lucene.html ,太耗资源