Mixing ActiveRecord find Conditions

I want to find records on a combination of created_on >= some date AND name IN some list of names.

For ">=" I'd have to use sql condition. For "IN" I'd have to use a hash of conditions where the key is :name and the value is the array of names.

Is there a way to combine the two?

Answers


You can use named scopes in rails 2.1 and above

Class Test < ActiveRecord::Base
  named_scope :created_after_2005, :conditions => "created_on > 2005-01-01"
  named_scope :named_fred, :conditions => { :name => "fred"}
end

then you can do

Test.created_after_2005.named_fred

Or you can give named_scope a lambda allowing you to pass in arguments

Class Test < ActiveRecord::Base
  named_scope :created_after, lambda { |date| {:conditions => ["created_on > ?", date]} }
  named_scope :named, lambda { |name| {:conditions => {:name => name}} }
end

then you can do

Test.created_after(Time.now-1.year).named("fred")

If you're using an older version Rails, Honza's query is close, but you need to add parentheses for the strings that get placed in the IN condition:

Person.find(:all, :conditions => ["created_at > ? AND name IN (?)", date, names])

Using IN can be a mixed bag: it's fastest for integers and slowest for a list of strings. If you find yourself using just one name, definitely use an equals operator:

Person.find(:all, :conditions => ["created_at > ? AND name = ?", date, name])

The cool thing about named_scopes is that they work on collections too:

class Post < ActiveRecord::Base
  named_scope :published, :conditions => {:status => 'published'}
end

@post = Post.published

@posts = current_user.posts.published

For more on named_scopes see Ryan's announcement and the Railscast on named_scopes

class Person < ActiveRecord::Base
  named_scope :registered, lambda { |time_ago| { :conditions => ['created_at > ?', time_ago] } }
  named_scope :with_names, lambda { |names| { :conditions => { :names => names } } }
end

If you are going to pass in variables to your scopes you have to use a lambda.


You can chain the where clause:

Person.where(name: ['foo', 'bar', 'baz']).where('id >= ?', 42).first


The named scopes already proposed are pretty fine. The clasic way to do it would be:

names = ["dave", "jerry", "mike"]
date = DateTime.now
Person.find(:all, :conidtions => ["created_at > ? AND name IN ?", date, names])

I think I'm either going to use simple AR finders or Searchgasm.


Need Your Help

bash script, create array of all files in a directory

arrays bash shell while-loop

I have a directory myDir of many .html files. I am trying to create an array of all the files in the directory so I might be able to index the array and be able to refer to particular html files ...

Print a list of space-separated elements in Python 3

python python-3.x pretty-print

I have a list L of elements, say natural numbers. I want to print them in one line with a single space as a separator. But I don't want a space after the last element of the list (or before the f...