代码之家  ›  专栏  ›  技术社区  ›  Topher Rhodes

如何在MySQL中使用任意数量的多个标签过滤/限制所选帖子

  •  1
  • Topher Rhodes  · 技术社区  · 10 月前

    我正在构建一个有帖子的网站,每个帖子可以有零个或多个标签。用户可以选择零个或多个标签进行阻止。如果一个标签被阻止,这意味着选择帖子的查询将不会选择任何标记有用户任何被阻止标签的帖子。我已经能够进行一个查询,排除具有任何特定标签的帖子,但当我尝试添加一个排除具有以下标签的帖子的子句时 任何 在用户的被阻止标签中,它不起作用。不仅带有被阻止标签的帖子仍然显示,而且我收到了重复的帖子(帖子标记的每个标签对应一个),而且我没有收到任何没有标签的帖子。我如何允许用户指定任意数量的被阻止标签,然后根据帖子的标签过滤帖子查询?

    以下是相关表格的示例,简化后仅显示相关列:

    帖子

    身份证件 标题
    1. 帖子A
    2. 职位B
    3. 职位C

    标签

    身份证件 名称
    1. 标签1
    2. 标签2
    3. 标签3

    posts_tags

    身份证件 标签 邮递
    1. 1. 1.
    2. 2. 1.
    3. 3. 2.

    blocked_tags

    身份证件 标签 用户
    1. 1. 用户1
    2. 2. 用户1
    3. 3. 用户2

    我的查询看起来像这样(在实际查询中还有更多,我只是展示了基础知识和相关子句):

    SELECT *
    FROM posts
    JOIN tags_posts ON tags_posts.post = posts.id
    WHERE tags_posts.tag NOT IN
         (SELECT blocked_tags.tag
          FROM blocked_tags
          WHERE blocked_tags.user = :user)
    

    And:user被替换为当前用户的id。假设当前用户是user1,那么根据上表中的数据,我希望这个查询返回以下结果:

    身份证件 标题
    2. 职位B
    3. 职位C

    如果当前用户是user2,那么我希望它返回以下内容:

    身份证件 标题
    1. 帖子A
    2. 职位B

    但相反,无论我是哪个用户,我都会得到以下结果:

    身份证件 标题
    1. 帖子A
    1. 帖子A
    2. 职位B

    过滤帖子的正确方法是什么,以便排除任何标记有用户阻止标签的帖子?非常感谢。

    1 回复  |  直到 10 月前
        1
  •  1
  •   GarethD    10 月前

    您可以使用 NOT EXISTS 检查与帖子关联的标签是否都不在给定的用户阻止列表中:

    SELECT  *
    FROM    posts AS p
    WHERE   NOT EXISTS
            (   SELECT  1
                FROM    tags_posts AS tp
                        INNER JOIN blocked_tags AS bt
                            ON bt.tag = tp.tag
                WHERE   bt.user = :user
                AND     tp.post = p.id
            );
    

    Example on db<>fiddle

    如果你真的需要返回与帖子相关的标签,你可以通过执行类似的操作来避免第二次连接(一个在select中,一个在not exists中) HAVING :

    SELECT  p.id, 
            p.title,
            GROUP_CONCAT(t.name) AS tags
    FROM    posts AS p
            LEFT JOIN tags_posts AS tp
                ON tp.post = p.id
            LEFT JOIN tags AS t
                ON t.id = tp.tag
            LEFT JOIN blocked_tags AS bt
                ON bt.tag = tp.tag
                AND bt.user = 'user2'
    GROUP BY p.id, p.title
    HAVING COUNT(bt.tag) = 0 /*No blocked tags*/
    

    Example on db<>fiddle