市面上的全文搜索引擎非常多,但通常想要支持中文搜索和拼音搜索都挺费力的。又是下载编译安装,又是装插件扩展,挺烦的。
我用 Sphinx 研究出了一种可以不用任何扩展,就能支持中文搜索+拼音搜索的解决方案。性能没差,效果也不差,一般不追求完美的已经足够了。
通常搜索引擎因为都是老外开发的,而欧美国家的词语,一般都是用空格分隔,所以很好做分词。而亚洲文字就比较尴尬了,连续的并没有分隔符,这样文字的分词就是一个大问题。
所以想要让搜索引擎支持中文搜索,首先要解决分词问题。比如我想到的就是把一串中文转为英文,存入文本索引,这样搜索引擎就可以识别了。
/** * 编码sphinx中文存储 * @param string $str * @return string */ function encodeSphinxChinese($str) { return str_replace('\\', '%', substr(json_encode($str), 1, -1)); }
如上代码,我是用的将字符串转为 unicode 格式,sphinx建索引和搜索时候,都要将搜索词编码后才可以匹配结果。
那下面来说说拼音搜索的实现,要实现拼音搜索,就要先将中文翻译成拼音。考虑到多音字的情况,我将所有可能性的拼音都建进sphinx的索引里了。这里使用的是我开源的ChineseUtil(https://github.com/Yurunsoft/ChineseUtil)。
比如将“我的”转换为拼音后,得到两个结果:“wo de”和“wo di”,将他们都存入文本索引。
拼音索引问题解决,那接下来就是搜索的时候了。用户搜索拼音,不可能给你按空格,很有可能输入的是“wode”,那这时候是匹配不到的。这时候需要进行拼音分词。
比如用户输入“xianggang”,拼音分词后得到2个结果:“xi ang gang”和“xiang gang”,这时候处理搜索接口时候,需要将这两个词都作为搜索词。
然后通过PHP代码,将返回的搜索结果进行合并和权重排序。
$sc = new SphinxClient; // 这里做一些其它事,省略 $results = $sc->RunQueries(); if(false === $results) { throw new Exception(mb_convert_encoding($sc->GetLastError(), 'UTF-8', 'GBK')); } $totalFound = 0; $matches = array(); foreach($results as $item) { if(!empty($item['error'])) { throw new Exception($item['error']); } $totalFound += $item['total_found']; if(!empty($item['matches'])) { foreach($item['matches'] as $id => $match) { if(!isset($matches[$id])) { $matches[$id] = []; } if(isset($matches[$id]['count'])) { ++$matches[$id]['count']; } else { $matches[$id]['count'] = 2; } $matches[$id]['weight'] += $match['weight']; } } } unset($results); uasort($matches, function(&$a, &$b){ if(isset($a['count'])) { $a['weight'] /= $a['count']; unset($a['count']); } if(isset($b['count'])) { $b['weight'] /= $b['count']; unset($b['count']); } if($a['weight'] === $b['weight']) { return 0; } else { return $a['weight'] > $b['weight'] ? -1 : 1; } }); $matches = array_keys($matches); if(null === $limit) { return $matches; } else { return array_splice($matches, 0, $limit); }
最终的查询结果,不追求完美的也可以了,当然还有一些不是很严重的缺陷。本文仅提供一种思路,并不代表是最佳解决方案。代码单纯复制粘贴无用,仅作参考!