You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel搜索功能优化:拼写容错查询问题解决方案咨询

嘿,这个问题太常见了——用户搜东西时手滑打错字简直是日常操作,咱们得让搜索功能更“懂人”一点才行。下面给你几个在Laravel里实现容错拼写匹配的方案,从简单易用到进阶优化都有:

方案1:用SQL的发音匹配函数(快速上手)

SQL自带的SOUNDEX和DIFFERENCE函数可以基于发音来匹配字符串,刚好能解决“拼写错但发音像”的问题:

  • SOUNDEX会把字符串转换成代表发音的编码
  • DIFFERENCE对比两个字符串的SOUNDEX编码,返回0-4的数值,4代表发音几乎完全一致

你可以直接修改现有的查询,保留原有模糊匹配的同时,加上发音匹配的条件:

use DB;
public function search(Request $request){
    $query = $request->input('query');
    $products = DB::table('products')
        // 保留原有的模糊匹配逻辑
        ->where('product_title', 'like', '%'.$query.'%')
        // 新增发音匹配:允许相似度≥3(可根据需求调整)
        ->orWhereRaw("DIFFERENCE(product_title, ?) >= 3", [$query])
        ->get();
}

这样像deemoo和Demo、prooductt和Product这类发音相近的错误拼写,就能匹配到目标结果了。

方案2:MySQL全文搜索(适合大数据量场景)

如果你的产品数据比较多,LIKE和发音函数的性能会越来越差,这时候推荐用MySQL的全文搜索功能,它不仅支持拼写容错,还能处理语义相关性,性能也更优。

第一步:给字段创建全文索引

先给products表的product_title字段创建全文索引,可以通过迁移文件或者手动执行SQL:

ALTER TABLE products ADD FULLTEXT INDEX ft_product_title (product_title);

第二步:在Laravel中使用全文搜索

Laravel 5.6+原生支持全文搜索,你可以用Eloquent模型或者DB门面来实现:

// 用DB门面的写法
$products = DB::table('products')
    ->whereRaw("MATCH(product_title) AGAINST(? IN NATURAL LANGUAGE MODE)", [$query])
    ->get();

// 如果用Product模型的写法更简洁
$products = \App\Models\Product::whereFullText('product_title', $query)->get();

全文搜索会自动处理拼写相近的词汇,而且查询效率比LIKE高很多,数据量大的话优先选这个方案。

方案3:基于编辑距离的精准匹配(自定义容错程度)

如果需要精确控制允许多少个拼写错误(比如最多允许2个字符的差异),可以用Levenshtein距离——它计算的是两个字符串之间需要多少次插入、删除或替换操作才能完全一致,数值越小越相似。

注意:部分数据库默认没有这个函数

如果你的MySQL没有LEVENSHTEIN函数,先手动创建它:

DELIMITER //
CREATE FUNCTION LEVENSHTEIN(s1 VARCHAR(255), s2 VARCHAR(255))
RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE s1_len, s2_len, i, j, c, c_temp INT;
    DECLARE s1_char CHAR;
    DECLARE cv0, cv1 VARBINARY(256);
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2);
    IF s1_len = 0 THEN RETURN s2_len; END IF;
    IF s2_len = 0 THEN RETURN s1_len; END IF;
    SET cv0 = 0x00;
    FOR i FROM 1 TO s2_len DO
        SET cv0 = CONCAT(cv0, UNHEX(HEX(i)));
    END FOR;
    FOR i FROM 1 TO s1_len DO
        SET s1_char = SUBSTRING(s1, i, 1), cv1 = UNHEX(HEX(i)), j = 1;
        FOR j FROM 1 TO s2_len DO
            SET c = IF(s1_char = SUBSTRING(s2, j, 1), 0, 1);
            SET c_temp = CONV(HEX(SUBSTRING(cv0, j, 1)), 16, 10) + c;
            IF CONV(HEX(SUBSTRING(cv1, j-1, 1)), 16, 10) + 1 < c_temp THEN
                SET c_temp = CONV(HEX(SUBSTRING(cv1, j-1, 1)), 16, 10) + 1;
            END IF;
            IF CONV(HEX(SUBSTRING(cv0, j+1, 1)), 16, 10) + 1 < c_temp THEN
                SET c_temp = CONV(HEX(SUBSTRING(cv0, j+1, 1)), 16, 10) + 1;
            END IF;
            SET cv1 = CONCAT(cv1, UNHEX(HEX(c_temp)));
        END FOR;
        SET cv0 = cv1;
    END FOR;
    RETURN CONV(HEX(SUBSTRING(cv0, s2_len+1, 1)), 16, 10);
END //
DELIMITER ;

然后在Laravel中使用

use DB;
public function search(Request $request){
    $query = $request->input('query');
    $products = DB::table('products')
        ->select('*', DB::raw('LEVENSHTEIN(product_title, ?) as distance', [$query]))
        // 允许最多2个字符的错误(可根据需求调整)
        ->having('distance', '<=', 2)
        // 把最相似的结果排在前面
        ->orderBy('distance')
        ->get();
}

根据你的需求选就行——如果只是小体量数据,方案1最快;数据多的话方案2性能最优;要是想精确控制允许多少个拼写错误,方案3更灵活。

内容的提问来源于stack exchange,提问作者styloz ashik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:12:37