【问题标题】:Advanced search with many to many in Rails 4Rails 4 中的多对多高级搜索
【发布时间】:2019-09-09 21:12:11
【问题描述】:

我有一个房地产清单,其中每个房地产都有一些特点。然后,我有 2 张桌子:

  • real_estates,用于存储用户添加的所有房地产。
  • re_home_features,用于存储管理员添加的所有默认功能,如游泳池、壁橱、办公室、花园以及房地产可以拥有的许多功能

同一个房地产可以有很多特征,同样的特征可以有很多房地产。我创建了这些模型:

real_estate.rb

class RealEstate < ActiveRecord::Base
    has_many :real_estate_home_features
    has_many :re_home_features, as: :home_features, through: :real_estate_home_features, dependent: :destroy
end

re_home_features.rb

class ReHomeFeature < ActiveRecord::Base
    has_many :real_estate_home_features
    has_many :real_estates, through: :real_estate_home_features
end

real_estate_home_feature.rb

class RealEstateHomeFeature < ActiveRecord::Base
    belongs_to :real_estate
    belongs_to :re_home_feature
end

这样,多对多的关系就可以正常工作了。

我使用一些参数搜索房地产,例如:

  • 客厅数量
  • 浴室数量
  • 售价(最低到最高)
  • 总面积(最小到最大)
  • 房地产代码
  • 还有很多其他参数

我的搜索是这样的:

real_estates_controller.rb

def search
    r = real_estates
    r = r.where(code: params['code']) if params['code'].present?
    r = r.where(city_name: params['city']) if params['city'].present?
    r = r.where(garage: params['garage']) if params['garage'].present?
    r = r.where(restrooms: params['restrooms']) if params['restrooms'].present?

    r = r.paginate(:page => params[:page], :per_page => 10)

    r
end

此搜索也运行良好。这样做没有问题,因为所有参数都在同一个表 real_estates 中。

但是现在,搜索有点复杂。我必须搜索具有特定功能的房地产。示例:我想要所有房地产,其中有 4 个卫生间、2 辆汽车在车库并且有 游泳池。

在我的示例中,搜索具有 4 个卫生间和 2 辆汽车的房地产给我返回了 50 个房地产,但其中只有 15 个房地产有游泳池。

如何过滤这 50 个不动产以仅显示与“池功能”相关的记录?

我无法在视图中进行验证,因为会导致每页的编号错误。我认为过滤器必须发生在数据库查询时刻,就在分页之前。

感谢您的帮助!

环境

rails -v: Rails 4.2.1
ruby -v: ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
so: Ubuntu 16.04.5 LTS
localhost database: MySQL (development)
server database: Postgres (production)

编辑 1

根据@jvillian 的回答,我将更改我的控制器,在查询中添加一个 JOIN。

if params['home_features'].present? && params['home_features'].any?
    r = r.joins(:re_home_features).where(re_home_features: {id: params['home_features']}).distinct(:id)
end

在我的测试中,我有:

params['home_features'] = [1 , 2, 3]

我有 1 个具有这 3 个功能的房地产。但是,在视图中,我得到了相同的站点显示了 3 次。如果我添加了不同的,房地产只显示一次。但是...如果我将参数更改为:

params['home_features'] = [1 , 2, 3, 500]

我没有具有这 4 个功能的房地产,但结果是一样的。 没有明显区别,房地产被展示了3次。使用 dinstict,房地产显示一次。预期结果为零,因为我想要具有所有选定功能的房地产。

但我想我们快到了!我将提供一些关于我的模型的信息:

表格不动产

id | title      | description | code | city_name      | garage | ...
 7 | Your House | Awesome...  | 1234 | Rio de Janeiro | 4

表 re_home_features

id | name
 1 | Pool
 2 | Garden
 3 | Tenis court
 4 | Closet

table real_estate_home_features - 多对多关联

id | real_estate_id | re_home_feature_id
 1 |              7 |                  1
 2 |              7 |                  2
 3 |              7 |                  3

如果我跑:

r = r.joins(:re_home_features).where(re_home_features: {id: [1,2,3,500]}).distinct(:id)

我收到了这些查询(rails 控制台):

SELECT DISTINCT `real_estates`.* FROM `real_estates` INNER JOIN `real_estate_home_features` ON `real_estate_home_features`.`real_estate_id` = `real_estates`.`id` INNER JOIN `re_home_features` ON `re_home_features`.`id` = `real_estate_home_features`.`re_home_feature_id` WHERE `re_home_features`.`id` IN (1, 2, 3, 500)

它返回 1 个结果。房地产 id 7。如果我删除不同的,我有 3 个结果:相同的房地产,3 次。

预期结果为零。如果params['home_features'] = [1 , 2, 3] 我希望得到 1 个结果。

编辑 2

此连接方法有效,但返回类似于“OR”查询。就我而言,我需要一个带有“AND”的连接查询。查询必须是:“返回所有具有特征 1 AND 2 AND 3 的房地产”。

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 activerecord rails-activerecord ruby-on-rails-4.2


    【解决方案1】:

    我不确定这是“高级搜索”。在12.1.4 Specifying Conditions on the Joined Tables 下的指南中介绍了对连接模型的搜索。它看起来像:

    real_estates.joins(:re_home_features).where(re_home_features: {feature_name: 'pool'})
    

    当然,这可能不会完全起作用,因为您没有告诉我们太多关于如何找到“池功能”的信息。但是,它应该给你正确的方向。

    顺便说一句,原因如下:

    params['home_features'] = [1 , 2, 3, 500]
    

    返回具有any 的home_features 的记录是因为它使用了“或”。如果你想要 real_estates 和 all home_features,那么你想要一个“和”。你可以google一下。

    我想我会尝试类似:

    def search
    
        r = real_estates.joins(:re_home_features)
    
        %i(
          code
          city_name
          garage
          restrooms
        ).each do |attribute|
          r = r.where(attribute => params[attribute]) unless params[attribute].blank?
        end
    
        params[:home_features].each do |home_feature_id|
          r = r.where(re_home_features: {id: home_feature_id})
        end unless params[:home_features].blank?
    
        r = r.uniq
        r = r.paginate(:page => params[:page], :per_page => 10)
        r
    
    end
    

    注意:我将 params[:city] 更改为 params[:city_name] 以使第一个 each 迭代器更干净。如果你想这样做,你需要改变你的观点。如果你不想这样做,那么你可以回到你已经拥有的非迭代器方法。

    这未经测试,所以我不相信它会完全按照介绍的那样工作。

    【讨论】:

    • 你好@jvillian,谢谢你的回答!我马上试试!
    • 不起作用,但这是一个好方法。我现在得走了,但 2 小时后我会测试更多!
    • 是的。我在我的回答中猜想它不会起作用,因为你没有告诉我们关于你的ReHomeFeature 模型的足够多的信息。为您的问题添加更多细节以获得更好的答案。
    • 嘿@jvillian!你的方法是对的!我现在可以看到结果了。我将用结果更新我的问题,并提供有关我的模型的更多信息。
    • 太棒了!随时为未来的搜索者投票/接受。
    猜你喜欢
    • 1970-01-01
    • 2023-03-10
    • 2014-06-08
    • 1970-01-01
    • 2016-01-01
    • 2018-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多