返回

MySQL 关键词搜索:任意顺序完全匹配方案

mysql

MySQL 搜索:关键词任意顺序但需完全匹配

搜索功能是网站的重要组成部分。有时,需求是查找包含多个关键词,并且这些关键词的出现顺序不重要的记录。数据库查询应该如何优化,以确保返回的结果是完全匹配关键词,哪怕顺序不同? 这篇文章深入探讨该问题,并给出一些实践方法。

问题分析

代码段展示了常见的搜索逻辑:将输入的关键词使用空格分割,然后针对每个词进行模糊匹配(LIKE %keyword%)。 这种方式确实会返回包含任何 一个关键词的记录,而并非必须包含所有 关键词。

因此,blue car 作为查询,它会查找包含 blue 或者 car 的所有条目。这与需求:需要同时包含 bluecar,无论它们的顺序是不同的。

解决方案

这里有几种解决方案来达成要求,每个方案都有其侧重点和应用场景。

1. 使用 AND 连接 LIKE 条件

将所有关键词的匹配条件用 AND 连接,可以确保记录同时包含所有关键词,而不再是仅仅包含其中之一。

function search_it() {
    if (isset($_GET['s'])) {
        global $wpdb;
        $address_table = $wpdb->prefix . 'my_products';
        $search = $_GET['s'];
        $search = trim($search);
        $keywords = explode(" ", $search);
        
        $where_conditions = [];
        foreach ($keywords as $keyword) {
             $keyword = "%{$keyword}%";
            $where_conditions[] = $wpdb->prepare('(name LIKE %s OR price LIKE %s OR id LIKE %s OR market_price LIKE %s OR image_url LIKE %s)',$keyword,$keyword, $keyword, $keyword,$keyword );
        }
        
        if(empty($where_conditions)) {
            return [];
        }
        
         $where = "WHERE " . implode(" AND ", $where_conditions);

         $results = $wpdb->get_results( "SELECT * FROM {$address_table} {$where} LIMIT 0,30" );

        return $results;
    }
}

操作步骤:

  1. 修改 search_it() 函数:不再是迭代 keywords 生成最终 where 子句,改为循环构建匹配条件。
  2. 构建的每个匹配条件使用 prepare 防止SQL注入,并使用OR逻辑合并字段的搜索结果。
  3. 使用implode(" AND ", $where_conditions) 将匹配条件数组使用 AND 连接成完整的 WHERE 子句。
  4. 将新的 WHERE 子句嵌入SQL语句执行查询,并返回结果。

这种方案利用 AND 条件保证所有关键词都出现在搜索结果中,可以满足基本的关键词任意顺序匹配需求。不过模糊匹配也会增加不必要的匹配,导致搜索结果有偏差。

2. 使用 FULLTEXT 索引与 MATCH ... AGAINST

对于需要高效率搜索大量文本的场景,MySQL 的 FULLTEXT 索引是个很好的选择。

首先需要在表上建立全文索引:

ALTER TABLE `my_products` ADD FULLTEXT(`name`, `price`,`id`,`market_price`,`image_url`);

或者也可以通过下面的命令创建一个索引,如果之前已经存在需要drop:

ALTER TABLE my_products DROP INDEX your_index_name; 
CREATE FULLTEXT INDEX your_index_name ON my_products (`name`,`price`,`id`,`market_price`,`image_url`);

your_index_name是你希望命名的索引名字。

然后,在 PHP 代码中使用 MATCH ... AGAINST 进行搜索:

function search_it() {
    if (isset($_GET['s'])) {
        global $wpdb;
        $address_table = $wpdb->prefix . 'my_products';
        $search = $_GET['s'];
        $search = trim($search);
    
         $where = $wpdb->prepare('WHERE MATCH(name,price,id,market_price,image_url) AGAINST (%s IN BOOLEAN MODE)', $search);


          $results = $wpdb->get_results( "SELECT * FROM {$address_table} {$where} LIMIT 0,30" );
        return $results;
    }
}

操作步骤:

  1. 使用 ALTER TABLE 或者CREATE FULLTEXT INDEX语句给对应表和字段添加 FULLTEXT 索引,这里对字段的类型有要求,例如TEXT, VARCHAR类型字段
  2. search_it()函数中, where 子句使用了MATCH(name,price,id,market_price,image_url) AGAINST (%s IN BOOLEAN MODE)BOOLEAN MODE 可以支持更灵活的关键词搜索逻辑
  3. 直接运行查询,不需要像前面方法那样分割关键词

FULLTEXT 索引能够快速处理大规模文本搜索,它还提供了高级的搜索操作,例如:短语匹配、排除词汇等。这种方案效率较高,更适用于文本量大的场景。

总结

对于关键词搜索需求,使用 AND 连接 LIKE 条件能满足基本需求。当数据量变大,或者对搜索性能要求高的时候,使用 FULLTEXT 索引会更加高效,通过 MATCH ... AGAINST 可以灵活设置搜索逻辑。需要注意,修改表结构务必提前备份数据,且建立索引的过程比较消耗资源,避免影响现有业务。 选择适合自己业务场景的方法是解决这类问题的关键。