搜尋介面優化方案——冪集分詞表
阿新 • • 發佈:2020-08-28
前言:
在業務開發中,有一些面向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===========");
}
}
4. 搜尋功能方法示例。
/**
* @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;
}