【问题标题】:Filter users with a join table使用连接表过滤用户
【发布时间】:2020-11-06 17:53:14
【问题描述】:

我有用于用户、标签和连接表标签的表。

我目前正在使用scope通过不同的变量过滤用户,例如:

scope = scope.where('users.created_at > :created_after', created_after: params[:created_after])

我正在尝试按分配给用户的标签过滤用户(通过has many 关系)。

在用户模型中:

has_many :taggings, as: :taggable
has_many :tags, through: :taggings

用户表:

╔════╦══════════════╦
║ id ║  name        ║
╠════╬══════════════╬
║  1 ║ Picard       ║ 
║  2 ║ Kirk         ║
║  3 ║ Sisko        ║
║  4 ║ Janeway      ║
╚════╩══════════════╩

标签表:

╔════╦══════════════╦
║ id ║  Tag         ║ 
╠════╬══════════════╬
║  1 ║ TOS          ║  
║  2 ║ TNG          ║ 
║  3 ║ DS9          ║ 
║  4 ║ VOY          ║ 
╚════╩══════════════╩

标签表:

╔════╦═══════════╦═══════════════╦═══════════════════════╦
║ id ║  tag_id   ║ taggable_id   ║ taggable_type         ║ 
╠════╬═══════════╬═══════════════╬═══════════════════════╣
║  1 ║ 1         ║    1          ║  User                 ║
║  2 ║ 1         ║    4          ║  Organization         ║
║  3 ║ 2         ║    2          ║  User                 ║
║  4 ║ 1         ║    3          ║  User                 ║
╚════╩═══════════╩═══════════════╩═══════════════════════╝

我尝试过类似的方法:

SELECT * FROM users u
  JOIN taggings j, tags t
  WHERE t.tag = <tag>
    AND t.id = j.tag_id
    AND j.taggable_type = 'User'
    AND u.id = j.taggable_id

【问题讨论】:

    标签: sql ruby-on-rails postgresql


    【解决方案1】:

    你可以得到一个计数并使用必须规定用户必须有一个或多个标签:

    tags = ["TOS", "TNG"]
    User.joins(:tags)
        .where(tags: { tag: tags })
        .group(:id)
        .having(
          # Arel for "COUNT(tags.*) >= ?"
          Tag.arel_table[Arel.star].count.gte(tags.length)
        )
    

    由于您使用的是多态关联,因此您可能希望将其烘焙到一个模块中以便您可以重复使用它:

    module Taggable
      extend ActiveSupport::Concern
      included do
        has_many :taggings, as: :taggable
        has_many :tags, through: :taggings
      end
    
      class_methods do
        def with_tags(*tags) 
          joins(:tags)
            .where(tags: { tag: tags })
            .group(:id)
            .having(  
              Tag.arel_table[Arel.star]
                 .count.gteq(tags.length)
            )
        end
      end
    end
    
    class User < ApplicationRecord
      include Taggable
    end
    

    【讨论】:

    • 如果您希望能够同时急切加载标签,也可以使用子查询或横向连接(在 Postgres 上),因为您无法获取连接的行除非您使用不关心 SQL 标准或歧义等讨厌的东西的 MySQL/MariaDB,否则分组时使用表。
    • 这最终效果很好!谢谢,最大。只是一个小提示:有一个错字。 .count.gte(tags.length) 应该是 .count.gteq(tags.length)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-31
    • 1970-01-01
    • 2018-02-22
    相关资源
    最近更新 更多