搜索接口优化方案 —— 幂集分词表

前言

在业务开发中,有一些面向 C 端的搜索接口。比如根据商品名搜索,之前也提过通过 ES 解决,但这次主要以一种较为简单快捷的方式优化搜索速度。

思路

1. 通过程序将所有商品名进行分词并幂集组合。
2. 将所有商品名称幂集存入一张单独的表中。
3. 每个分词对应一个商品 ID。
4. 搜索关键词对幂集表的分词模糊查询并取出商品 ID。
5. 对应的商品 ID 就是搜索出来的商品。

编码

1. 数组元素的幂集函数。

if (!function_exists('powerSet')) {

    function powerSet($array)
    {

        $results = array(array());

        foreach ($array as $element) {

            foreach ($results as $combination) {

                $results[] = array_merge(array($element), $combination);

            }

        }

        return array_values($results);
    }
}

2. 搜索分词表创建。

字段名 类型 描述
id int 主键 ID
goods_id int 商品编号
search_path varchar 分词字符

3. 生成搜索词数据命令,以后可通过 php think make:name 执行。

<?php
namespace app\common\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;

use think\Db;

class MakeName extends Command{

    protected function configure(){

        $this->setName('make:name');
    }

    protected function execute(Input $input, Output $output){

        ini_set ('memory_limit', '512M');

        $output->info("===========BEGIN===========");

        $goods = Db::name("goods")->where("class_id",2)->cursor();

        foreach ($goods as  $goods_value) {

            $output->info("===========".$goods_value['goods_name']."===========");

            $goods_name = strtolower($goods_value['goods_name']);

            $name_array = explode(" ", $goods_name);

            $list = powerSet($name_array);

            $path = [];

            foreach ($list as  $value) {

                if(!count($value)) continue;

                $temp = array_reverse($value);

                $data['goods_id'] = $goods_value['id'];
                $data['search_path'] = implode("",$temp);
                $path[] = $data;
                $output->writeln($data['search_path']);
                unset($data);
            }

            Db::name("search")->where("goods_id",$goods_value['id'])->delete();

            Db::name("search")->insertAll($path);

            unset($path);
        }

        $output->info("===========END===========");
    }
}
  1. 搜索功能方法示例。
/**
 * @Notes:
 * @param string $keyWord 搜索关键字
 * @param int $page 页码
 * @param int $size 每页数量
 * @return array
 */
public function search($keyWord = '', $page = 1, $size = 10)
{
    $where = [];
    $where[] = $keyWord . '%';
    $where[] = str_replace(" ", "", $keyWord) . '%';
    $keyArr = explode(" ", $keyWord);
    foreach ($keyArr as $value) {
        $where[] = trim($value) . '%';
    }

    $ids = Db::name("search")->where("search_path", "like", $where, "OR")->column("goods_id");

    $ids = array_unique($ids);

    $goods = 0;
    $goods = Db::name("goods")->where("state", 1)->where("goods_name", "=", $keyWord)->value("id");
    if ($goods && $page == 1) {

        $key = array_search($goods, $ids);
        unset($ids[$key]);
    }

    $map[] = ['state', '=', 1];
    $map[] = ['delete_time', '=', 0];
    $map[] = ['id', 'in', $ids];

    $res = self::where($map)->field("id,goods_name")->order(['sort' => 'desc', 'id' => 'desc'])->page($page, $size)->select()->toArray();

    if ($goods && $page == 1) {
        if ($res) {
            $first = $res[0];
            $res[0] = ['id' => $goods, 'goods_name' => $keyWord];
            $res[] = $first;
        } else {
            $res[0] = ['id' => $goods, 'goods_name' => $keyWord];
        }

    }
    return $res;
}

php
本作品采用《CC 协议》,转载必须注明作者和本文链接
公众号: ZERO开发, 专注后端实战技术分享,致力于给猿友们提供有价值的内容。
北桥苏
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!