如何通过PHP优化搜索功能,实现部分匹配、大小写不敏感及拼写容错查询
如何通过PHP优化搜索功能,实现部分匹配、大小写不敏感及拼写容错查询
我完全懂你的痛点——现在这个搜索太"死板"了:必须整个字符串匹配、大小写错一点或者拼漏一个字母就查无结果,这和用户日常用的搜索体验差太远了。下面我就用PHP+PDO一步步优化你的代码,解决这些问题,完全贴合你的需求。
一、先搞定核心需求:大小写不敏感+分词模糊匹配
你当前的代码是把整个搜索词当成一个整体做模糊匹配,比如搜"book 1: conquest",只会找包含这个完整短语的记录,而用户实际想要的是只要包含"book"、"1"、"conquest"任意一个(或者全部)关键词的结果,同时大小写不影响。
修改后的基础优化代码
<?php define("DB_HOST", "localhost"); define("DB_NAME", "officiallasereyeswebsite"); define("DB_CHARSET", "utf8"); define("DB_USER", "root"); define("DB_PASSWORD", ""); // 1. 预处理搜索词:清理、拆分单个关键词 $search = isset($_GET['search']) ? trim($_GET['search']) : ''; // 去掉多余空格,拆分关键词,过滤空值 $keywords = array_filter(preg_split('/\s+/', $search)); $pdo = new PDO( "mysql:host=".DB_HOST.";charset=".DB_CHARSET.";dbname=".DB_NAME, DB_USER, DB_PASSWORD, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); $results = []; if (!empty($keywords)) { // 2. 动态构建SQL条件:每个关键词对应一个模糊匹配规则 $conditions = []; $params = []; foreach ($keywords as $keyword) { // 统一转小写,彻底解决大小写敏感问题 $lowerKeyword = strtolower($keyword); // 对item和code字段分别做模糊匹配 $conditions[] = "(LOWER(`item`) LIKE ? OR LOWER(`code`) LIKE ?)"; $params[] = "%{$lowerKeyword}%"; $params[] = "%{$lowerKeyword}%"; } // 用AND连接=必须包含所有关键词;用OR连接=包含任意关键词,按需选 $sql = "SELECT * FROM `items` WHERE " . implode(' AND ', $conditions); $stmt = $pdo->prepare($sql); $stmt->execute($params); $results = $stmt->fetchAll(); } // 返回AJAX结果 if (isset($_GET["ajax"])) { echo json_encode($results); } ?>
这段代码的关键改进点
- 分词处理:把搜索词拆成单个关键词,比如"book 1: conquest"会拆成
["book", "1:", "conquest"],每个关键词单独匹配数据库字段 - 大小写完全不敏感:用
LOWER()把数据库字段和搜索词都转成小写,不管你的数据库 collation 是啥,都能保证大小写不影响结果 - 灵活的匹配规则:可以自由切换
AND/OR——用AND就是必须包含所有关键词,用OR就是包含任意关键词就显示,完全贴合你的业务需求
二、实现拼写容错:PHP端用编辑距离过滤结果
如果用户拼错单词(比如把"conquest"写成"conqest"),我们可以用PHP自带的levenshtein()函数计算两个字符串的"编辑距离"(也就是需要增、删、改几个字符才能让两个字符串一样),保留差异小的结果。
带拼写容错的完整代码
<?php define("DB_HOST", "localhost"); define("DB_NAME", "officiallasereyeswebsite"); define("DB_CHARSET", "utf8"); define("DB_USER", "root"); define("DB_PASSWORD", ""); $search = isset($_GET['search']) ? trim($_GET['search']) : ''; // 预处理:去掉标点,拆分关键词 $cleanSearch = preg_replace('/[^\w\s]/u', '', $search); $keywords = array_filter(preg_split('/\s+/', $cleanSearch)); $pdo = new PDO( "mysql:host=".DB_HOST.";charset=".DB_CHARSET.";dbname=".DB_NAME, DB_USER, DB_PASSWORD, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); $rawResults = []; if (!empty($keywords)) { // 先拿候选结果:放宽SQL匹配条件,比如匹配关键词的前缀,避免漏过相似结果 $conditions = []; $params = []; foreach ($keywords as $keyword) { $lowerKeyword = strtolower($keyword); // 取关键词的前n-1个字符做匹配,比如"conqest"就匹配包含"conqe"的记录 $shortKeyword = substr($lowerKeyword, 0, max(2, strlen($lowerKeyword)-1)); $conditions[] = "(LOWER(`item`) LIKE ? OR LOWER(`code`) LIKE ?)"; $params[] = "%{$shortKeyword}%"; $params[] = "%{$shortKeyword}%"; } // 用OR连接,拿到所有可能的候选结果 $sql = "SELECT * FROM `items` WHERE " . implode(' OR ', $conditions); $stmt = $pdo->prepare($sql); $stmt->execute($params); $rawResults = $stmt->fetchAll(); } // 2. PHP端用编辑距离过滤,保留拼写相似的结果 $results = []; $maxEditDistance = 2; // 允许的最大差异:2意味着最多2个字符的错误,按需调整 foreach ($rawResults as $item) { $isMatch = false; // 合并item和code字段,统一做匹配 $itemContent = strtolower($item['item'] . ' ' . $item['code']); foreach ($keywords as $keyword) { $lowerKeyword = strtolower($keyword); $minDistance = PHP_INT_MAX; // 遍历内容的子串,找到和关键词最相似的部分 $keywordLen = strlen($lowerKeyword); for ($i = 0; $i <= strlen($itemContent) - $keywordLen; $i++) { $subStr = substr($itemContent, $i, $keywordLen); $distance = levenshtein($lowerKeyword, $subStr); if ($distance < $minDistance) { $minDistance = $distance; } } // 如果编辑距离在阈值内,就认为匹配成功 if ($minDistance <= $maxEditDistance) { $isMatch = true; break; } } if ($isMatch) { $results[] = $item; } } // 返回AJAX结果 if (isset($_GET["ajax"])) { echo json_encode($results); } ?>
拼写容错的逻辑说明
- 先拿候选结果:通过SQL匹配关键词的前缀(比如去掉最后一个字符),先把所有可能的相似结果捞出来,避免PHP端处理全表数据
- 编辑距离过滤:用
levenshtein()计算关键词和结果内容的差异度,只保留差异在2个字符以内的结果(这个阈值你可以自己调,越大容错越高) - 额外预处理:加了去掉标点的逻辑,比如把"1:"变成"1",让搜索更智能
三、额外优化小技巧
- 智能清理搜索词:可以用
preg_replace('/[^\w\s]/u', '', $search)去掉所有非文字和空格的字符,让关键词更干净 - 性能优化:如果你的数据量很大,PHP端的编辑距离过滤会有点慢,可以先让MySQL用全文索引(
MATCH AGAINST)缩小范围,再在PHP端做二次过滤 - 结果排序:可以根据编辑距离给结果排序,把最相似的结果放在最前面
内容来源于stack exchange
相关产品推荐
相关产品推荐

