问题
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 ,太耗资源