I have this code
def evaluate(collection)
if collection.none?
[]
else
collection.group(#group).pluck(*#columns)
end
end
The collection is an ActiveRecord::Relation object - for e.g. User.where(:name => 'Killer')
Now sometimes I also pass the Rails 4 none relation Users.none, that's why the check for none. If I do not check for none?, the call to pluck throws an arguments exception.
The problem is whenever I query any relation for none? it executes the query. See here:
> User.where(id: 1).none?
User Load (0.2ms) SELECT "users".* FROM "v1_passengers" WHERE "users"."id" = 1
=> false
> User.where(id: 1).none.none?
=> true
I do not want to execute to query just to check for none. Any workarounds?
Update: The none? method is actually array method thats why the query is executed. It's like calling to_a on the relation. What I want to know is how to figure out if the relation is a none
Found one method to do this without firing query. When you call none on a relation it appends the ActiveRecord::NullRelation to the extending_values array of the relation:
> User.where(id: 1).extending_values.include?(ActiveRecord::NullRelation)
=> false
> User.where(id: 1).none.extending_values.include?(ActiveRecord::NullRelation)
=> true
Not sure you can, there's no distinguishing feature between a Null Relation and an Actual Relation. Perhaps go down the rescue route:
begin
collection.group(#group).pluck(*#columns)
rescue #add exact Exception to catch
[]
end
Not exactly clean but gets round the problem
I've got a weird issue... In a model User, that has no relation with the model Interest, I try to call this query:
# file model.rb
def self.my_func
Interest.where('id IN (?)', [1,2])
end
But it's completely ignored... and if I replace by this:
# file model.rb
def self.my_func
Interest.find(1)
end
the .find() method is triggered and I get result.
If I directly call Interest.where('id IN (?)', [1,2]) in rails console it works...
I'm on RoR 3.2.13
Any idea?
Thank you all.
Rails doesn't evaluate the query until you actually try to access the results. Calling Model.where just returns an ActiveRecord::Relation onto which you can chain additional where/order/etc calls.
Try this (.all forces the query to be evaluated and returns an array of results):
def self.my_func
Interest.where(id: [1,2]).all
end
Note that should shouldn't actually do this. It's much better for your model to just allow the ActiveRecord::Relation to be returned from the method, so the calling code can apply additional scoping/ordering methods to it.
Also note that, instead of manually building an id in (?) query, Rails is smart enough to do it for you if you just use where(id: [1,2]).
Bonus notes:
On the terminal, the results get evaluated from Model.where immediately because IRB invokes inspect on the result of each expression you enter so it has something to print, and the returned ActiveRecord::Relation evaluates its query when inspected. You can bypass this to prove the point to yourself by adding ;nil, so that your statement evaluates to nil; note that the SELECT doesn't happen until I manually call x.inspect:
irb(main):008:0> x = User.where("name like 'bob'"); nil
=> nil
irb(main):009:0> x.inspect
User Load (0.5ms) SELECT "users".* FROM "users" WHERE (name like 'bob')
=> "[]"
I'm new to rails. Just wondering which is the better approach that will return nil if the subject_id can't be found:
#subject = Subject.find_by_id(params[:subject_id])
or
#subject = Subject.where(:id => params[:subject_id]).first
Thanks.
I prefer find_by as the name is descriptive and you get the object with out having to call a second function (i.e. first)
User.find(9) # returns User object. Throws exception when not found.
User.find_by(id: 9) # returns User object. Returns nil when not found.
User.where(id: 9).first # returns User object. Returns nil when not found.
They both generate the same SQL statement:
1.9.3p194 :003 > Example.find_by_id(9)
Example Load (0.3ms) SELECT "examples".* FROM "examples" WHERE "examples"."id" = 9 LIMIT 1
nil
1.9.3p194 :004 > Example.where(:id => 9).first
Example Load (0.3ms) SELECT "examples".* FROM "examples" WHERE "examples"."id" = 9 LIMIT 1
nil
So they'll have the same performance characteristics at the database. There may be a slight difference in the Rails code for find_by_*_ vs. where, but I'd imagine that will be negligible compared to query time.
Edit: In light of Ryan Bigg's comment below, I'd have to suggest the second form for forward compatibility.
I am new to rails. What I see that there are a lot of ways to find a record:
find_by_<columnname>(<columnvalue>)
find(:first, :conditions => { <columnname> => <columnvalue> }
where(<columnname> => <columnvalue>).first
And it looks like all of them end up generating exactly the same SQL. Also, I believe the same is true for finding multiple records:
find_all_by_<columnname>(<columnvalue>)
find(:all, :conditions => { <columnname> => <columnvalue> }
where(<columnname> => <columnvalue>)
Is there a rule of thumb or recommendation on which one to use?
where returns ActiveRecord::Relation
Now take a look at find_by implementation:
def find_by
where(*args).take
end
As you can see find_by is the same as where but it returns only one record. This method should be used for getting 1 record and where should be used for getting all records with some conditions.
Edit:
This answer is very old and other, better answers have come up since this post was made. I'd advise looking at the one posted below by #Hossam Khamis for more details.
Use whichever one you feel suits your needs best.
The find method is usually used to retrieve a row by ID:
Model.find(1)
It's worth noting that find will throw an exception if the item is not found by the attribute that you supply. Use where (as described below, which will return an empty array if the attribute is not found) to avoid an exception being thrown.
Other uses of find are usually replaced with things like this:
Model.all
Model.first
find_by is used as a helper when you're searching for information within a column, and it maps to such with naming conventions. For instance, if you have a column named name in your database, you'd use the following syntax:
Model.find_by(name: "Bob")
.where is more of a catch all that lets you use a bit more complex logic for when the conventional helpers won't do, and it returns an array of items that match your conditions (or an empty array otherwise).
Model.find
1- Parameter: ID of the object to find.
2- If found: It returns the object (One object only).
3- If not found: raises an ActiveRecord::RecordNotFound exception.
Model.find_by
1- Parameter: key/value
Example:
User.find_by name: 'John', email: 'john#doe.com'
2- If found: It returns the object.
3- If not found: returns nil.
Note: If you want it to raise ActiveRecord::RecordNotFound use find_by!
Model.where
1- Parameter: same as find_by
2- If found: It returns ActiveRecord::Relation containing one or more records matching the parameters.
3- If not found: It return an Empty ActiveRecord::Relation.
There is a difference between find and find_by in that find will return an error if not found, whereas find_by will return null.
Sometimes it is easier to read if you have a method like find_by email: "haha", as opposed to .where(email: some_params).first.
Since Rails 4 you can do:
User.find_by(name: 'Bob')
which is the equivalent find_by_name in Rails 3.
Use #where when #find and #find_by are not enough.
The accepted answer generally covers it all, but I'd like to add something,
just incase you are planning to work with the model in a way like updating, and you are retrieving a single record(whose id you do not know), Then find_by is the way to go, because it retrieves the record and does not put it in an array
irb(main):037:0> #kit = Kit.find_by(number: "3456")
Kit Load (0.9ms) SELECT "kits".* FROM "kits" WHERE "kits"."number" =
'3456' LIMIT 1
=> #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",
updated_at: "2015-05-12 06:10:56", job_id: nil>
irb(main):038:0> #kit.update(job_id: 2)
(0.2ms) BEGIN Kit Exists (0.4ms) SELECT 1 AS one FROM "kits" WHERE
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" =
1 [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]]
(0.6ms) COMMIT => true
but if you use where then you can not update it directly
irb(main):039:0> #kit = Kit.where(number: "3456")
Kit Load (1.2ms) SELECT "kits".* FROM "kits" WHERE "kits"."number" =
'3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456",
created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58",
job_id: 2>]>
irb(main):040:0> #kit.update(job_id: 3)
ArgumentError: wrong number of arguments (1 for 2)
in such a case you would have to specify it like this
irb(main):043:0> #kit[0].update(job_id: 3)
(0.2ms) BEGIN Kit Exists (0.6ms) SELECT 1 AS one FROM "kits" WHERE
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1
[["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
(0.5ms) COMMIT => true
Apart from accepted answer, following is also valid
Model.find() can accept array of ids, and will return all records which matches.
Model.find_by_id(123) also accept array but will only process first id value present in array
Model.find([1,2,3])
Model.find_by_id([1,2,3])
The answers given so far are all OK.
However, one interesting difference is that Model.find searches by id; if found, it returns a Model object (just a single record) but throws an ActiveRecord::RecordNotFound otherwise.
Model.find_by is very similar to Model.find and lets you search any column or group of columns in your database but it returns nil if no record matches the search.
Model.where on the other hand returns a Model::ActiveRecord_Relation object which is just like an array containing all the records that match the search. If no record was found, it returns an empty Model::ActiveRecord_Relation object.
I hope these would help you in deciding which to use at any point in time.
Suppose I have a model User
User.find(id)
Returns a row where primary key = id. The return type will be User object.
User.find_by(email:"abc#xyz.com")
Returns first row with matching attribute or email in this case. Return type will be User object again.
Note :- User.find_by(email: "abc#xyz.com") is similar to User.find_by_email("abc#xyz.com")
User.where(project_id:1)
Returns all users in users table where attribute matches.
Here return type will be ActiveRecord::Relation object. ActiveRecord::Relation class includes Ruby's Enumerable module so you can use it's object like an array and traverse on it.
Both #2s in your lists are being deprecated. You can still use find(params[:id]) though.
Generally, where() works in most situations.
Here's a great post: https://web.archive.org/web/20150206131559/http://m.onkey.org/active-record-query-interface
The best part of working with any open source technology is that you can inspect length and breadth of it.
Checkout this link
find_by ~> Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself. If no record is found, returns nil.
find ~> Finds the first record matching the specified conditions , but if no record is found, it raises an exception but that is done deliberately.
Do checkout the above link, it has all the explanation and use cases for the following two functions.
I will personally recommend using
where(< columnname> => < columnvalue>)
I've got the following code in my controller:
#items = Item.where(:user_id => 1).order("updated_at DESC").limit(2)
#oldest_item = #items.last
For some reason, and I'm guessing this has to do with my recent upgrade to Rails 3, #oldest_item is not being set to the last item in #items, but is instead being set to the last item that matches Item.where(:user_id => 1).order("updated_at DESC").
So imagine there are 3 items that match, A, B, and C. #items is being set to [A, B], and then #oldest_item is being set to C.
Oddly, when I call #items.last from within my view, it is properly returning B.
When I paste in the two lines from my controller into the console, it also properly returns B.
Can someone explain to me what the heck is going on here?
For some reason, ActiveRecord::Relation is ignoring the limit option.
In Rails 3, ActiveRecord doesn't actually execute your query until need to access the results. Calling last does this (but again, ignores the limit).
You can tell ActiveRecord to execute the query by calling all on your query. Then, when you run last on that, it'll give you the "last" record you're looking for.
#items = Item.where(:user_id => 1).order("updated_at DESC").limit(2)
# #items is an ActiveRecord::Relation here
#oldest_item = #items.last
# Returns "C" instead of "B". This is actually ignoring the `limit` parameter
#items = Item.where(:user_id => 1).order("updated_at DESC").limit(2).all
# #items is an Array of ActiveRecord objects here
#oldest_item = #items.last
# Returns "B"
This doesn't seem like expected behavior to me. I've filed a bug in the rails issues tracker.
Update: #BaroqueBobcat submitted a patch which got accepted, so it should be fixed in the upcoming 3.1 release of Rails.
I'm using Rails 6 and had a similar issue. I used reload on my record to reload it from the database.
account = Account.new
# => #<Account id: nil, email: nil>
account.id = 1
account.reload
# Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]]
# => #<Account id: 1, email: 'account#example.com'>