sphinx(九)根据关键词相似度排序

408 阅读2分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

全文检索通过sphinx搜索出来的内容是没有问题的。

但是搜索结束之后,文章的排序还是多少有点小问题,是这样,我最开始是使用时间倒叙排序,这样就会有一个小问题就是,我想要的结果,或者说跟我搜索关键词最贴近的结果不会出现在前几条。这个体验很不好。

然后,我这里使用了PHP内置的similar_text函数对文章的描述以及标题进行相似度的计算,然后根据计算之后的相似度数值进行倒叙排序。

我这里封装了一个函数:仅作示例,具体还是需要根据你自己的需求来

function similar_arr($array, $keyword, $arr_key = 'title')
{
    //数组相似度处理
    foreach ($array as $key => $value) 
    {
        similar_text($value[$arr_key], $keyword, $percent);
        $value['percent'] = $percent;
        $data[] = $value;
         
    }
 
    //取出数组中percent的一列,返回一维数组
    $percent =  array_column($data, 'percent');
 
    //排序,根据 percent 排序
    array_multisort($percent, SORT_DESC, $data);
    
    return $data;
}

// $data是一个二维数组
$res = similar_arr($data, '微信小程序');
var_dump($res);

这个是没有问题的,但是其对中文的相似度计算不是很友好。有点瞎算的感觉。

这可怎么办呢?也不能用这玩意啊。

百度上还是大婶多,我这里找到了一个计算中文相似度的一个类,我这里稍加改动了一下:

整体如下所示:

Lcscontroller.php

<?php
 
namespace App\Http\Controllers\index;
 
/**
 * @name: 文章相似度计算类
 * @author: camellia
 * @date: 2021-03-04 
 */
class LcsController extends BaseController
{
    private $str1;
    private $str2;
    private $c = array();
    /**
     * @name: 返回串一和串二的最长公共子序列
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function getLCS($str1, $str2, $len1 = 0, $len2 = 0)
    {
        $this->str1 = $str1;
        $this->str2 = $str2;
        if ($len1 == 0) $len1 = strlen($str1);
        if ($len2 == 0) $len2 = strlen($str2);
        $this->initC($len1, $len2);
        return $this->printLCS($this->c, $len1 - 1, $len2 - 1);
    }
    /**
     * @name: 返回两个串的相似度
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function getSimilar($str1, $str2)
    {
        $len1 = strlen($str1);
        $len2 = strlen($str2);
        $len = strlen($this->getLCS($str1, $str2, $len1, $len2));
        if(($len1 + $len2) > 0)
        {
            return $len * 2 / ($len1 + $len2);
        }
        else
        {
            return 0;
        }
        
    }
    /**
     * @name: 函数名
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function initC($len1, $len2)
    {
        for ($i = 0; $i < $len1; $i++) 
        {
            $this->c[$i][0] = 0;
        }
        for ($j = 0; $j < $len2; $j++) 
        {
            $this->c[0][$j] = 0;
        }
        for ($i = 1; $i < $len1; $i++) 
        {
            for ($j = 1; $j < $len2; $j++) 
            {
                if ($this->str1[$i] == $this->str2[$j]) 
                {
                    $this->c[$i][$j] = $this->c[$i - 1][$j - 1] + 1;
                } 
                else if ($this->c[$i - 1][$j] >= $this->c[$i][$j - 1]) 
                {
                    $this->c[$i][$j] = $this->c[$i - 1][$j];
                } 
                else 
                {
                    $this->c[$i][$j] = $this->c[$i][$j - 1];
                }
            }
        }
    }
    /**
     * @name: 函数名
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function printLCS($c, $i, $j)
    {
        if($i < 0 || $j < 0)
        {
            return "";
        }
        if ($i == 0 || $j == 0) 
        {
            if ($this->str1[$i] == $this->str2[$j]) 
            {
                return $this->str2[$j];
            }
            else 
            {
                return "";
            }
        }
        if ($this->str1[$i] == $this->str2[$j]) 
        {
            return $this->printLCS($this->c, $i - 1, $j - 1) . $this->str2[$j];
        }
        else if ($this->c[$i - 1][$j] >= $this->c[$i][$j - 1]) 
        {
            return $this->printLCS($this->c, $i - 1, $j);
        } 
        else 
        {
            return $this->printLCS($this->c, $i, $j - 1);
        }
    }
}

调用:

 /**
     * @name: 根据相似度对数组进行排序
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function similar_arr($array, $keyword, $arr_key_one = 'arttitle', $arr_key_two='content', $arr_key_three= 'artdesc')
    {
        $lcs = new LcsController();
 
        //数组相似度处理
        foreach ($array as $key => $value) {
            // similar_text函数对中文相似度处理的不是很友好
            // similar_text($value[$arr_key], $keyword, $percent);
            $title_percent = $lcs->getSimilar($value[$arr_key_one], $keyword);
            //返回最长公共子序列
            //echo $lcs->getLCS("hello word","hello china");
            $value['title_percent'] = $title_percent;
            // $content_percent = $lcs->getSimilar($value[$arr_key_two], $keyword);
            // $value['content_percent'] = $content_percent;
            $desc_percent = $lcs->getSimilar($value[$arr_key_three], $keyword);
            $value['desc_percent'] = $desc_percent;
            $data[] = $value;
        }
 
        //取出数组中percent的一列,返回一维数组
        // $percent = array_column($data, 'percent');
        //排序,根据 percent 排序
        // array_multisort($percent, SORT_DESC, $data);
        // $array = $this->sortArrByManyField($data, 'title_percent', SORT_DESC, 'content_percent', SORT_DESC, 'desc_percent', SORT_DESC);
        $array = $this->sortArrByManyField($data, 'title_percent',SORT_DESC, 'id', SORT_DESC, 'desc_percent', SORT_DESC );
        return $array;
    }
    /**
     * @name: php二维数组根据多个字段排序
     * @author: camellia
     * @date: 2021-03-04 
     * @param:  data    type    description
     * @return: data    type    description
     */
    public function sortArrByManyField()
    {
        $args = func_get_args(); // 获取函数的参数的数组
        if(empty($args))
        {
            return null;
        }
        $arr = array_shift($args);
        if(!is_array($arr))
        {
            throw new Exception("第一个参数不为数组");
        }
        foreach($args as $key => $field)
        {
            if(is_string($field)){
            $temp = array();
            foreach($arr as $index=> $val){
                $temp[$index] = $val[$field];
            }
            $args[$key] = $temp;
            }
        }
        $args[] = &$arr;//引用值
        call_user_func_array('array_multisort',$args);
        return array_pop($args);
    }

调用计算相似度方法

 $listShow = $this->similar_arr($list, $search, 'arttitle');

这个最后的计算出来的相似度其实也不是特别的精确,但是,要比PHP内置的similar_text函数要好。

具体实现的效果,请访问我的个人博客:guanchao.site

有好的建议,请在下方输入你的评论。

欢迎访问个人博客 guanchao.site

欢迎访问小程序:

在这里插入图片描述