开发者

How to merge multiple 'having' clause relation in rails 3

Facing problem for merging multiple having clause ... As in my model i had two scopes written

1.)

开发者_运维问答
 scope :vacant_list, lambda {|daterange|
    where("vacancies.vacancy_date" => daterange , "vacancies.availability" => ["Y" ,"Q"]).joins(:vacancies).group("vacancies.property_id").having("count(vacancies.vacancy_date) >= #{daterange.count}") unless daterange.blank?
  }

2.)

scope :amenity_type, lambda {|term|
    where("amenities.name" => term).joins(:amenities).group("amenities.property_id").having("count(amenities.name) >= #{term.size}") unless term.blank?
  }

I need to do something like this

@model = Model.vacant_list(daterange).amenity_type(term)

But i always get wrong number of arguments (2 for 1)

/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.10/lib/arel/select_manager.rb:100:in `having'
/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.6/lib/active_record/relation/query_methods.rb:180:in `build_arel'
/home/vivek/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.6/lib/active_record/relation/query_methods.rb:149:in `arel'.

If i remove any one of scopes having clause then the above action works perfectly . Is there any way to like- @model = Model.vacant_list(daterange) and then remove the active record relation and then apply @model.amenity_type(term). I tried lots of things but didnt find any solution for this.....


I think you're doing this wrong - it took me quite a while to dig out what the actual intent of the 'having' clauses above was. From what I can tell, it looks like the idea is that you pass in an array of dates or amenities and want to find properties that match all of them.

The underlying issue is that (AFAIK) code like this will NOT do the right thing:

# NOTE: WILL NOT WORK
scope :vacant_on, lambda { |date| where('vacancies.vacancy_date' => date, "vacancies.availability" => ["Y" ,"Q"]).joins(:vacancies) }

scope :vacant_list, lambda {|daterange|
  daterange.inject(self) { |rel, date| rel.vacant_on(date) }
}

Unless this has changed in Arel (haven't poked it much) then this fails because you end up with exactly one join to the vacancies table, but multiple where clauses that specify incompatible values. The solution is to do multiple joins and alias them individually. Here's how I used to do it in Rails 2:

named_scope :vacant_on, lambda { |date|
    n = self.connection.quote_table_name("vacancies_vacant_on_#{date}")
    { :joins => "INNER JOIN vacancies AS #{n} ON #{n}.property_id = properties.id",
      :conditions => ["#{n}.vacancy_date = ? AND #{n}.availability IN (?)", date, ["Y","Q"]] }
}

Explicitly specifying an 'AS' here lets multiple versions of this scope coexist in one query, and you get the results you'd expect.

Here's a rough translation of this to modern Arel syntax:

scope :vacant_on, lambda { |date|
    our_vacancies = Vacancy.arel_table.alias
    joins(our_vacancies).on(our_vacancies[:property_id].eq(Property.arel_table[:id])).
    where(our_vacancies[:vacancy_date].eq(date),
          our_vacancies[:availability].in(["Y" ,"Q"]))
}

Haven't tried it, but this post:

http://www.ruby-forum.com/topic/828516

and the documentation seem to imply it would do the right thing...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜