【问题标题】:Rails: Filter on has_many_through association where all checked associations existRails:过滤所有检查关联都存在的 has_many_through 关联
【发布时间】:2015-01-28 03:23:20
【问题描述】:

我正在尝试构建一个复选框过滤器,这进一步减少了每个额外复选框的结果数量,其中每个复选框都代表 has_many through 关联中的一个关系。

我有一个具有以下型号的应用:

  • 医院
  • 医院特色
  • 功能

以下是关联:

class Hospital < ActiveRecord::Base
  has_many :hospital_features
  has_many :features, through: :hospital_features
end

class HospitalFeature < ActiveRecord::Base
  belongs_to :hospital
  belongs_to :feature
end

class Feature < ActiveRecord::Base
  has_many :hospital_features
  has_many :hospitals, through: :hospital_features
end

我有一个复选框表单,其中列出了所有可用的功能。

<%= label_tag("helipad", "Helipad") %>
<%= check_box_tag("features[]", "helipad" %>

<%= label_tag("telesurgery", "Telesurgery") %>
<%= check_box_tag("features[]", "telesurgery" %>

<%= label_tag("emergency room", "Emergency Room") %>
<%= check_box_tag("features[]", "emergency room" %>

我正在尝试像您在购物网站上一样进行过滤,其中每个复选框进一步过滤到仅具有所有选中功能的医院。

我现在使用的查询:

hospitals = Hospital.joins(:features).where(features: {name: features} )

反之。每选中一个额外的框都会增加结果的数量,因为它返回的是具有任何一个选中特征的医院,而不是具有所有选中特征的医院。

因此,如果您选中“直升机停机坪”和“远程外科手术”,它应该只返回同时具有“直升机停机坪”和“远程外科手术”的医院,而不是任何具有“直升机停机坪”或“远程外科手术”的医院。

环顾四周,似乎找不到明显的解决方案。我很感激任何帮助。提前致谢。

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    也许这不是最漂亮的解决方案,但它对我有用。

    Hospital.joins(:features).where(features: {name: features}).group('hospitals.id').having("count(*) >= ?", features.size)
    

    【讨论】:

    • 我认为这种方法的问题是我用点符号添加 AND 条件没有问题。我可以通过简单地添加另一个where 来做到这一点。问题是我事先不知道要检查多少个框,所以我不知道要包括多少个wheres。如果有办法动态添加任意数量的wheres,那将是一个有效的解决方案。
    • 对不起,我收回了。这个 AR:Hospital.joins(:features).where(, 3).where("feature_id = ?", 4) 产生这个 SQL:SELECT "hospitals".* FROM "hospitals" INNER JOIN "hospital_features" ON "hospital_features"."hospital_id" = "hospitals"."id" INNER JOIN "features" ON "features"."id" = "hospital_features"."feature_id" WHERE (feature_id = 3) AND (feature_id = 4) 这显然是错误的,因为它需要一行有 2 个不同的 feature_id,这是不可能的。我又迷路了。
    • 第二次尝试。你应该把它放在另一个答案中,这样我就会看到变化。这最终与我最终使用的解决方案非常相似,即: hospital = Hospital.joins(:features) .where('features.name in (?)', searched_features) .group("hospitals.id") .having ('COUNT() = ?', searched_features.length) 这会为该医院拥有的每个特征返回同一医院。因此,如果列表包含 4 个特征,并且一家医院拥有全部 4 个特征,则该医院将被返回 4 次。因此,然后您按医院 ID 分组并添加等于过滤特征数的“HAVING COUNT()”。
    【解决方案2】:

    找到了解决办法。首先将医院加入特征,然后过滤 where 'features.name in (?)' 将特征数组插入到查询中。

    因此,这将为该医院的每个功能返回同一家医院。因此,如果列表包含 4 个特征,并且一家医院拥有全部 4 个特征,则该医院将被返回 4 次。同样,如果它只有 4 个特征中的 3 个,它将返回 3 次。

    然后您按医院 ID 分组并添加等于过滤特征数的“HAVING COUNT(*)”。

    那么最终结果是:

      hospitals = Hospital.joins(:features)
        .where('features.name in (?)', searched_features)
        .group("hospitals.id")
        .having('COUNT(*) = ?', searched_features.length)
    

    希望这最终对其他人有所帮助。如果有人找到更优雅的方法来做到这一点,请告诉我。

    【讨论】:

      【解决方案3】:

      我认为有很多选项可以做到这一点——对我来说效果很好的一个是 Sunspot Solr 过滤面。 Railscasts 很好地解释了它:http://railscasts.com/episodes/278-search-with-sunspot

      【讨论】:

        【解决方案4】:

        使用我的Where Exists gem:

        result = Hospital.all
        features.each do |feature|
          result = scope.where_exists(:features, name: feature)
        end
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-10-16
          • 1970-01-01
          • 1970-01-01
          • 2015-01-27
          • 1970-01-01
          相关资源
          最近更新 更多