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

时间:2019-03-13 16:53:59

标签: php sql search

我正在尝试为我的网站创建一个产品搜索,用户可以在其中搜索多种语言的产品,并希望在没有完全匹配的情况下获得模糊的搜索结果。

  • 我有一个pro_search表,其中有列id, pro_id, en, de, es, fr, it
  • pro_id列是指以下产品的ID 自己的桌子。
  • en, de, es, fr, it列已翻译 每种语言的每种产品的meta。
  • 元只是由空格分隔的关键字
  • $term是搜索词。
  • $lang是指用户选择的语言

因此,首先,我执行一个基本的“ LIKE” SQL查询以查看是否存在匹配项,如果没有结果,我将查询所有产品并使用similar_text()函数创建一个按相似度排序的数组< / p>

例如,我搜索“衬衫”,如果该产品的元数据仅包含单词“衬衫”,则很好,但是如果元数据包含“蓝色品牌的T恤”,则说明性更强,并为用户提供了搜索的机会按品牌划分,但这意味着搜索将很可能变得模糊,而不是通过LIKE SQL查询找到。

这是一种工作方式,但我想知道如何加以改进,是否有更好的搜索方式或人们通常会如何做?我是否应该将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);
    }

我之前也问过类似的问题,人们向我指出了将术语与meta进行比较的不同方法(例如levenshtein),但是我所看到的一切都是在比较两个简单的单词(例如苹果和橘子),并且对于具有成千上万种产品的现实生活应用程序而言,这还不够好,并且用户可以搜索几乎所有内容(如$term='literally anything';

关键问题:

  • 我的meta应该只包含产品名称还是多个相关名称 关键字(关键字过多意味着单个单词不太相似 整个)?
  • 如果我在中继中有多个关键字,我应该 提取每个关键字并将其与搜索进行比较 学期?
  • 也可能会使用否定关键字 单个产品。

3 个答案:

答案 0 :(得分:2)

您可以在SQL中使用SOUNDEX

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

诸如Manual worka之类的东西将起作用。您只需要调整值(当前为2)即可满足您的需求。

我发现您已经尝试过Levenshtein算法,但应该注意this adaptation(它也与UTF-8字符串兼容)

就我而言,soundex的效率更高,这取决于用户与应用程序的交互方式。


但是正如评论中所述,像ElasticSearchAlgolia这样的第三方可能会更有效率。

在某些情况下,我从未使用过它,因为该公司不允许我们使用第三方软件。这就是为什么我同时尝试 Levensthein Soundex

答案 1 :(得分:2)

如果您仍在设计系统,则可能会有所不同。就搜索而言,只需执行精确搜索,然后按照前面的建议在db中进行搜索,因为这样做的速度要快得多-而是“从每次交互中学习”。

  • 某种程度上的用户类型
  • 如果发现很好,您可以进行精确搜索。
  • 如果没有,则对输入的术语的每个部分进行模糊搜索。仍然找不到,您需要soundex。您正在寻找东西!但是向用户显示一长串列表以进行过滤。
  • 最终用户选择一个。一旦完成,您便将他们输入的术语添加到他们选择的产品中。

一个基本的想法是,您正在从每次交互中学习并丰富您的搜索集。同样,只要有一个术语是用户,并且用户实际点击了您的商品,您就可以依赖该术语-产品关联,因为对术语-产品关联的信心得到了改善。

类似地,每当您出现一个选项时,用户都应该很容易说出“ Not this”,然后引导他们通过商品的预选层次结构,最终当他们选择一个商品时,将其搜索词保存到该商品中产品。

因此,在接下来的几个月中,如果您获得足够的用户,您将拥有针对您的产品类别的丰富的自然搜索字词数据集,并且每个字词的置信度都很高。

答案 2 :(得分:1)

您正在寻找Full-Text Searches WITH QUERY EXPANSION

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

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

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

为此,用于搜索的列必须为TEXT类型,并且索引的类型必须为FULLTEXT,可以使用ALTER TABLECREATE INDEX给出索引,如果您使用的是phpMyAdmin来管理数据库,您可以通过转到该表的结构,然后在该列的“操作”下单击“更多”并选择“全文本”来实现。

之后,您可以使用MATCH AGAINST语法执行搜索。 MATCH()获取要搜索的列。 AGAINST使用一个字符串进行搜索,并使用一个可选的修饰符来指示要执行的搜索类型。

具有查询扩展的全文本搜索:

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

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

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

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

下面的示例向您展示如何搜索产品名称或元数据至少包含一个单词的产品(衬衫T恤)。

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

您可以在MYSQL文档(答案开头的链接)和here

中阅读更多信息。

也不要错过How Fine-Tuning MySQL Full-Text Search