PHP项目中如何实现模糊搜索?

wen PHP项目 3

本文目录导读:

PHP项目中如何实现模糊搜索?

  1. 使用SQL LIKE 语句(最基础)
  2. MySQL全文搜索(性能更好)
  3. 使用搜索引擎(高级方案)
  4. 完整示例:前后端分离的模糊搜索
  5. 性能优化建议
  6. 选择建议

在PHP项目中实现模糊搜索,常见的方法有以下几种:

使用SQL LIKE 语句(最基础)

MySQL示例

<?php
$search = $_GET['search'] ?? '';
$keyword = "%{$search}%";
// 使用预处理语句防止SQL注入
$stmt = $pdo->prepare("SELECT * FROM products 
                      WHERE name LIKE :keyword 
                      OR description LIKE :keyword2");
$stmt->execute([
    ':keyword' => $keyword,
    ':keyword2' => $keyword
]);
$results = $stmt->fetchAll();
?>

MySQL全文搜索(性能更好)

先创建全文索引

ALTER TABLE products ADD FULLTEXT INDEX ft_index (name, description);

PHP代码

<?php
$search = $_GET['search'] ?? '';
// 自然语言模式
$stmt = $pdo->prepare("SELECT *, 
                      MATCH(name, description) AGAINST(:keyword) AS relevance 
                      FROM products 
                      WHERE MATCH(name, description) AGAINST(:keyword IN NATURAL LANGUAGE MODE)
                      ORDER BY relevance DESC");
$stmt->execute([':keyword' => $search]);
$results = $stmt->fetchAll();
?>

使用搜索引擎(高级方案)

Elasticsearch示例

<?php
require 'vendor/autoload.php';
use Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->build();
$params = [
    'index' => 'products',
    'body'  => [
        'query' => [
            'multi_match' => [
                'query' => $_GET['search'],
                'fields' => ['name^3', 'description'] // name字段权重更高
            ]
        ]
    ]
];
$response = $client->search($params);
$results = $response['hits']['hits'];
?>

完整示例:前后端分离的模糊搜索

后端 (search.php)

<?php
header('Content-Type: application/json');
try {
    $pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $search = $_GET['q'] ?? '';
    if (empty($search)) {
        echo json_encode(['status' => 'error', 'message' => '请输入搜索关键词']);
        exit;
    }
    // 防止SQL注入
    $keyword = '%' . $search . '%';
    $stmt = $pdo->prepare("SELECT id, name, description, price 
                          FROM products 
                          WHERE name LIKE :keyword 
                          OR description LIKE :keyword 
                          LIMIT 20");
    $stmt->execute([':keyword' => $keyword]);
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo json_encode([
        'status' => 'success',
        'data' => $results,
        'total' => count($results)
    ]);
} catch (PDOException $e) {
    echo json_encode(['status' => 'error', 'message' => '数据库错误']);
}
?>

前端 (search.html)

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">模糊搜索示例</title>
    <style>
        #searchResults { margin-top: 20px; }
        .result-item { padding: 10px; border-bottom: 1px solid #eee; }
    </style>
</head>
<body>
    <input type="text" id="searchInput" placeholder="输入搜索关键词...">
    <div id="searchResults"></div>
    <script>
        const searchInput = document.getElementById('searchInput');
        const resultsDiv = document.getElementById('searchResults');
        let timeoutId;
        searchInput.addEventListener('input', function() {
            clearTimeout(timeoutId);
            timeoutId = setTimeout(() => performSearch(this.value), 300);
        });
        function performSearch(query) {
            if (query.length < 2) {
                resultsDiv.innerHTML = '';
                return;
            }
            fetch(`search.php?q=${encodeURIComponent(query)}`)
                .then(response => response.json())
                .then(data => {
                    if (data.status === 'success') {
                        displayResults(data.data);
                    } else {
                        resultsDiv.innerHTML = `<p>${data.message}</p>`;
                    }
                })
                .catch(error => {
                    resultsDiv.innerHTML = '<p>搜索出错,请重试</p>';
                });
        }
        function displayResults(results) {
            if (results.length === 0) {
                resultsDiv.innerHTML = '<p>未找到相关结果</p>';
                return;
            }
            let html = '';
            results.forEach(item => {
                html += `
                    <div class="result-item">
                        <h3>${highlightText(item.name)}</h3>
                        <p>${highlightText(item.description)}</p>
                        <p>价格: ¥${item.price}</p>
                    </div>
                `;
            });
            resultsDiv.innerHTML = html;
        }
        // 高亮搜索关键词
        function highlightText(text) {
            const keyword = searchInput.value;
            if (!keyword) return text;
            const regex = new RegExp(`(${keyword})`, 'gi');
            return text.replace(regex, '<mark>$1</mark>');
        }
    </script>
</body>
</html>

性能优化建议

数据库层面

-- 添加索引优化LIKE查询
CREATE INDEX idx_name ON products(name);
-- 对于前缀模糊搜索有效
-- 避免以%开头的LIKE查询
-- 使用全文索引
ALTER TABLE products ADD FULLTEXT INDEX ft_search (name, description);

PHP层面

// 1. 限制搜索长度
$search = substr($search, 0, 50);
// 2. 清理特殊字符
$search = preg_replace('/[^\p{L}\p{N}\s]/u', '', $search);
// 3. 缓存搜索结果
$cacheKey = 'search_' . md5($search);
$cached = apcu_fetch($cacheKey);
if ($cached !== false) {
    return $cached;
}
// ... 执行查询
apcu_store($cacheKey, $results, 300); // 缓存5分钟

选择建议

方案 适用场景 优点 缺点
LIKE 小数据量(<1万条) 简单易用 性能差,不支持中文分词
全文索引 中等数据量 性能较好,支持中文 配置复杂
Elasticsearch 大数据量,复杂搜索 功能强大,支持分词 需要额外服务
Sphinx 全文搜索需求 性能优异 配置维护复杂

选择哪种方案取决于你的数据量、搜索复杂度和性能要求,对于大多数中小型项目,MySQL全文搜索已经足够。

抱歉,评论功能暂时关闭!