【问题标题】:mysql query for autocomplete not working自动完成的mysql查询不起作用
【发布时间】:2014-11-13 21:12:01
【问题描述】:

我有以下查询,用于自动完成用户正在搜索的学校名称。如果$query = har

SELECT *, 
       CASE 
         WHEN text LIKE '$query' THEN 1 
         WHEN text LIKE '$query%' THEN 2 
         WHEN text LIKE '%$query%' THEN 3 
       end AS priority 
FROM   (SELECT b.school_name AS `text`, 
               'school'      AS `type`, 
               b.slug          AS `id`, 
               n.neighbourhood AS 'params' 
        FROM   schools b 
               LEFT JOIN school_addresses ba 
                      ON ( b.id = ba.school_id ) 
               LEFT JOIN neighbourhoods n 
                      ON ( ba.neighbourhood_id = n.id ) 
        WHERE  b.entity_status = 'active' 
               AND ba.city_id = '$city' 
               AND b.visibility != 'delisted' 
               AND (b.school_name LIKE '$query' 
                    OR b.school_name LIKE '$query%' 
                    OR b.school_name LIKE '%$query%') 
        UNION 
        SELECT tg.OPTION AS `text`, 
               'tags'    AS `type`, 
               tg.OPTION AS `id`, 
               tg.OPTION AS 'params' 
        FROM   tags t 
               LEFT JOIN tag_options tg 
                      ON ( t.id = tg.tag_id ) 
        WHERE  t.tag = 'Cuisines' 
               AND (tg.OPTION LIKE '$query' 
                    OR tg.OPTION LIKE '$query%' 
                    OR tg.OPTION LIKE '%$query%') 
        UNION 
        SELECT category   AS `text`, 
               'category' AS `type`, 
               category   AS `id`, 
               category   AS 'params' 
        FROM   categories 
        WHERE  category LIKE '$query' 
               OR category LIKE '$query%' 
               OR category LIKE '%$query%' 
        UNION 
        SELECT area   AS `text`, 
               'area' AS `type`, 
               id     AS `id`, 
               id     AS 'params' 
        FROM   areas 
        WHERE  city_id = '$city' 
               AND (area LIKE '$query' 
                    OR area LIKE '$query%' 
                    OR area LIKE '%$query%') 
        UNION 
        SELECT district    AS `text`, 
               'districts' AS `type`, 
               id          AS `id`, 
               id          AS 'params' 
        FROM   districts 
        WHERE  city_id = '$city' 
               AND (district LIKE '$query' 
                    OR district LIKE '$query%' 
                    OR district LIKE '%$query%') 
        UNION 
        SELECT neighbourhood   AS `text`, 
               'neighbourhood' AS `type`, 
               id              AS `id`, 
               id              AS 'params' 
        FROM   neighbourhoods 
        WHERE  city_id = '$city'
               AND (neighbourhood LIKE '$query' 
                    OR neighbourhood LIKE '$query%' 
                    OR neighbourhood LIKE '%$query%') 
        ) AS t1 
WHERE 1 
ORDER  BY priority
LIMIT  5

这是它产生的结果

'text'      'type'     'id'           'params'     'priority'
Harvard     mba     harv-ny-city     new york       2
Harcum      mba     har-pa           Pa             2
Harford     mba     harf-md          Maryland       2

我的问题是如何同时使用上述查询中的“学校名称”“文本”和上述查询中的“学校地点”“参数”进行搜索。就像 $query = 'harford ma' 然后它应该产生如下结果:

   'text'      'type'     'id'           'params'     'priority'
    Harford     mba     harf-md          Maryland       2
    Harford     mba     harv-ny-city     new york       2
    Harford     mba     har-pa           Pa             2

我几乎整天都在玩这个,没有结果。

Logic-> 这是我网站中的自动搜索功能。用户可以尝试查找这些学校的学校名称或城市。但用户也可以同时搜索两者。例如,在孟买、德里、钦奈有 School iit。用户可以像这样搜索:“iit de”->只要用户输入它,它就会自动完成并在顶部搜索中引入 iit Delhi,然后是其他 iit 位置。总共应该显示 5 个结果。

【问题讨论】:

  • 危险!您正在使用用户提供的 $query、$city - 您将获得 sql 注入!如果 $query 是 '; drop table areas; -- 会发生什么?
  • @AMADANONInc。 $query 是用户在搜索栏中输入的内容。这是错误的做法吗?
  • 这正是问题所在。如果用户希望搜索'; drop table areas; --,您的查询会是什么样子?答案:SELECT *, CASE WHEN text LIKE ''; drop table areas; -- - 他们刚刚放下了你的桌子。阅读有关 sql 注入攻击的信息。有一些方法可以解决这个问题,但细节因语言而异。简而言之,您不应该只将用户提供的文本放入这样的查询中。
  • @AMADANONInc。 - 谢谢,我会读的。知道如何查询另一部分吗?
  • What happens if $query is '; drop table areas; -- 抛出异常,注入今天没那么简单)

标签: mysql


【解决方案1】:

通常,您必须使用任何用于调用此查询的编程语言来处理此问题。

您需要将查询拆分为单独的单词并对其进行清理(以防止 SQL 注入,并删除可能影响您的“喜欢”查询的 % 字符。如果没有标点符号,请删除它在实际表中会很好地做到这一点)。

然后您必须动态构建查询,并在每个字段中使用每个单词作为查询词,例如:

           AND (b.school_name LIKE '%$queryWords[0]%' 
                OR b.school_name LIKE '%$queryWords[1]%' 
                OR b.school_name LIKE '%$queryWords[2]%') 

...等等。

请务必注意,您不需要 WHERE 中的条件为 LIKE 'x%' OR LIKE '%x' OR like '%x%'。这是多余的,并且会不必要地减慢查询速度,因为它们都包含在LIKE '%x%' 中。如果匹配是否精确,唯一会产生影响的地方是在您构造排序依据的优先级的表达式中,因此每个 where 条件都应该像我上面指出的那样 - 每个单词只需 LIKE '%$word%' .

或者您可以决定对于学校名称您只检查$word[0],对于学校地点您只检查$word[1] 等等。这取决于您是否相信人们会输入诸如 ma harford 或仅输入 harford ma 之类的查询。

这里最大的挑战是构建优先级。我建议将优先级设置得越高,数字越高,而不是像您所做的那样越低,因为这将允许您sum 给定字段的优先级。所以使用ORDER BY priority DESC

优先级表达式本身会相当复杂:

CASE WHEN text = '$queryWords[0]' OR text = '$queryWords[1]' THEN 3 
     WHEN text LIKE '$queryWords[0]%' OR text LIKE '$queryWords[1]%' THEN 2 
     WHEN text LIKE '%$queryWords[0]%' OR text LIKE '%$queryWords[1]%' THEN 1
     ELSE 0 
END 
+
CASE WHEN params = '$queryWords[0]' OR params = '$queryWords[1]' THEN 3 
     WHEN params LIKE '$queryWords[0]%' OR params LIKE '$queryWords[1]%' THEN 2 
     WHEN params LIKE '%$queryWords[0]%' OR params LIKE '%$queryWords[1]%' THEN 1
     ELSE 0 
END 
AS priority 

(当然,如果单词更多,那么每个OR 的部分会更长WHEN)。

如果您希望学校名称比学校位置更重要,那么您应该将其更改为:

CASE WHEN text = '$queryWords[0]' OR text = '$queryWords[1]' THEN 12 
     WHEN text LIKE '$queryWords[0]%' OR text LIKE '$queryWords[1]%' THEN 8
     WHEN text LIKE '%$queryWords[0]%' OR text LIKE '%$queryWords[1]%' THEN 4
     ELSE 0 
END 
+
CASE WHEN params = '$queryWords[0]' OR params = '$queryWords[1]' THEN 3 
     WHEN params LIKE '$queryWords[0]%' OR params LIKE '$queryWords[1]%' THEN 2 
     WHEN params LIKE '%$queryWords[0]%' OR params LIKE '%$queryWords[1]%' THEN 1
     ELSE 0 
END 
AS priority 

这实质上是将优先级构建为以 4 为基数的数字,因此即使在最不精确的选项中,text 上的匹配也总是高于 params 上的匹配,即使在最佳匹配中也是如此。如果您添加另一个匹配标准,请将每个数字乘以 4,然后在末尾添加 3,2,1。

【讨论】:

  • 非常感谢您的解释。唯一我不明白的是,如果我输入“哈佛马”,马里兰帕姆将如何显示在顶部?因为我们只检查 school_name? AND (b.school_name LIKE '%$queryWords[0]%' OR b.school_name LIKE '%$queryWords[1]%' OR b.school_name LIKE '%$queryWords[2]%')
  • 我正在使用上面提到的优先级: WHEN text = '$queryWords[0]' OR text = '$queryWords[1]' THEN 12 ...但是当我搜索时它总是选择 12 “哈福德马”这个词只得到哈福德而忽略了马城?
  • 视情况而定。如果您只想在学校实际匹配其中一个单词时才优先考虑邻域,那么查询就可以了。每个匹配的学校都会有任何邻里,你的优先级将把ma放在首位。如果您希望即使学校不匹配也能显示匹配的社区,您还必须将它们添加到 OR 中。
  • 抱歉,我在那儿缺少了一些 % 标志。我编辑并修复了。
  • 是的,我对第二种情况进行了完整的搜索。我会尝试输入“小写”,看看是否有什么不同。
猜你喜欢
  • 2020-11-25
  • 2013-03-25
  • 1970-01-01
  • 2016-09-17
  • 2013-11-09
  • 2012-07-10
相关资源
最近更新 更多