用redis+swcs实现的简单搜索引擎
在上一篇文章:搜索引擎的多重分词算法及分词词典更新时对索引的重新生成的简单想法 中,我用十分纠结的语言描述了一种基于redis+swcs实现简单搜索引擎的思路,今天就做了一个简单的demo,测试了下搜索的可行性.
悲剧,下面的代码被wordpress过滤的很严重,大家先将就着看,我研究研究
首先是分词及索引:
//以下代码的使用前提是已经安装了scws的php扩展和phpredis扩展
//scws init
$nowtime =time(); //获取当前的系统时间,搜索引擎已内容的发表时间作为搜索结果的排序权重
$cws = scws_new();
$cws->set_charset('utf8'); //注意!!!!redis良好支持utf8!!!!!!gbk编码未测!!!!
$multi = 8;
$cws->set_duality(false); //是否开启二元分词
$cws->set_ignore(true); //是否忽略标点
$cws->set_multi($multi); //设置多元分词模式,8表示将句子拆成单字,返回的分词结果为"正常分词"+"所有单字"
//假设"魔兽争霸"的正常分词结果为"魔兽"和"争霸",它的单字为"魔","兽","争","霸"
//那么将multi设为8的时候的结果就是"魔兽","争霸","魔","兽","争","霸"
//更详细的内容参加scws的文档
//end
$r = Redis();
$r->connect('127.0.0.1');
$r->select(15);//用redis第15号数据库作为测试数据库
$postid = $r->incr("global:nextPostId"); //获取当前数据的id,很redis的写法
$post = $_POST['content'];
$r->set("post:{$postid}",$post); //$post中保存为获取到的内容
//word split and index
$cws->send_text($post); //将$post中的内容发往scws分词程序
while ($res = $cws->get_result()){ //scws的结果并非一次性返回,而是分多次,若取到的结果为空则表示分词完毕
foreach ($res as $tmp)
{
if ($tmp['len'] == 1 && $tmp['word'] == "\r"){
continue;
}elseif ($tmp['len'] == 1 && $tmp['word'] == "\n"){
continue;
}else{
$r->zAdd("index:time:{$tmp['word']}",$nowtime,$postid); //将分好的词作为索引的key,时间作为权重,$postid作为zset的内容
//在这里我采用zset(sorted sets)来保存索引的内容,方便对搜索结果进行排行
}
}
}
$cws->close();
//end
然后是搜索部分:
$r = Redis(); //初始化redis
$r->connect('127.0.0.1');
$r->select(15);
$keyword = $_GET['k']; //获取搜索的关键字
// scws init
$nowtime = time();
$cws = scws_new();
$cws->set_charset('utf8');
$cws->set_duality(false);
$cws->set_ignore(true);
$cws->set_multi(0); //这里的0表示,只返回正常的分词结果
//end
//word split and search
$cws->send_text($keyword); //对关键字进行分词
$keyarray = array(); //用于保存用于查询的key的数组,这么做的原因是phpredis扩展在使用zinter来判断zset的相同内容时所传递的参数为key的数组
while ($res = $cws->get_result()){
foreach ($res as $tmp)
{
if ($tmp['len'] == 1 && $tmp['word'] == "\r"){
continue;
}elseif ($tmp['len'] == 1 && $tmp['word'] == "\n"){
continue;
}else{
$keyarray[] = "index:time:{$tmp['word']}"; //将需要查询的key放入数组
}
}
}
$cws->close();
$tmpkeyname = "tmpkey:{$nowtime}"; //用于保存zinter命令产生的临时搜索结果列表
//redis的另一个好处就是,你可以很方便的给搜索结果做缓存
$r->zInter($tmpkeyname ,$keyarray); //获取搜索结果
//这里我们还是以"魔兽争霸"做例子,"魔兽争霸"被分词为"魔兽"和"争霸"
//那么在$keyarray中保存的内容就应该是 array("index:time:魔兽","index:time:争霸")
//zInter所返回的结果,就是post内容中既包含"魔兽",又包含"争霸"的$postid号,继续看下面
$data = $r->sort($tmpkeyname,array('get'=>"post:*"));//通过sort指令对$tmpkeyname中的$postid数据按score(这里是时间)排序
//并根据排序结果get到post的内容 参见redis 的sort指令
foreach($data as $d){ //输出结果
echo "{$d}";
}
$r->delete($tmpkeyname ); //删除临时搜索结果,此例子没有用到缓存
//end
此代码仅仅实现了一个很简单的索引,和搜索功能,不过值得惊叹的是,就这么几十行代码就能实现一个简单的搜索引擎.
文章写的不是很清楚,各位看官有什么不清楚的地方可以跟我留言交流.谢谢:-)

