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

如何实现实用的网站站内搜索?酒店名称模糊搜索优化求助

针对用户友好型酒店搜索的优化方案

一、解决前缀匹配问题(如"taj mum"匹配"Taj Wellington Mews Mumbai")

你的核心问题是当前布尔模式全文搜索仅支持完整词匹配,需要调整为前缀模糊匹配,同时修复停用词处理的逻辑bug:

1. 修改搜索词生成逻辑

把每个关键词改为+关键词*格式,这样数据库会匹配以该关键词开头的所有词汇;同时修复之前遇到停用词直接终止循环的错误,改为跳过停用词:

$q = sanitize($_POST['search']);
// 用正则分割所有非单词字符,适配含连字符/特殊符号的输入
$qParts = preg_split('/\W+/', trim($q));
$fullTextQuery = '';
$filterWords = [
    'the', 'in', 'of', 'from', 'and', 'a', 'an'
];

foreach ($qParts as $key) {
    $keyLower = strtolower($key);
    // 跳过空字符串和停用词
    if(empty($key) || in_array($keyLower, $filterWords)) {
        continue;
    }
    // 添加前缀通配符,支持部分匹配
    $fullTextQuery .= '+'.$key.'* ';
}

// 处理空搜索场景
if(empty($fullTextQuery)) {
    $searchQuery = "SELECT name, link FROM hotels WHERE status = 2 LIMIT 10";
} else {
    // 增加相关性排序,让匹配度高的结果排在前面
    $searchQuery = "SELECT name, link, MATCH(name) AGAINST ('$fullTextQuery' IN BOOLEAN MODE) AS relevance 
                    FROM hotels 
                    WHERE MATCH(name) AGAINST ('$fullTextQuery' IN BOOLEAN MODE) AND status = 2
                    ORDER BY relevance DESC";
}

2. 调整MySQL全文索引配置

MySQL默认全文索引的最小词长是4,如果你需要支持2-3个字符的前缀(如"mum"),需要修改配置:

  • 编辑my.cnf(或my.ini)文件,添加/修改:
    ft_min_word_len=2
    
  • 重启MySQL服务
  • 重建全文索引:
    ALTER TABLE hotels DROP INDEX ft_name; -- 替换为你的全文索引名
    ALTER TABLE hotels ADD FULLTEXT INDEX ft_name (name);
    

二、增强子词匹配能力(可选,适配更灵活的输入)

如果用户可能输入中间片段(如"wellington"的"well"),可以使用MySQL的n-gram全文索引(MySQL 5.7+ / MariaDB支持):

  1. 创建n-gram索引:
    ALTER TABLE hotels ADD FULLTEXT INDEX ft_name_ngram (name) WITH PARSER ngram;
    
  2. 搜索时直接使用自然语言模式,无需手动添加通配符:
    SELECT name, link, MATCH(name) AGAINST ('taj mum' IN NATURAL LANGUAGE MODE) AS relevance
    FROM hotels
    WHERE MATCH(name) AGAINST ('taj mum' IN NATURAL LANGUAGE MODE) AND status = 2
    ORDER BY relevance DESC;
    

n-gram会自动将词汇拆分为2个字符的片段(可通过ngram_token_size调整),能匹配任意位置的子词。

三、处理拼写错误问题

对于用户的拼写错误(如"taj mumm"),可以通过编辑距离函数匹配相似词汇:

  1. 先创建MySQL自定义编辑距离函数:
    DELIMITER $$
    CREATE FUNCTION LEVENSHTEIN(str1 VARCHAR(255), str2 VARCHAR(255)) 
    RETURNS INT
    DETERMINISTIC
    BEGIN
        DECLARE len1, len2, i, j, cost, min_cost INT;
        DECLARE str1_char CHAR(1);
        SET len1 = CHAR_LENGTH(str1);
        SET len2 = CHAR_LENGTH(str2);
        IF len1 = 0 THEN RETURN len2; END IF;
        IF len2 = 0 THEN RETURN len1; END IF;
        CREATE TEMPORARY TABLE IF NOT EXISTS levenshtein (id INT AUTO_INCREMENT PRIMARY KEY, val INT NOT NULL);
        INSERT INTO levenshtein (val) VALUES (0);
        FOR i FROM 1 TO len1 DO INSERT INTO levenshtein (val) VALUES (i); END FOR;
        FOR j FROM 1 TO len2 DO
            SET str1_char = SUBSTRING(str2, j, 1);
            SET min_cost = j;
            FOR i FROM 1 TO len1 DO
                SET cost = IF(SUBSTRING(str1, i, 1) = str1_char, 0, 1);
                SET min_cost = LEAST((SELECT val FROM levenshtein WHERE id = i + 1) + 1, min_cost + 1, (SELECT val FROM levenshtein WHERE id = i) + cost);
                UPDATE levenshtein SET val = min_cost WHERE id = i;
            END FOR;
            UPDATE levenshtein SET val = j + 1 WHERE id = len1 + 1;
        END FOR;
        SET min_cost = (SELECT val FROM levenshtein WHERE id = len1 + 1);
        DROP TEMPORARY TABLE IF EXISTS levenshtein;
        RETURN min_cost;
    END$$
    DELIMITER ;
    
  2. 在代码中添加降级逻辑:如果前缀搜索无结果,就用编辑距离查找相似词汇:
    // 先执行前缀搜索
    $result = mysqli_query($conn, $searchQuery);
    if(mysqli_num_rows($result) == 0) {
        // 生成基于编辑距离的查询
        $editQuery = "SELECT name, link, LEVENSHTEIN(name, '$q') AS distance 
                      FROM hotels 
                      WHERE status = 2 
                      ORDER BY distance ASC LIMIT 10";
        $result = mysqli_query($conn, $editQuery);
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:04:55