您的位置:首页 > 其它

用Sphinx给dedecms 5.5添加全文检索的功能

2010-01-20 16:24 288 查看
一、前言

dede内容管理系统,算是国内比较主流的一款cms了。

今年做项目时也接触了两次。它的搜索功能比较薄弱,默认情况下只是对文章标题和关键字进行搜索,而且使用的是数据库的like方式查询,数据量一旦突破10W可能就会产生一些性能的瓶颈。

这里我们对它进行一些改造,让它的搜索不仅仅只是搜索标题和关键字,还包括文章内容。使用全文检索引擎,替换性能低下的like查询方式。

开源免费的全文检索引擎比较主流的选择有:lucene和sphinx,由于我们是php环境,方便起见,这里选择可以和mysql数据库完美结合的sphinx

二、Sphinx 简介



出自俄罗斯的开源全文搜索引擎软件Sphinx


单一索引最大可包含1亿条记录,在1千万条记录情况下的查询速度为0.x秒(毫秒级)。Sphinx创建索引的速度为:创建100万条记录的索引只需
3~4分钟,创建1000万条记录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。

官方网站:
http://www.sphinxsearch.com
推荐博文:
http://blog.s135.com/post/360/
三、Coreseek 简介

Coreseek是基于Sphinx的一款软件,对Sphinx做了一些改动,主要是中文切分词方面更合理一些

变更如下:

改进MMSeg切词策略,支持产品型号的切分

修正MMSeg切分结果不一致的问题

增加MMSeg的Python接口(Win32 Only)

修正了PySource的若干错误

为主流的Linux平台提供的预编译二进制包

官方网站:
http://www.coreseek.cn/
帮助手册pdf下载:
http://www.coreseek.cn/uploads/pdf/sphinx_doc_zhcn_0.9.pdf
四、准备工作



在对dedecms进行改动前,首先要安装Coreseek。Coreseek的安装可以查看上面提到的帮助手册,需要提醒的是:

1、Coreseek可以跑在windows环境下,但仅可用于测试

2、安装前需要首先安装python环境

Coreseek的安装方法还可以通过搜索得到
http://www.google.cn/search?hl=zh-CN&newwindow=1&q=coreseek+%E5%AE%89%E8%A3%85&btnG=Google+%E6%90%9C%E7%B4%A2&aq=f&oq=
这里就不做重复了,我的这次测试是在windows xp环境下进行的(公司有个discuz论坛也是用的Sphinx做全文检索,跑在linux下)。

五、Sphinx的config设置

source dedecms
{
type= mysql
sql_host= localhost
#mysql主机地址
sql_user= root
#mysql用户名
sql_pass= tear
#mysql密码
sql_db= dede
#dede库名
sql_port= 3306
#mysql端口
sql_query_pre= SET NAMES utf8
#数据库字符类型
sql_query_pre= SET SESSION query_cache_type=OFF
sql_query= SELECT aid, typeid, body FROM dede_addonarticle
sql_ranged_throttle= 0
sql_query_info= SELECT * FROM dede_addonarticle WHERE aid=$id
}
index cgfinal
{
source= dedecms
path= d:/csft/data/test1
#索引文件存放的地址
docinfo= extern
mlock= 0
morphology= none
min_word_len= 1
charset_type= zh_cn.utf-8
charset_dictpath= d:/csft/data
#分词字典的存放位置
html_strip= 0
}
indexer
{
mem_limit= 32M
}
searchd
#这里都是默认的,没做修改,还是linux的路径,因为只是测试,就不管它了,如果要好好设置的话要查看手册对照着做
{
log= /usr/local/sphinx/var/log/searchd.log
query_log= /usr/local/sphinx/var/log/query.log
read_timeout= 5
client_timeout= 3
max_children= 30
pid_file= /usr/local/sphinx/var/log/searchd.pid
max_matches= 10000
seamless_rotate= 1
preopen_indexes= 0
unlink_old= 1
mva_updates_pool= 1M
max_packet_size= 8M
max_filters= 256
max_filter_values= 4096
}


六、在dedecms数据库中建立sphinx表

前面的安装过程中,给mysql增加了一个sphinx模块,可以创建sphinx类型的表。

建表sql如下:

CREATE TABLE IF NOT EXISTS `sphinx` (
`id` int(11) NOT NULL,
`weight` int(11) NOT NULL,
`query` varchar(255) NOT NULL,
KEY `Query` (`query`)
) ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://localhost:3312/cgfinal';


七、修改dede的搜索类 include/arc.searchview.class.php

改两个方法:

1、CountRecord

//统计列表里的记录
function CountRecord()
{
$this->TotalResult = -1;
if(isset($GLOBALS['TotalResult']))
{
$this->TotalResult = $GLOBALS['TotalResult'];
}
if(isset($GLOBALS['PageNo']))
{
$this->PageNo = $GLOBALS['PageNo'];
}
else
{
$this->PageNo = 1;
}
$ksql = $this->GetKeywordSql();
$ksqls = array();
if($this->StartTime > 0)
{
$ksqls[] = " arc.senddate>'".$this->StartTime."' ";
}
if($this->TypeID > 0)
{
$ksqls[] = " typeid in (".GetSonIds($this->TypeID).") ";
}
if($this->ChannelType > 0)
{
$ksqls[] = " arc.channel='".$this->ChannelType."'";
}
if($this->mid > 0)
{
$ksqls[] = " arc.mid = '".$this->mid."'";
}
$ksqls[] = " arc.arcrank > -1 ";
$this->AddSql = ($ksql=='' ? join(' And ',$ksqls) : join(' And ',$ksqls)." And ($ksql)" );
$cquery="select dede_addonarticle.* from dede_addonarticle, sphinx where dede_addonarticle.aid=sphinx.id and query='".$this->Keyword."'";
//$cquery = "Select * From `#@__archives` arc where ".$this->AddSql;
$hascode = md5($cquery);
$row = $this->dsql->GetOne("Select * From `#@__arccache` where `md5hash`='".$hascode."' ");
$uptime = time();
if(is_array($row) && time()-$row['uptime'] < 3600 * 24)
{
$aids = explode(',', $row['cachedata']);
$this->TotalResult = count($aids)-1;
$this->RsFields = $row['cachedata'];
}
else
{
if($this->TotalResult==-1)
{
$this->dsql->SetQuery($cquery);
$this->dsql->execute();
$aidarr = array();
$aidarr[] = 0;
while($row = $this->dsql->getarray())
{
$aidarr[] = $row['id'];
}
$nums = count($aidarr)-1;
$aids = implode(',', $aidarr);
$delete = "Delete From `#@__arccache` where uptime<".(time() - 3600 * 24);
$this->dsql->SetQuery($delete);
$this->dsql->executenonequery();
$insert = "insert into `#@__arccache` (`md5hash`, `uptime`, `cachedata`)
values('$hascode', '$uptime', '$aids')";
$this->dsql->SetQuery($insert);
$this->dsql->executenonequery();
$this->TotalResult = $nums;
}
}
}


2、GetArcList

//获得文档列表
function GetArcList($limitstart=0,$row=10,$col=1,$titlelen=30,$infolen=250,
$imgwidth=120,$imgheight=90,$achanneltype="all",$orderby="default",$innertext="",$tablewidth="100")
{
$typeid=$this->TypeID;
if($row=='') $row = 10;
if($limitstart=='') $limitstart = 0;
if($titlelen=='') $titlelen = 30;
if($infolen=='') $infolen = 250;
if($imgwidth=='') $imgwidth = 120;
if($imgheight='') $imgheight = 120;
if($achanneltype=='') $achanneltype = '0';
$orderby = $orderby=='' ? 'default' : strtolower($orderby);
$tablewidth = str_replace("%","",$tablewidth);
if($tablewidth=='') $tablewidth=100;
if($col=='') $col=1;
$colWidth = ceil(100/$col);
$tablewidth = $tablewidth."%";
$colWidth = $colWidth."%";
$innertext = trim($innertext);
if($innertext=='')
{
$innertext = GetSysTemplets("search_list.htm");
}
//排序方式
$ordersql = '';
if($orderby=="senddate")
{
$ordersql=" order by arc.senddate desc";
}
else if($orderby=="pubdate")
{
$ordersql=" order by arc.pubdate desc";
}
else if($orderby=="id")
{
$ordersql="  order by arc.id desc";
}
else
{
$ordersql=" order by arc.sortrank desc";
}
//搜索
$query = "Select arc.*,act.typedir,act.typename,act.isdefault,act.defaultname,act.namerule,
act.namerule2,act.ispart,act.moresite,act.siteurl,act.sitepath
from sphinx,`#@__archives` arc left join `#@__arctype` act on arc.typeid=act.id
where arc.id=sphinx.id and query='".$this->Keyword."' $ordersql limit $limitstart,$row";
$this->dsql->SetQuery($query);
$this->dsql->Execute("al");
$artlist = "";
if($col>1)
{
$artlist = "<table width='$tablewidth' border='0' cellspacing='0' cellpadding='0'>/r/n";
}
$this->dtp2->LoadSource($innertext);
for($i=0;$i<$row;$i++)
{
if($col>1)
{
$artlist .= "<tr>/r/n";
}
for($j=0;$j<$col;$j++)
{
if($col>1)
{
$artlist .= "<td width='$colWidth'>/r/n";
}
if($row = $this->dsql->GetArray("al"))
{
//处理一些特殊字段
$row["arcurl"] = GetFileUrl($row["id"],$row["typeid"],$row["senddate"],$row["title"],
$row["ismake"],$row["arcrank"],$row["namerule"],$row["typedir"],$row["money"],$row['filename'],$row["moresite"],$row["siteurl"],$row["sitepath"]);
$row["description"] = $this->GetRedKeyWord(cn_substr($row["description"],$infolen));
$row["title"] = $this->GetRedKeyWord(cn_substr($row["title"],$titlelen));
$row["id"] =  $row["id"];
if($row['litpic'] == '-' || $row['litpic'] == '')
{
$row['litpic'] = $GLOBALS['cfg_cmspath'].'/images/defaultpic.gif';
}
if(!eregi("^http://",$row['litpic']) && $GLOBALS['cfg_multi_site'] == 'Y')
{
$row['litpic'] = $GLOBALS['cfg_mainsite'].$row['litpic'];
}
$row['picname'] = $row['litpic'];
$row["typeurl"] = GetTypeUrl($row["typeid"],$row["typedir"],$row["isdefault"],$row["defaultname"],$row["ispart"],$row["namerule2"],$row["moresite"],$row["siteurl"],$row["sitepath"]);
$row["info"] = $row["description"];
$row["filename"] = $row["arcurl"];
$row["stime"] = GetDateMK($row["pubdate"]);
$row["textlink"] = "<a href="".$row["filename"]."" mce_href="".$row["filename"]."">".$row["title"]."</a>";
$row["typelink"] = "[<a href="".$row["typeurl"]."" mce_href="".$row["typeurl"]."">".$row["typename"]."</a>]";
$row["imglink"] = "<a href="".$row["filename"]."" mce_href="".$row["filename"].""><img src="".$row["picname"]."" mce_src="".$row["picname"]."" border='0' width='$imgwidth' height='$imgheight'></a>";
$row["image"] = "<img src="".$row["picname"]."" mce_src="".$row["picname"]."" border='0' width='$imgwidth' height='$imgheight'>";
$row['plusurl'] = $row['phpurl'] = $GLOBALS['cfg_phpurl'];
$row['memberurl'] = $GLOBALS['cfg_memberurl'];
$row['templeturl'] = $GLOBALS['cfg_templeturl'];
if(is_array($this->dtp2->CTags))
{
foreach($this->dtp2->CTags as $k=>$ctag)
{
if($ctag->GetName()=='array')
{
//传递整个数组,在runphp模式中有特殊作用
$this->dtp2->Assign($k,$row);
}
else
{
if(isset($row[$ctag->GetName()]))
{
$this->dtp2->Assign($k,$row[$ctag->GetName()]);
}
else
{
$this->dtp2->Assign($k,'');
}
}
}
}
$artlist .= $this->dtp2->GetResult();
}//if hasRow
else
{
$artlist .= "";
}
if($col>1) $artlist .= "</td>/r/n";
}//Loop Col
if($col>1)
{
$artlist .= "</tr>/r/n";
}
}//Loop Line
if($col>1)
{
$artlist .= "</table>/r/n";
}

$this->dsql->FreeResult("al");
return $artlist;
}


八、运行


到这里dedecms就可以借助sphinx进行全文检索了,当然,在生产环境下,还要做些优化。

下面是一些操作:

1、启动sphinx

在sphinx/bin目录下

searchd --config d:/csft

/csft.conf

这里csft.conf是上面的sphinx的config文件

命令为dos命令

2、创建索引

在sphinx/bin目录下

indexer dedecms –config d:/csft

/csft.conf

写的比较仓促,而且这个测试是前两个月做的,所以可能总结的不够好,如果有问题,欢迎发邮件给我:warran(at)qq.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: