PHP拼音搜索方案

wen PHP项目 7

本文目录导读:

PHP拼音搜索方案

  1. 最简方案:使用拼音数据库(推荐)
  2. MySQL全文索引方案(大数据量)
  3. Redis缓存方案(高性能)
  4. 前端+后端配合方案
  5. Elasticsearch搜索方案(企业级)
  6. 建议

我来给你提供几种PHP拼音搜索的解决方案,从简单到复杂都有:

最简方案:使用拼音数据库(推荐)

安装方式

composer require overtrue/pinyin

核心实现代码

<?php
use Overtrue\Pinyin\Pinyin;
class PinyinSearch
{
    private $pinyin;
    public function __construct()
    {
        $this->pinyin = new Pinyin();
    }
    /**
     * 将中文转拼音
     */
    public function toPinyin($text)
    {
        // 获取全拼
        $full = $this->pinyin->convert($text);
        // 获取首字母
        $abbr = $this->pinyin->abbr($text);
        return [
            'full' => implode('', $full),      // 全拼无空格
            'full_space' => implode(' ', $full), // 全拼带空格
            'abbr' => $abbr,                     // 首字母
        ];
    }
    /**
     * 搜索方法 - 支持拼音和中文混合搜索
     */
    public function search($keywords, $data)
    {
        $results = [];
        $kwPinyin = $this->toPinyin($keywords);
        foreach ($data as $item) {
            $itemPinyin = $this->toPinyin($item['name']);
            // 多种匹配方式
            if (strpos($itemPinyin['full'], $keywords) !== false || // 直接匹配中文
                strpos($itemPinyin['full'], $kwPinyin['full']) !== false || // 匹配全拼
                strpos($itemPinyin['abbr'], $kwPinyin['abbr']) !== false || // 匹配首字母
                $this->fuzzyMatch($keywords, $itemPinyin)) { // 模糊匹配
                $results[] = $item;
            }
        }
        return $results;
    }
    /**
     * 模糊匹配(支持拼音简拼和全拼混合)
     */
    private function fuzzyMatch($keyword, $pinyinData)
    {
        $keyword = strtolower($keyword);
        $full = strtolower($pinyinData['full']);
        $abbr = strtolower($pinyinData['abbr']);
        // 简拼+全拼混合,如 "北京欢迎你" -> "bjhyn"
        if (preg_match('/^[a-z]+$/', $keyword)) {
            // 全字母匹配
            if (strpos($full, $keyword) !== false) return true;
            // 首字母匹配
            if (strpos($abbr, $keyword) !== false) return true;
            // 混合匹配:部分全拼+部分简拼
            $pattern = '';
            foreach (str_split($keyword) as $char) {
                $pattern .= $char . '.*';
            }
            if (preg_match('/' . $pattern . '/', $full)) return true;
        }
        return false;
    }
}
// 使用示例
$search = new PinyinSearch();
$data = [
    ['id' => 1, 'name' => '北京欢迎你'],
    ['id' => 2, 'name' => '上海迪士尼'],
    ['id' => 3, 'name' => '广州长隆'],
    ['id' => 4, 'name' => '深圳世界之窗'],
];
// 搜索 "北京" 或 "bj" 或 "北京hyn"
$results = $search->search('bj', $data);
print_r($results);

MySQL全文索引方案(大数据量)

数据库表结构设计

-- 创建拼音字段表
CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `pinyin` varchar(255) DEFAULT NULL,      -- 全拼 
  `pinyin_abbr` varchar(50) DEFAULT NULL,  -- 首字母
  PRIMARY KEY (`id`),
  KEY `idx_pinyin` (`pinyin`),
  KEY `idx_pinyin_abbr` (`pinyin_abbr`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

PHP实现

<?php
class PinyinFullTextSearch
{
    private $pdo;
    private $pinyin;
    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
        $this->pinyin = new Pinyin();
    }
    /**
     * 数据插入时同步拼音
     */
    public function addProduct($name)
    {
        $pinyin = $this->pinyin->convert($name);
        $abbr = $this->pinyin->abbr($name);
        $stmt = $this->pdo->prepare(
            "INSERT INTO products (name, pinyin, pinyin_abbr) 
             VALUES (?, ?, ?)"
        );
        $stmt->execute([
            $name,
            implode('', $pinyin),
            $abbr
        ]);
        return $this->pdo->lastInsertId();
    }
    /**
     * 高效拼音搜索 - SQL查询
     */
    public function search($keyword)
    {
        // 将关键词转为拼音
        $pinyinKeyword = $this->pinyin->convert($keyword);
        $abbrKeyword = $this->pinyin->abbr($keyword);
        $sql = "SELECT * FROM products
                WHERE 
                    name LIKE :keyword OR
                    pinyin LIKE :pinyin OR
                    pinyin_abbr LIKE :abbr
                ORDER BY 
                    CASE 
                        WHEN name LIKE :keyword_exact THEN 1
                        WHEN pinyin LIKE :pinyin_exact THEN 2
                        WHEN pinyin_abbr LIKE :abbr_exact THEN 3
                        ELSE 4
                    END
                LIMIT 20";
        $stmt = $this->pdo->prepare($sql);
        $like = '%' . $keyword . '%';
        $likePinyin = '%' . implode('', $pinyinKeyword) . '%';
        $likeAbbr = '%' . $abbrKeyword . '%';
        $stmt->execute([
            'keyword' => $like,
            'pinyin' => $likePinyin,
            'abbr' => $likeAbbr,
            'keyword_exact' => $keyword . '%',
            'pinyin_exact' => implode('', $pinyinKeyword) . '%',
            'abbr_exact' => $abbrKeyword . '%'
        ]);
        return $stmt->fetchAll();
    }
}

Redis缓存方案(高性能)

<?php
class PinyinRedisSearch
{
    private $redis;
    private $pinyin;
    public function __construct(Redis $redis)
    {
        $this->redis = $redis;
        $this->pinyin = new Pinyin();
    }
    /**
     * 初始化拼音索引
     */
    public function buildIndex($products)
    {
        foreach ($products as $product) {
            $pinyin = $this->pinyin->convert($product['name']);
            $abbr = $this->pinyin->abbr($product['name']);
            // 建立索引
            $this->redis->sAdd('pinyin:' . implode('', $pinyin), $product['id']);
            $this->redis->sAdd('pinyin_abbr:' . $abbr, $product['id']);
            $this->redis->sAdd('pinyin_space:' . implode(' ', $pinyin), $product['id']);
            // 建立模糊索引(每个拼音前缀)
            $fullPinyin = implode('', $pinyin);
            for ($i = 1; $i <= strlen($fullPinyin); $i++) {
                $prefix = substr($fullPinyin, 0, $i);
                $this->redis->sAdd('pinyin_prefix:' . $prefix, $product['id']);
            }
        }
    }
    /**
     * 快速搜索
     */
    public function search($keyword)
    {
        if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $keyword)) {
            // 中文搜索
            $ids = $this->redis->sInter('pinyin:' . $this->pinyin->convert($keyword));
        } else {
            // 拼音搜索
            $ids = $this->redis->sMembers('pinyin_prefix:' . strtolower($keyword));
        }
        // 获取产品信息
        $products = [];
        foreach ($ids as $id) {
            $products[] = $this->getProductById($id);
        }
        return $products;
    }
}

前端+后端配合方案

PHP后端返回拼音数据

<?php
// 返回含有拼音的数据
public function getProductsWithPinyin()
{
    $products = $this->getAllProducts();
    foreach ($products as &$product) {
        $pinyin = $this->pinyin->convert($product['name']);
        $product['pinyin'] = implode('', $pinyin);
        $product['pinyin_abbr'] = $this->pinyin->abbr($product['name']);
        $product['pinyin_array'] = $pinyin; // 每个字对应的拼音
    }
    return json_encode($products);
}

前端JavaScript实现实时搜索

// search.js
class PinyinSearchBox {
    constructor(data) {
        this.data = data;
        this.init();
    }
    init() {
        this.searchInput.addEventListener('input', (e) => {
            const keyword = e.target.value.toLowerCase();
            this.filterData(keyword);
        });
    }
    filterData(keyword) {
        if (!keyword) {
            this.renderAll();
            return;
        }
        const results = this.data.filter(item => {
            // 中文匹配
            if (item.name.includes(keyword)) return true;
            // 拼音匹配
            if (item.pinyin.includes(keyword)) return true;
            // 首字母匹配
            if (item.pinyin_abbr.includes(keyword)) return true;
            // 拼音首字母与汉字混合匹配
            const keywordArray = keyword.split('');
            let pinyinIndex = 0;
            let nameIndex = 0;
            while (nameIndex < item.name.length && pinyinIndex < keywordArray.length) {
                // 检查是否为汉字
                if (/[\u4e00-\u9fa5]/.test(keywordArray[pinyinIndex])) {
                    if (item.name[nameIndex] === keywordArray[pinyinIndex]) {
                        pinyinIndex++;
                    }
                    nameIndex++;
                } else {
                    // 拼音匹配
                    const pinyinChar = item.pinyin_array[nameIndex] || '';
                    if (pinyinChar.startsWith(keywordArray[pinyinIndex])) {
                        pinyinIndex++;
                        nameIndex++;
                    } else {
                        return false;
                    }
                }
            }
            return pinyinIndex === keywordArray.length;
        });
        this.renderResults(results);
    }
}

Elasticsearch搜索方案(企业级)

<?php
// 使用Elasticsearch
use Elastic\Elasticsearch\ClientBuilder;
class ElasticPinyinSearch
{
    private $client;
    public function __construct()
    {
        $this->client = ClientBuilder::create()
            ->setHosts(['localhost:9200'])
            ->build();
    }
    /**
     * 创建索引(带拼音分析器)
     */
    public function createIndex()
    {
        $params = [
            'index' => 'products',
            'body' => [
                'settings' => [
                    'analysis' => [
                        'analyzer' => [
                            'pinyin_analyzer' => [
                                'type' => 'custom',
                                'tokenizer' => 'standard',
                                'filter' => ['pinyin_filter']
                            ]
                        ],
                        'filter' => [
                            'pinyin_filter' => [
                                'type' => 'pinyin',
                                'keep_full_pinyin' => true,
                                'keep_first_letter' => true,
                                'keep_joined_full_pinyin' => true
                            ]
                        ]
                    ]
                ],
                'mappings' => [
                    'properties' => [
                        'name' => [
                            'type' => 'text',
                            'analyzer' => 'pinyin_analyzer',
                            'search_analyzer' => 'standard'
                        ]
                    ]
                ]
            ]
        ];
        return $this->client->indices()->create($params);
    }
    /**
     * 拼音搜索
     */
    public function search($keyword)
    {
        $params = [
            'index' => 'products',
            'body' => [
                'query' => [
                    'multi_match' => [
                        'query' => $keyword,
                        'fields' => ['name^3', 'name.pinyin']
                    ]
                ],
                'highlight' => [
                    'fields' => [
                        'name' => ['pre_tags' => ['<b>'], 'post_tags' => ['</b>']]
                    ]
                ]
            ]
        ];
        return $this->client->search($params);
    }
}

建议

  • 小项目:使用 overtrue/pinyin 包,简单易用
  • 中型项目:数据库添加拼音字段 + 索引
  • 大型项目:Elasticsearch + IK分析器 + 拼音插件
  • 高并发:Redis缓存 + 拼音索引

需要哪种具体方案的完整实现,或者有特定的使用场景,可以详细说说。

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