【问题标题】:How to check a condition on a join?如何检查加入的条件?
【发布时间】:2015-10-16 17:18:43
【问题描述】:

我正在尝试为我的 Widget 搜索中的用户设置过滤。我希望他们能够点击多个标签,并且只返回应用了正确标签的结果。

表格设置如下:

  • 小部件有很多标签,通过标记
  • 标签有很多小部件,通过标签
  • 标签是一个连接表

在我的控制器中,我正在迭代小部件,并检查标签加入的条件:

@widgets = Widget.all
@current_tags = [1,5,7,10,15] # Passed in from params
@current_tags.each do |t|
  @widgets = @widgets.joins(:tags).where("tags.id=?", t)
end

我只希望返回具有所有这些标签的小部件。

这似乎适用于一个标签,但是一旦您单击另一个标签,一旦您选择了多个标签,就会产生问题。例如,这会按预期返回小部件 1:

# Widget 1 is joined to tags 1,5,7,9
@current_tags = [5]

这不会返回任何结果,即使它应该返回小部件 1:

# Widget 1 has tags 1,5,7,9
@current_tags = [5,7]

我在检查标签和小部件之间的连接方面做错了吗?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    您可能对acts-as-taggable-on gem 感兴趣,但至于您的问题,这是一个棘手的问题!您肯定可以使用一个强大的 Rails 习惯用法,但这里有一个蛮力方法:

    unless @current_tags.empty?
      # set initial state to widgets that match the first tag
      @widgets = @widgets.joins(:tags).where("tags.id=?", @current_tags.shift)
      @current_tags.each do |t|
        # then keep only widgets in initial state AND the next tag
        @widgets &= @widgets.joins(:tags).where("tags.id=?", t)
      end
    end
    

    【讨论】:

      【解决方案2】:

      您宁愿将查询更改为:

      @current_tags = [1,5,7,10,15]
      @widgets = Widget.joins(:tags).where("tags.id IN (?)", @current_tags)
      

      它正在通过用户选择的标签寻找小部件。

      【讨论】:

      • 这给了你一个 OR,发帖人要求一个 AND:“我只希望返回具有所有这些标签的小部件。”
      【解决方案3】:

      如果每个小部件只链接一次标签,您可以这样做:

      @widgets =
        Widget
          .select('widgets.id')
          .joins("INNER JOIN taggings ON taggings.widget_id = widgets.id")
          .where(tag_id: @current_tags)
          .group("widgets.id")
          .having("COUNT(*) = #{@current_tags.length}")
      

      这段代码的另一种写法:

      widget_ids =
        Tagging
          .select(:widget_id)
          .where(tag_id: @current_tags)
          .group(:widget_id)
          .having("COUNT(*) = #{@current_tags.length}")
      
      @widgets = Widget.where(id: widget_ids)
      

      【讨论】:

        【解决方案4】:

        一个有点尴尬的方法是让你的循环链 EXISTS 像这样:

        @widgets = Widget.all
        @current_tags = [1,5,7,10,15]
        @current_tags.each do |t|
          @widgets = @widgets.where(
            widget.taggings.where(tag_id: t).exists # << a bit of Arel for ya
          )
        end
        

        这应该理论上可行。在实践中,我已经看到类似这个错误的代码带有错误的参数绑定,即。 e. Rails 传入 one 参数,用于 two 占位符。您可能可以通过使用诸如.where('taggings.tag_id = ?', t) 之类的纯 SQL 条件来解决此问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-08-26
          • 1970-01-01
          • 2020-08-07
          • 2021-10-29
          • 2019-10-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多