市面上的全文搜索引擎非常多,但通常想要支持中文搜索和拼音搜索都挺费力的。又是下载编译安装,又是装插件扩展,挺烦的。
我用 Sphinx 研究出了一种可以不用任何扩展,就能支持中文搜索+拼音搜索的解决方案。性能没差,效果也不差,一般不追求完美的已经足够了。
通常搜索引擎因为都是老外开发的,而欧美国家的词语,一般都是用空格分隔,所以很好做分词。而亚洲文字就比较尴尬了,连续的并没有分隔符,这样文字的分词就是一个大问题。
所以想要让搜索引擎支持中文搜索,首先要解决分词问题。比如我想到的就是把一串中文转为英文,存入文本索引,这样搜索引擎就可以识别了。/**
* 编码sphinx中文存储
* @param string $str
* @return string
*/
function encodeSphinxChinese($str)
{
return str_replace('\\', '%', substr(json_encode($str), 1, -1));
}
如上代码,我是用的将字符串转为 unicode 格式,sphinx建索引和搜索时候,都要将搜索词编码后才可以匹配结果。
那下面来说说拼音搜索的实现,要实现拼音搜索,就要先将中文翻译成拼音。考虑到多音字的情况,我将所有可能性的拼音都建进sphinx的索引里了。这里使用的是我开源的
比如将“我的”转换为拼音后,得到两个结果:“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);
}
最终的查询结果,不追求完美的也可以了,当然还有一些不是很严重的缺陷。本文仅提供一种思路,并不代表是最佳解决方案。代码单纯复制粘贴无用,仅作参考!