Z-Blog PHP 站内搜索精准匹配优化方案:中英文混合拆分与整词搜索

56

一、问题背景

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 匹配,显著提升站内搜索体验。

网友评论

访客信息