php调用sphinx有两种方式:

  • 一是引入sphinx的接口
  • 二是安装php的sphinx扩展

sphinx重要的几个方法

①setMatchMode ( int $mode )全文检索模式匹配

$mode表示匹配模式, 有以下5种匹配模式

Constant    Description
SPH_MATCH_ALL    匹配所有查询关键字,比如你搜索"杭州钢材",那么实际搜索的是同时包含"杭州"和"钢材"两个关键字的文章
SPH_MATCH_ANY    匹配任意一个查询关键字,比如你搜索"杭州钢材",那么实际搜索的是包含"杭州"或"钢材"关键字的文章
SPH_MATCH_PHRASE 将查询看成一个词组,要求按顺序完整匹配,这个类似于select * like "%杭州钢材%"。比如你搜"2016年8月份杭州钢材"能搜到,但是搜2016年8月份杭州统计汇总,钢材价格"就搜不到。
SPH_MATCH_BOOLEAN    将查询看成一个布尔表达式,可以简单地进行与或运算。例如(杭州!钢材),搜索匹配杭州但是不匹配钢材的记录
SPH_MATCH_EXTENDED2    sphinx扩展匹配模式,将查询看成sphinx内部的表达式,可以使用以下运算符:
   或 例如:杭州 |钢材
   非 例如:杭州 !钢材
   字段搜索符 例如@title 杭州 @description 钢材  表示搜索文章title包含杭州,描述包含钢材的记录<
   字段限位  例如@title[100] 杭州 表示搜索标题包含杭州,并且标题长度不超过100的记录
   多字段搜索符 例如@(title,content)杭州钢材 表示搜索标题或文章内容里包含"杭州钢材"的记录
   严格搜索符 例如 杭州 << 钢材 表示杭州,钢材需要按顺序出现
   字段开始和结束符 例如 ^杭州...钢材$ 表示限定必须以杭州开始,钢材结束
SPH_MATCH_FULLSCAN 完整扫描,此模式下所有的查询词都将被忽略,没有分词结果。
SPH_MATCH_EXTENDED 同SPH_MATCH_EXTEND2

②setSortMode 全文检索排序模式

Constant    Description
SPH_SORT_RELEVANCE    按相关度评分排序,最好的匹配排在前面
SPH_SORT_ATTR_DESC    按照属性降序排列 例如 :setSortMode(SphinxClient::SPH_SORT_ATTR_DESC,'publish_time')按照发布时间降序
SPH_SORT_ATTR_ASC    按照属性升序排列 例如:setSortMode(SphinxClient::SPH_SORT_ATTR_ASC'publish_time')按照发布时间升序
SPH_SORT_TIME_SEGMENTS segments表示部分,段的意思。这个模式表示先按时间排序,再按相关度排序。这个时间是sphinx自己内部定义的规则
SPH_SORT_EXTENDED    按类似sql的方式排列组合起来,比如@weight desc,publish_time desc。表示先按权值,即匹配度降序,再按发布时间降序
 其中以@开头的是内置属性,包括
 @weight 权值 
 @id 文档id
 @relevance 等同于weight
SPH_SORT_EXPR    表达式排序方式,即可以按照表达式计算出来的结果进行排序。例如 setSortMode(SphinxClient::SPH_SORT_EXPR,'@weight+@id')

③setRankingMode 搜索排名

Constant    Description
SPH_RANK_PROXIMITY_BM25    Default ranking mode which uses both proximity and BM25 ranking
SPH_RANK_BM25    Statistical ranking mode which uses BM25 ranking only (similar to most of other full-text engines). This mode is faster, but may result in worse quality on queries which contain more than 1 keyword.
SPH_RANK_NONE    不进行排序

④setFilterRanging($attribute,$min,$max,$exclude=false)

指定一个值的范围,例如setFilterRanging(‘publish_time’,’2016-08-26 00:00:00′,’2016-08-27 00:00:00′);第四个参数若为true,表示取不在这个范围的数据。

⑤setFieldweights(array $weights)设置查询的权重,权重大的会优先搜索

例如:

setFieldweights([
  'title'=>10,
  'content'=>6,
   'description'=>3
]);//优先匹配title里包含关键字的,然后是content,然后是description。

⑥setLimits($offset,$limit,$max=1000,$cutoff)分页查询

$offset,$limit类似于mysql里的offset,limit.

$max表示搜索请求中返回的最大数据量,默认是1000.比如搜索”行情”,实际上有10000条记录,但是没有设置此参数,默认只返回1000条记录。所有totals为1000.所以一般这个值须要设置。

$cutoff控制查询的数量限制,它跟$max的区别是:$max是返回的最大数据量,比如说实际搜到了10000条记录,但是只返回1000条。$cutoff表示一开始搜索时就限制搜索的数据量,比如最多搜索50条停止,那么搜索到50条就会停止搜索。这个一般可以不做设置。

⑦setGroupBy($attribute,$func,$groupsort=’@group desc ‘)分组查询

$func默认有以下几个:

SPH_GROUPBY_DAY,
SPH_GROUPBY_WEEK,
SPH_GROUPBY_MONTH,
SPH_GROUPBY_YEAR,
SPH_GROUPBY_ATTR,
SPH_GROUPBY_ATTRPAIR

DAY,WEEK,MONTH,YEAR这个一般可以用于搜索最近一天,一周,一月或一年的记录。ATTR表示按照指定的属性值分组

⑧setArrayResult设置返回的数据为数组

⑨setFilter($attribute,array $value,$exclude = false)

属性过滤,例如setFilter(‘id’,[1,2,3]);第三个参数若为true,表示id不为1,2,3的。

php里调用sphinx例子

包括搜索和更新状态两个功能使用

//实例化   
require_once("sphinxclient.class.php");   
$sphinx = new SphinxClient;   
//搜索功能   
$mode = SPH_MATCH_EXTENDED2;   //匹配模式   
$ranker = SPH_RANK_PROXIMITY_BM25; //统计相关度计算模式,仅使用BM25评分计算   
$sphinx->SetServer('127.0.0.1', '9312');   
$sphinx->SetArrayResult(true);   
$sphinx->SetMatchMode($mode);   
$sphinx->SetRankingMode($ranker);   
//过滤status字段,只显示正常帖子,不显示已删除帖子。字段要在sphinx配置为索引   
$sphinx->SetFilter('status', array(0));   
//过滤时间段   
if($StartTime > 0)   
{   
    $sphinx->SetFilterRange('senddate', $StartTime, time(), false);   
}   
//设置字段的权重   
$sphinx->SetFieldWeights(array('title' => 10, 'description' => 5, 'body' => 5));   
//设置排序,先按权重,再按id   
$sphinx->SetSortMode( SPH_SORT_EXTENDED, "@weight DESC, @id desc" );   
//分页   
$limitstart = 0;   
$row = 10;   
$sphinx->SetLimits($limitstart, (int)$row, ($row>1000) ? $row : 1000);   
//结果   
$res = array();   
$res = $sphinx->Query($this->Keywords." @flag !s", 'mysql, delta');   
var_dump($res);   
  
//删除索引,删除帖子时更新状态(主索引和增量索引),不让搜索时搜索出来   
$sphinx->UpdateAttributes('mysql', array('status'),array($aid => array(-2)));   
$sphinx->UpdateAttributes('delta', array('status'),array($aid => array(-2)));   

框架中使用

我在thinkphp5框架中使用。php环境为7.2后发现sphinx的高亮显示出了问题。自己中间加入了中文分词手动替换高亮显示

namespace app\admin\service;


use function array_column;
use function array_combine;
use function array_key_exists;
use function is_array;
use function join;
use think\Db;
use think\Exception;
use think\Paginator;
use think\paginator\driver\Bootstrap;
use function var_dump;

class sphinx
{
    private $sphinx;
    private $data=[];
    private $keyword;
    private $tableArr=['wk_courses'=>1,'wk_article'=>2,'wk_wenda_post'=>3];
    private $total_found=0;
    private $table_total=[];
    private $opts=[
        "before_match"=>'<span style="color: red;">',
        "after_match"=>'</span>',
    ];



    public function __construct()
    {

        $this->sphinx = new \SphinxClient;
        $this->sphinx->setServer("localhost", 9312);
        $this->sphinx->setMatchMode(SPH_MATCH_EXTENDED2);
//        $this->sphinx->setMatchMode(SPH_MATCH_ANY);
        $this->sphinx->setMaxQueryTime(30);
        $this->sphinx->SetArrayResult(false);
        $this->sphinx->SetSelect ( "*" );
        $weights = array ('title'=>94, 'content'=>6);
        $this->sphinx->SetFieldWeights($weights);
        //设置返回信息的内容,等同于SQL

//        $this->sphinx->SetSortMode(SPH_SORT_RELEVANCE);
        $this->sphinx->SetSortMode( SPH_SORT_EXTENDED, "@weight DESC, @id desc" );
        $this->sphinx->SetRankingMode(SPH_RANK_PROXIMITY_BM25);


        return $this;
    }

    public function keyWord($keyword)
    {
        $this->keyword=trim($keyword);
        return $this;
    }


    /**
     * @param $table
     * @param int $count
     * @return $this
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public  function search($table,$count=5)
    {

        $this->sphinx->SetLimits ( 0, $count, 1000,0);
        $res = $this->sphinx->query($this->keyword,"{$table}:{$table}_delta"); #1 关键字,*是所有数据源source
        if(!array_key_exists($table,$this->tableArr)
            || $this->keyword==null
            || $res==false
            ||empty($res['matches'])){
            return $this;
        }
        $total_found=$res['total_found'];
        if($total_found>$count){$more=true;}else{$more=false;}
        $ids=join (',',array_keys($res['matches']));
        if(empty($ids)){return $this;}
//        $field=Db::query("SHOW COLUMNS FROM `".$table."`",[]);
//        $fieldArr=array_column($field,'Field');

        $data=[];

//        $res=Db::table($table)->where(['id'=>['in',$ids]])->select()->toArray();
        $data=Db::table($table)->where(['id'=>['in',$ids]])->select()->toArray();
//        try{
//            foreach ($res as $key=>$val){
//                $tmp=[];
//                $tmpData=$this->sphinx->BuildExcerpts( $val, $table, $this->keyword, $this->opts);
//                foreach ($tmpData as $k=>$v){
//                    $tmp[$fieldArr[$k]]=$v;
//                }
//                $tmp['db_type']=$this->tableArr[$table];
//                $tmp=$this->$table($tmp,$table);
//                $data[]=$tmp;
//            }
//        }catch (\Exception $e){
//            $data=$res;
            foreach ($data as $key=>$val){
                $data[$key]['db_type']=$this->tableArr[$table];
            }
            $data=$this->$table($data,$table);
//        }
        $this->data[$this->tableArr[$table]]=$data;
        $this->total_found+=$total_found;
        $this->table_total[$this->tableArr[$table]]=$more;
        return $this;
    }

    public function getData()
    {
        $data=[];
        $data['num']=$this->total_found;
        $data['data']=$this->data;
        $data['more_status']=$this->table_total;
        return $data;
    }


    public function SearchLimitTable($page=0,$pageSize=10,$table,$path)
    {
        $this->sphinx->SetLimits (($page-1)*$pageSize, $pageSize, 1000,0);
        $res = $this->sphinx->query($this->keyword,"{$table}:{$table}_delta"); #1 关键字,*是所有数据源source
        if(!array_key_exists($table,$this->tableArr)
            || $this->keyword==null
            || $res==false
            ||empty($res['matches'])){
            return false;
        }
        $this->total_found=$res['total_found'];
        $ids=join (',',array_keys($res['matches']));
        if(empty($ids)){return FALSE;}
        $result=Db::table($table)->where(['id'=>['in',$ids]])->select()->toArray();
        $result=$this->$table($result,$table);
        $data=[];
        $pageConfig=['type' => 'bootstrap','var_page' =>'page','list_rows' => 15,'path'=>'/'.$path ];
        $pageConfig['query']['word']=$this->keyword;
        $pageObj=Bootstrap::make($result, $pageSize, $page, $res['total_found'], false, $pageConfig);
        $data['data']=$result;
        $data['page']=$pageObj->render();
        $data['num']=$res['total_found'];
        return $data;


    }

    public function scws(string $string)
    {
        $word=[];
        if (config('scws.start') && extension_loaded("scws")) {
            //实例化分词插件核心类
            $so = scws_new();
            //设置分词时所用编码
            $so->set_charset('utf-8');
            //设置分词所用词典(此处使用utf8的词典)
            $so->set_dict('/usr/local/scws/etc/dict.utf8.xdb');
            //设置分词所用规则
            $so->set_rule('/usr/local/scws/etc/rules.utf8.ini');
            //分词前去掉标点符号
            $so->set_ignore(TRUE);
            //是否复式分割,如“中国人”返回“中国+人+中国人”三个词。
            $so->set_multi(TRUE);
            //设定将文字自动以二字分词法聚合
            $so->set_duality(TRUE);
            //要进行分词的语句
            $so->send_text($string);
            //获取分词结果,如果提取高频词用get_tops方法
            $word=[];
            while ($tmp = $so->get_result()) {
                foreach ($tmp as $k=>$v){
                    array_push($word,$v['word']);
                }

            }

            $so->close();
        }
        if(empty($word)){
            $word=[$string];
        }
        return $word;
    }


    /**
     * 课程索引处理结果
     * @param $data
     * @param $table
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function wk_courses($data,$table)
    {
        $teacher=[];
        if(!empty($data)){
            $teacher_ids=array_column($data,'teacher_id');
            $tmp=Db::name('teacher')->alias('t')
                ->join('wk_user_info u','t.user_id=u.pid','LEFT')
                ->where(['t.id'=>['in',join(',',$teacher_ids)]])->field('u.nickname,t.id')
                ->select()->toArray();
            $teacher=array_combine(array_column($tmp,'id'),array_column($tmp,'nickname'));
        }
        $word=$this->scws($this->keyword);

        foreach ($data as $key=>$val){
            foreach ($val as $k=>$v){
                if($k=='title'){
                    $str=cut(strip_tags( myTrim($val[$k])),185);
                    $data[$key][$k]=$this->replace_keyword($str,$word);
                }
                if($k=='content'){
                    $str=cut(strip_tags( myTrim($val[$k])),175);
                    $data[$key][$k]=$this->replace_keyword($str,$word);

                }

                if($k=='create_time'){
                    $data[$key]['create_time']=tranTime($v);
                }
                if($k=='teacher_id'){
                    if(array_key_exists($v,$teacher)){
                        $data[$key][$k]=$teacher[$v];
                    }else{$data[$key][$k]="未知";}
                }
            }
        }

        return $data;
    }


    /**
     *
     * @param $data
     * @param $table
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function wk_article($data,$table)
    {
        $userInfo=$userAvatar=[];
        if(!empty($data)){
            $user_ids=array_column($data,'user_id');
            $tmp=Db::name('user_info')
                ->where(['pid'=>['in',join(',',$user_ids)]])->field('nickname,pid,avatar')
                ->select()->toArray();
            $userInfo=array_combine(array_column($tmp,'pid'),array_column($tmp,'nickname'));
            $userAvatar=array_combine(array_column($tmp,'pid'),array_column($tmp,'avatar'));
        }
        $word=$this->scws($this->keyword);
        foreach ($data as $key=>$val){
            foreach ($val as $k=>$v){
                if($k=='title'){
                    $str=cut(strip_tags( myTrim($val[$k])),185);
                    $data[$key][$k]=$this->replace_keyword($str,$word);
                }
                if($k=='content'){
                    $str=cut(strip_tags( myTrim($val[$k])),185);
                    $data[$key][$k]=$this->replace_keyword($str,$word);
                }

                if($k=='create_time'){
                    $data[$key]['create_time']=tranTime($v);
                }
                if($k=='user_id'){
                    if(array_key_exists($v,$userInfo)){
                        $data[$key]['nickname']=$userInfo[$v];
                    }else{$data[$key]['nickname']="未知";}
                    if(array_key_exists($v,$userAvatar)){
                        $data[$key]['avatar']=$userAvatar[$v];
                    }else{$data[$key]['avatar']="未知";}
                }

            }
        }
        return $data;
    }


    /**
     *
     * @param $data
     * @param $table
     * @return mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public function wk_wenda_post($data,$table)
    {
        $reply_ids=$replyData=$userIds=[];
        if(!empty($data)){
            $ids=array_combine(array_column($data,'id'),array_column($data,'reply_status'));
            foreach ($ids as $k=>$v){
                if($ids[$k]==1){
                    array_push($reply_ids,$k);
                }
            }

            if(!empty($reply_ids)){
                foreach ($reply_ids as $v){
                        $tmpData=Db::query("select * from wk_wenda_reply where "
                    ."id=(select id from wk_wenda_reply where ".
                    " pid=? order by `status` desc,id desc limit 1);",[$v]);
                        $tmp=$tmpData[0];
                        foreach ($tmp as $key=>$val){
                            if($key=='content'){
                                $tmp[$key]=str_ireplace($this->keyword,"<b>{$this->keyword}</b>",
                                    cut(strip_tags( myTrim($tmp[$key])),185)
                                );
                            }
                        }
                $replyData[$tmp['pid']]=$tmp;
                    array_push($userIds,$tmp['uid']);
                }
                if(!empty($userIds)){
                    $userData=Db::name('user_info')->where(['pid'=>['in',join(',',$userIds)]])
                        ->field('pid,avatar,nickname')->select()->toArray();
                    foreach ($replyData as $k=>$v){
                        foreach ($userData as $kk=>$vv){
                            if($v['uid']==$vv['pid']){
                                $replyData[$k]['nickname']=$vv['nickname'];
                                $replyData[$k]['avatar']=$vv['avatar'];
                            }
                        }
                    }
                }
            }
        }
        $word=$this->scws($this->keyword);
        foreach ($data as $key=>$val){
            foreach ($val as $k=>$v){
                if($k=='title'){
                    $str=cut(strip_tags( myTrim($val[$k])),185);
                    $data[$key][$k]=$this->replace_keyword($str,$word);
                }

                if($k=='content'){
                    $str=cut(strip_tags( myTrim($val[$k])),185);
                    $data[$key][$k]=$this->replace_keyword($str,$word);
                }

                if($k=='create_time'){
                    $data[$key]['create_time']=tranTime($v);
                }
                if($k=='id'){
                    if(array_key_exists($v,$replyData)){
                        $data[$key]['children']=$replyData[$v];
                    }else{
                        $data[$key]['children']="";
                    }
                }
            }
        }
        return $data;
    }


    /**
     * 替换关键词
     * @param $str
     * @param array $word
     * @return mixed
     */
    public function replace_keyword($str,array $word)
    {
        foreach ($replace=range(1,count($word)) as $k=>$v) {
            $replace[$k]="<b>$word[$k]</b>";
        }
        return     str_ireplace($word,$replace,$str);

    }



    private function remark()
    {
//        $link=mysqli_connect("127.0.0.1","wk","wk123456");
//        mysqli_select_db($link,'wk');
//        $sql="select * from {$table} where id in({$ids})";
//        $rst=mysqli_query($link,$sql);
//        $data=[];
//        while($row=mysqli_fetch_assoc($rst)){
//            $tmp=[];
//            $tmpData=$this->sphinx->BuildExcerpts($row, $table, $this->keyword, $opts);
//            foreach ($tmpData as $k=>$v){
//                $tmp[$fieldArr[$k]]=$v;
//            }
//            $tmp['db_type']=$this->tableArr[$table];
//            $data[]=$tmp;
//        }
//        mysqli_close($link);
    }


}

文章参考地址:https://blog.csdn.net/phphub/article/details/52325094

最后修改:2020 年 01 月 05 日
如果觉得我的文章对你有用,请随意赞赏