【问题标题】:Best way to do a weighted search over multiple fields in mysql?对mysql中的多个字段进行加权搜索的最佳方法?
【发布时间】:2011-09-23 17:20:24
【问题描述】:

这是我想做的:

  • 将搜索主题与我的表格的多个字段进行匹配
  • 按字段的重要性和匹配的相关性(按此顺序)对结果进行排序

例如:假设我有一个博客。然后有人搜索“php”。结果会这样显示:

  • 首先,字段“title”的匹配项,按相关性排序
  • 然后,“body”字段的匹配项也按相关性排序
  • 等等与指定的字段...

我实际上是用 PHP 中的一个类来做到这一点的,但是它使用了很多 UNIONS(很多!)并且随着搜索主题的大小而增长。所以我担心性能和DOS问题。有没有人知道这方面的线索?

【问题讨论】:

    标签: php mysql search


    【解决方案1】:

    这种进行加权搜索/结果的方法可能适合您:

    SELECT *,
        IF(
                `name` LIKE "searchterm%",  20, 
             IF(`name` LIKE "%searchterm%", 10, 0)
          )
          + IF(`description` LIKE "%searchterm%", 5,  0)
          + IF(`url`         LIKE "%searchterm%", 1,  0)
        AS `weight`
    FROM `myTable`
    WHERE (
        `name` LIKE "%searchterm%" 
        OR `description` LIKE "%searchterm%"
        OR `url`         LIKE "%searchterm%"
    )
    ORDER BY `weight` DESC
    LIMIT 20
    

    它使用一个选择子查询来提供对结果排序的权重。在这种情况下,搜索了三个字段,您可以指定每个字段的权重。它可能比联合更便宜,并且可能是纯 MySQL 中更快的方法之一。

    如果你有更多的数据并且需要更快的结果,你可以考虑使用像 Sphinx 或 Lucene 这样的东西。

    【讨论】:

    • 我喜欢这种方法!你能解释一下 FROM 之前的 IF 发生了什么吗?我不使用复杂的查询 =/
    • 基本上它是一个IF函数,如果条件(第一个参数)为真,将使用第二个参数(权重),否则将使用第三个参数(0-weight)。该手册包含所有详细信息:dev.mysql.com/doc/refman/5.0/en/…
    • 那太好了!所以如果它出现在名称(而不是开头)、描述和网址中,它的权重会是 16? (10+5+1)
    • 对于这种“相似性”搜索,您应该使用 Lucene / SolR。
    • 优秀的解决方案!它很简单,它支持多个搜索词和多个可搜索字段。谢谢!
    【解决方案2】:

    您可以将多个 mysql MATCH() 值相加,首先将每个值乘以它们的权重。

    当然是简化...

    '(MATCH(column1) AGAINST(\''.$_GET['search_string'].'\') * '.$column1_weight.')
     + (MATCH(column2) AGAINST(\''.$_GET['search_string'].'\') * '.$column2_weight.')
     + (MATCH(column3) AGAINST(\''.$_GET['search_string'].'\') * '.$column3_weight.')
     AS relevance'
    

    然后

    'ORDER BY relevance'
    

    【讨论】:

    • 如果您的权重设置正确,它将按所需顺序放置。
    • 你能给我一个如何设置权重的例子吗?
    • @hugo_leonardo 如果标题中的匹配非常弱,而正文中的匹配非常强,则正文匹配项仍有可能出现在标题匹配项之前。这是一种更好的方法,但是,并非所有标题匹配都比所有身体匹配更有价值,具体取决于匹配分数。另外,请记住,被匹配的字段需要有一个全文索引。
    • 但是显然你会在 SQL 中使用 $_GET 变量之前对其进行转义。
    • @DavidYell 是的,或者更好的是,使用 PDO 准备好的语句。
    【解决方案3】:

    使用 MySQL 的 CASE 函数 (https://dev.mysql.com/doc/refman/5.7/en/case.html) 有一种原生且简洁的方式来执行此操作。

    示例(未经测试):

    从“我的表”中选择 * WHERE (`name` LIKE "%searchterm%" OR `description` LIKE %searchterm%" OR `url` LIKE "%searchterm%") 按情况订购 WHEN `name` LIKE "searchterm%" THEN 20 WHEN `name` LIKE "%searchterm%" THEN 10 WHEN `description` LIKE "%searchterm%" THEN 5 当 `url` 像 "%searchterm%" THEN 1 否则 0 结尾 限制 20

    我已经将它用于我自己的许多加权搜索,并且绝对是一种享受!

    【讨论】:

      【解决方案4】:

      您应该使用专用索引器将所有数据预取到优化的可搜索索引中。 Sphinx 和类似的产品在这方面做得很好。

      【讨论】:

        【解决方案5】:

        我有同样的问题,并且在一个 MySQL 论坛上得到了完整的回答。 Here's the thread. 有点长线(因为我有点啰嗦),但回报正是你想要的。

        【讨论】:

        • @hugo_leonardo -- 是的,回答的两个人给出了非常周到的答案。
        • @PeteWilson:请在您的帖子中添加相关引号,因为外部链接可能会中断。
        【解决方案6】:
        SELECT post_name, post_title,
            (CASE WHEN `post_name` LIKE '%install%' THEN(9 / LENGTH(post_name) * 100) ELSE 0 END) 
            + (CASE WHEN `post_title` LIKE '%install%' THEN(9 / LENGTH(post_title) * 50) ELSE 0 END)
                AS priority
        FROM wp_posts
        WHERE
            post_title LIKE '%install%'
            OR post_name LIKE '%install%'
        ORDER BY priority DESC
        

        这个查询不仅会检查列的权重,还会检查每一行的权重:

        • 检查搜索词在每个字段单元格中的重要性。例如,如果搜索 installinstall 将胜过 install something(长度包含在权重计算中)。
        • 每个字段都可以分配权重(在本例中为 100 和 50,可选)。

        基本上,如果我们有这些值并搜索install:(1 列示例,但它也适用于多列)

        • “关于安装的其他内容”
        • “安装”
        • “安装一些东西”

        搜索会给出这个顺序:

        • 安装” - 128 重量
        • 安装东西” - 52 重量
        • “其他关于安装” - 32 重量

        【讨论】:

          猜你喜欢
          • 2019-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-09
          • 1970-01-01
          • 2010-11-16
          • 1970-01-01
          相关资源
          最近更新 更多