一、问题背景
Z-Blog PHP 默认的 'search' 查询类型在底层数据库类中,会将用户输入的搜索词按 UTF-8 字符逐字拆分。具体表现为:
中文搜索"心脏支架" → 拆成:心、脏、支、架(分别匹配)
英文搜索"FDA" → 拆成:F、D、A(分别匹配)
搜索"FDA approval" → 拆成:F、D、A、a、p、p...(极泛化)
这种逻辑使用 OR 关系连接,导致只要文章包含搜索词中任意一个字符或字母就会被匹配,无法实现精准搜索。
二、优化原理
将搜索逻辑改为混合拆分:
中文片段:按单字拆分,逐字匹配(保持中文搜索习惯)
英文/数字/符号片段:按空格拆分为单词,整词匹配(不再拆成单个字母)
匹配关系:所有拆分后的单元使用 AND 关系,必须同时满足才返回结果
三、操作步骤
步骤 1:备份核心文件
cp /www/wwwroot/wangzhanwww/zb_system/function/c_system_route.php \ /www/wwwroot/wangzhanwww/zb_system/function/c_system_route.php.bak
步骤 2:修改搜索查询逻辑
编辑 zb_system/function/c_system_route.php,找到 ViewSearch 函数内的以下代码(约第 355 行):
// 原代码
if ($q) {
$w[] = array('search', 'log_Content', 'log_Intro', 'log_Title', $q);
} else {
$w[] = array('=', 'log_ID', 0);
}将其整体替换为以下混合拆分逻辑:
if ($q) {
$escaped_q = $zbp->db->EscapeString($q);
// 按空格分割成多个片段(英文单词以空格分隔)
$segments = preg_split('/\s+/', $escaped_q, -1, PREG_SPLIT_NO_EMPTY);
$conditions = array();
foreach ($segments as $seg) {
// 判断是否包含中文字符
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $seg)) {
// 中文片段:逐字拆分,每个字单独匹配
$chars = preg_split('//u', $seg, -1, PREG_SPLIT_NO_EMPTY);
foreach ($chars as $char) {
$char = $zbp->db->EscapeString($char);
$conditions[] = "(log_Title LIKE '%{$char}%' OR log_Intro LIKE '%{$char}%' OR log_Content LIKE '%{$char}%')";
}
} else {
// 纯英文/数字/符号片段:整体作为单词匹配,不再拆字母
$conditions[] = "(log_Title LIKE '%{$seg}%' OR log_Intro LIKE '%{$seg}%' OR log_Content LIKE '%{$seg}%')";
}
}
if (!empty($conditions)) {
// AND 关系:所有拆分单元必须同时满足(更精准)
$w[] = array('CUSTOM', implode(' AND ', $conditions));
}
} else {
$w[] = array('=', 'log_ID', 0);
}步骤 3:保存并验证
保存文件后,在网站前台进行搜索测试:
搜索"FDA" → 应只返回包含完整"FDA"词组的文章
搜索"心脏支架" → 应同时包含"心""脏""支""架"四个字
搜索"心脏支架 FDA" → 应同时满足中文"心脏支架"和英文"FDA"
四、升级维护脚本
Z-Blog 系统升级会覆盖 c_system_route.php。建议将以下脚本保存为 patch_search.php 放在网站根目录,升级后执行一次即可恢复搜索优化:
<?php
require 'zb_system/function/c_system_base.php';
$zbp->Load();
$file = '/www/wwwroot/wangzhanwww/zb_system/function/c_system_route.php';
$content = file_get_contents($file);
$search_old = <<<'SEARCH'
if ($q) {
$w[] = array('search', 'log_Content', 'log_Intro', 'log_Title', $q);
} else {
$w[] = array('=', 'log_ID', 0);
}
SEARCH;
$search_new = <<<'SEARCH'
if ($q) {
$escaped_q = $zbp->db->EscapeString($q);
$segments = preg_split('/\s+/', $escaped_q, -1, PREG_SPLIT_NO_EMPTY);
$conditions = array();
foreach ($segments as $seg) {
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $seg)) {
$chars = preg_split('//u', $seg, -1, PREG_SPLIT_NO_EMPTY);
foreach ($chars as $char) {
$char = $zbp->db->EscapeString($char);
$conditions[] = "(log_Title LIKE '%{$char}%' OR log_Intro LIKE '%{$char}%' OR log_Content LIKE '%{$char}%')";
}
} else {
$conditions[] = "(log_Title LIKE '%{$seg}%' OR log_Intro LIKE '%{$seg}%' OR log_Content LIKE '%{$seg}%')";
}
}
if (!empty($conditions)) {
$w[] = array('CUSTOM', implode(' AND ', $conditions));
}
} else {
$w[] = array('=', 'log_ID', 0);
}
SEARCH;
if (strpos($content, $search_old) !== false) {
$content = str_replace($search_old, $search_new, $content);
file_put_contents($file, $content);
echo "搜索优化修复完成\n";
} else {
echo "未找到原始搜索代码,可能已修改或版本不匹配,请手动检查\n";
}五、效果对比
| 搜索词 | 修改前(Z-Blog 默认) | 修改后 |
|---|---|---|
| 心脏支架 | 拆成:心、脏、支、架(OR 关系,含"心"就匹配) | 拆成:心、脏、支、架(AND 关系,必须同时含四个字) |
| FDA | 拆成:F、D、A(含 F 就匹配) | 整体:FDA(必须含完整 FDA) |
| FDA approval | 拆成:F、D、A、a、p、p...(极泛) | 拆成:FDA、approval(必须同时含两个英文词) |
| 心脏支架 FDA | 拆成:心、脏、支、架、F、D、A... | 拆成:心、脏、支、架、FDA(必须同时满足) |
六、可选调整
上述方案使用 AND 关系,要求所有拆分单元同时出现,结果更精准。如果希望更宽松(出现任意一个片段就匹配),将代码中的 implode(' AND ', $conditions) 改为 implode(' OR ', $conditions) 即可。
七、总结
本方案仅修改 Z-Blog PHP 核心路由文件中的搜索查询逻辑,不涉及主题文件或数据库结构变更,适用于所有 Z-Blog PHP 站点。通过中英文混合拆分策略,解决了默认搜索按字符拆分导致的泛化匹配问题,实现中文单字与英文整词的精准 AND 匹配,显著提升站内搜索体验。