提问者:小点点

PHP/SQL-改进搜索功能/模糊搜索


我正在尝试为我的网站创建一个产品搜索,用户可以用多种语言搜索产品,如果没有完全匹配,(希望)会得到模糊的搜索结果。

  • 我有一个pro_search表,它有id、pro_id、en、de、es、fr列,it
  • pro_id列指的是产品在其自己的表中的id
  • en、de、es、fr、it列中有各种语言的每个产品的翻译元
  • 元只是用空格分隔的关键字
  • $term是搜索术语
  • $lang是指用户选择的语言

所以首先我做一个基本的SQL查询,看看是否有匹配,如果没有结果,我查询所有的产品,并使用similar_text()函数创建一个按相似度排序的数组

例如,我搜索'衬衫',如果这个产品的meta只包含单词'衬衫',这很好,但是如果meta包含'蓝色品牌t恤',这是更具描述性的,给用户一个按品牌搜索的机会,但这意味着搜索很可能会变得模糊,而不是用likeSQL查询找到。

这是可行的,但我想知道如何改进,有更好的搜索方法吗?或者人们通常是怎么做的?我应该把meta分成每个单独的关键词,试着看看有多少单词匹配,而不是把这个词和整个meta匹配吗?

    $ids = [];

    $params = ['%'.$term.'%'];
    $sql = "SELECT * FROM pro_search WHERE $lang LIKE ?";
    $stmt = DB::run($sql,$params);

    $count = $stmt->rowCount();
    if($count > 0){

        // product search
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
            $id = $row["pro_id"];
            array_push($ids,$id);
        }
        show_products($ids);

    }else{

        // product fuzzy search
        $sql = "SELECT * FROM pro_search";
        $stmt = DB::run($sql);
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
            $id = $row["pro_id"];
            $result = $row[$lang];
            similar_text($term,$result,$similarity);
            $similar_array[$similarity][] = $id;
        }

        $closest_match = array_keys($similar_array);
        rsort($closest_match);
        $match_count = count($closest_match);

        for($i=0; $i<$match_count; $i++){
            foreach($similar_array[$closest_match[$i]] as $id){
                array_push($ids,$id);
            }
        }
        show_products($ids);
    }

我以前也问过类似的问题,人们向我指出了将这个词与元词进行比较的不同方法(比如Levenstein),但我所看到的一切都是比较两个简单的词(比如苹果和橙子),这对于一个拥有数千种产品的现实应用程序来说还不够好,用户可以搜索任何东西(如$term=“迭代任何东西”;

关键问题:

  • 我的meta应该只有产品名称还是多个相关关键词(关键词太多意味着单个单词与整体不太相似)
  • 如果我在元中有多个关键字,我应该取每个单独的关键字并将其与搜索词进行比较吗
  • 此外,可能对个别产品使用负面关键词

共3个答案

匿名用户

您正在寻找带有查询扩展的全文搜索

MySQL支持使用LIKE运算符和正则表达式进行文本搜索。但是,当文本列很大并且表中的行数增加时,使用这些方法有一些限制:

  • 性能:MySQL必须扫描整个表才能根据like语句中的模式或正则表达式中的模式找到确切的文本。
  • 灵活搜索:使用like运算符和正则表达式搜索,很难进行灵活的搜索查询,例如,查找描述包含汽车但不经典的产品。
  • 相关性排名:无法指定结果集中的哪一行与搜索词更相关。

由于这些限制,MySQL扩展了一个非常好的功能,即全文搜索。从技术上讲,MySQL根据启用的全文搜索列的单词创建索引,并在此索引上执行搜索。MySQL使用复杂的算法来确定与搜索查询匹配的行。

要做到这一点,将用于搜索的列必须是TEXT类型和FULLTEXT类型的索引,索引可以使用ALTER TABLE或CREATE index给定,如果您使用phpMyAdmin来管理数据库,则可以转到该表的“结构”,然后单击该列的“操作”下的“更多”并选择FULLTEXT。

之后,您可以使用匹配语法执行搜索。MATCH()接受要搜索的列。接受一个要搜索的字符串和一个可选的修饰符,该修饰符指示要执行的搜索类型。

在某些情况下,用户希望基于他们所拥有的知识来搜索信息。用户使用他们的经验来定义关键字以搜索信息,并且通常那些关键字太短。

为了帮助用户根据太短的关键字查找信息,MySQL全文搜索引擎引入了一个称为查询扩展的概念。

查询扩展用于基于自动相关性反馈(或盲查询扩展)来扩展全文搜索的搜索结果。从技术上讲,MySQL全文搜索引擎在使用查询扩展时执行以下步骤:

  • 首先,MySQL全文搜索引擎查找与搜索查询匹配的所有行。
  • 其次,它检查搜索结果中的所有行并找到相关词。
  • 第三,它根据相关词而不是用户提供的原始关键字再次执行搜索。

以下示例显示了如何搜索产品名称或meta中至少包含一个单词(shirt tshirt)的产品。

SELECT * FROM products WHERE MATCH(product_name,product_meta) AGAINST('shirt tshirt' WITH QUERY EXPANSION)

您可以在MYSQL文档(答案开头的链接)和此处阅读更多信息

也不要错过如何微调MySQL全文搜索

匿名用户

如果你还在设计这个系统,你可以有一些不同的想法。在搜索方面,只需按照前面的建议进行精确搜索并在数据库中进行,因为这要快得多,但“要从每次交互中学习”。

  • 某些术语中的用户类型
  • 如果发现很棒,你可以进行精确搜索
  • 如果没有,则对输入的术语的每个部分进行模糊搜索。仍然没有找到,你做soundex。你想找点什么!但是向用户提供一个长列表以进行筛选
  • 最终用户选择一个。一旦他们这样做了,你就将他们输入的术语添加到他们选择的产品中

所以基本的想法是,你从每次互动中学习,丰富你的搜索集。此外,每当一个术语被用户使用,并且用户实际上点击了你的项目时,随着你对该术语-产品关联的信心的提高,你就对该术语-产品关联进行计数。

同样,无论何时您呈现一个选项,用户都应该可以很容易地说“不是这个”,然后引导他们通过预先选择的项目层次结构,最终当他们选择一个时,您将他们的搜索词保存到该产品中。

因此,在几个月内,如果您获得了足够的用户,您将为您的产品类别提供一个丰富的自然搜索词数据集,每个词的置信度。

匿名用户

您可以在sql中使用SOUNDEX

SELECT * FROM users 
           WHERE SOUNDEX(job) 
LIKE CONCAT('%',SUBSTRING(SOUNDEX('Manual worker'),2),'%');

手动工作这样的事情会起作用。您只需要调整值(当前为 2)即可满足您的需求。

我看到你已经尝试了Levenshtein算法,但你应该注意这种适应(这也与UTF-8字符串兼容)

就我而言,soundex更高效,这将取决于您的用户将如何与您的应用程序交互。

但正如评论中所说,像ElasticSearch或Algolia这样的第三方可以更有效。

对于我从未使用过它,因为该公司不允许我们使用第三方软件。这就是为什么我同时尝试了Levensthein和Soundex的原因