Rails `find_by` returning huge ID - ruby-on-rails

Curious if anyone knows the intricacies of find_by since I've checked documentation and been unable to find info.
I know that find is used to find by primary keys like:
#user = User.find(params[:id]), returning the correct user.
Before I corrected my code it was #user = User.find_by(params[:id]) and returned a user with an ID way above the number of users in my DB.
Can anyone help me understand what is happening under the hood? What does find_by search by default when a parameter is omitted that is returning this strange user object?

find_by_field(value) is equivalent to where(field: value) but is not supposed to be used without appending a field name to the method like you mentioned. Moreover it returns only the first matching value. For example instead of doing User.where(name: 'John').limit(1) you can use: User.find_by_name 'John'.
On my side, using find_by with postgresql raises an error, when find_by_id does work:
User.find_by(1)
SELECT "users".* FROM "users" WHERE (1) ORDER BY "users"."email" ASC LIMIT 1
PG::DatatypeMismatch: ERROR: argument of WHERE must be type boolean, not type integer
User.find_by_id 1
SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."email" ASC LIMIT 1
<User id: 1, ...
User.where(id: 1) # note that there is no LIMIT 1 in the generated SQL
SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."email" ASC
You can use gem query_tracer to see the generated SQL or check this thread.

Please take a look here.
Here is an excerpt for 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.
Post.find_by name: 'Spartacus', rating: 4
Post.find_by "published_at < ?", 2.weeks.ago
Is that what you were looking for?
UPDATE
User.find_by(3) is equivalent to User.where(3).take
Here's the output from the console
pry(main)> User.where(3).take
#=> User Load (0.3ms) SELECT `users`.* FROM `users` WHERE (3) LIMIT 1

It's look like rails return to you an id of object in memory. It's like query User.find(params[:id]).object_id. But why it's happens? I try did same on my app with 4.2.4 version and all goes fine

Related

efficient rails query includes record (returns boolean)

My people have scores and I'd like an efficient way to query if the given user is in the top X users.
# person.rb
class Person
scope :top_score, -> {order('score DESC')}
scope :page_limit, -> { limit(10) }
def self.in_top_score(id)
top_score.page_limit.something_something_soemthign?
end
end
previously was doing:
user.id.in?(top_score.page_limit.pluck(:id))
but i'd prefer to move this check to the database to prevent the object serialization of hundreds/thousands of records.
Person.order('score DESC').select([:score, :id]).limit(1)
Person Load (0.5ms) SELECT score, id FROM `people` ORDER BY score DESC LIMIT 1
=> [#<Person id: "dxvrDy...", score: 35>]
now to check if another user exists in that list^^
Person.order('score DESC').select([:score, :id]).limit(1).exists?({id: "c_Tvr6..."})
Person Exists (0.3ms) SELECT 1 AS one FROM `people` WHERE `people`.`id` = 'c_Tvr6...' LIMIT 1
=> true
returns true but should return false
updated answer
Sorry, my original answer was incorrect. (The exists? query evidently uses LIMIT 1 and overwrites the LIMIT 10 from the page_limit scope, and evidently throws out the ORDER BY clause, too. Totally wrong! :-p)
What about this? It's a little bit less elegant, but I actually tested the answer this time :-p, and it seems to work as desired.
def self.in_top_score?(id)
where(id: id).where(id: Person.top_score.page_limit).exists?
end
Here's an example usage from my testing (using Rails 4.2.6) and the SQL it generates (which uses a subquery):
pry(main)> Person.in_top_score?(56)
Person Exists (0.4ms) SELECT 1 AS one FROM "people" WHERE "people"."id" = $1 AND "people"."id" IN (SELECT "people"."id" FROM "people" ORDER BY "people"."score" DESC LIMIT 10) LIMIT 1 [["id", 56]]
=> false
In my testing, this does indeed have at least a bit of a performance boost compared to your original version.
original answer
top_score.page_limit.exists?(user.id)
http://apidock.com/rails/ActiveRecord/FinderMethods/exists%3F

JOIN with COALESCE and ORDER BY in Rails

I just ran into an issue with Rails where I couldn't properly use JOIN and multiple ORDER BYs (with one of them being a COALESCE function) in the same query. Is this something that is illegal in SQL (doubtful) or is it just an issue with Rails' implementation? Also, how can I get around it?
# Works!
Post.joins(:author).order("COALESCE(title, '---') DESC")
=> SELECT "posts".* FROM "posts" INNER JOIN "authors" ON "posts"."author_id" = "authors"."id" ORDER BY COALESCE(title DESC, '--')
# Fails - syntax error in SQL
Post.joins(:author).order("COALESCE(title, '---') DESC").last #last should automatically apply an ORDER BY id DESC;
=> SELECT "posts".* FROM "posts" INNER JOIN "authors" ON "posts"."author_id" = "authors"."id" ORDER BY COALESCE(title DESC, '--') ASC LIMIT 1
when applying order("COALESCE(table.column, fallback_value) asc/desc") to my query I started getting the following error
ActiveRecord::UnknownAttributeReference: Query method called with non-attribute argument(s):
this was resolved by wrapping the order argument with Arel.sql as suggested by this Answer
order(Arel.sql("COALESCE(table.column, fallback_value) asc/desc"))
For more information on why this works I found this article from Thoughtbot to be helpful.
It looks like .last applies ASC LIMIT 1 to the SQL query. This seems to confuse the rails syntax parser and it moves the DESC into a strange place, which causes the error.
SELECT "discussion_topics".* FROM "discussion_topics" ORDER BY COALESCE(title DESC, '---') ASC LIMIT 1
as opposed to
SELECT "discussion_topics".* FROM "discussion_topics" ORDER BY COALESCE(title, '---') DESC
You could probably change the DESC to an ASC and use .first safely.
With Postgres I would try
Post.select("COALESCE(title, '---') DESC").joins(:author).order("COALESCE(title, '---') DESC").limit(1)

Rails update() method causing error for custom URL

I am to create custom URLs for my Rails app.
So far, I'm following the Friendy URLs documentation, as mentioned here https://gist.github.com/jcasimir/1209730.
I've replaced the default slug (ie: /books/4 ) with an attribute of the model (ie: /books/the-lord-of-the-rings ). In the book.rb model file:
# override to_param
# By default it returns the "slug" of the model
# (ie: #book.to_param => "1")
def to_param
title.parameterize
end
Consequently, I've over-written the Book.find() method, re-defined in the book.rb file as well.
# override find
# the "super" usage is Book.find(1) => object 1
# Now, it is Book.find("bookTitle") => object of that name
def self.find(input)
find_by_title(input)
end
This is added in order to be able to find my model without using the "slug", or ID of my model.
The issue arises when I try to update a Book entry. When #book.update(params[:book]) is called, this is the error I get from my server:
(0.2ms) BEGIN
Book Exists (0.5ms) SELECT 1 AS one FROM "books" WHERE ("books"."title" = 'nasty 22' AND "books"."id" != 62) LIMIT 1
Tag Load (0.6ms) SELECT "tags".* FROM "tags" WHERE "tags"."book_id" = $1 [["book_id", 62]]
Book Load (1.3ms) SELECT "books".* FROM "books" **WHERE "books"."title" = 62** LIMIT 1
PG::UndefinedFunction: ERROR: operator does not exist: character varying = integer
LINE 1: ...CT "books".* FROM "books" WHERE "books"."title" = 62 LIMIT...
The problematic line is
WHERE "books"."title" = 62
Somehow somewhere in the update method, it is reverting to trying to find my Book model using the ID instead of the Title.
Does anyone know how this problem can be solved?
Much appreciated.
If you just continue to follow the instructions in the gist it will fix your problem. You're going to have to deal with the fact that other callers will call Book.find passing an integer id, so you'll want to handle both cases. So, you'll need to check to determine if the parameter being passed is an integer ID or a title, and then call the appropriate find method (either super or find_by_title, respectively). It just won't work any other way, I would think, and find is a pretty basic method.

Rails validates uniqueness while updating object? - very strange

So, I have some simple User Model, and form for updating password.
#user.update_attributes(:password=>params[:password])
But this didn't work, and I figured out:
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."auth_token" = 'z7KU4I0IXLjiRMpdF6SOVQ' LIMIT 1
User Load (0.0ms) SELECT "users".* FROM "users" WHERE "users"."password_reset_token" = 'aMOjTN0ikPUOJo2JMVoDtQ' LIMIT 1
(0.0ms) BEGIN
User Exists (1.0ms) SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('somemail#mail.ru') AND "users"."id" != 1) LIMIT 1
(0.0ms) ROLLBACK
Redirected to http://localhost:3000/edit_user_by_reset?reset_token=aMOjTN0ikPUOJo2JMVoDtQ
By 3rd select I can tell, that here is uniqueness validation failed! And that is reason for ROLLBACK.
But it doesn't make sense, of course there is such row in DB, as it is UPDATE action. What should I do? I don't want pass :validate=>false here.
If you want to update password field only, you should not use mass_assignment method update_attributes, you should use update_attribute(:password, params[:user][:password]).
There is possible error with your params[:password] hash: if you use form_for #user then you should have params[:user][:password] and in common params[:user] for other fields.
You should check if the given user is valid (did you save him to DB without validation).
Check that your object is valid before you call update_attributes.
#user.valid?
I spent a long time trying to debug a similar issue only to find that my data was bad to start with. It had nothing to do with update_attributes. After modifying the culprit DB entry, my model became valid again after calling #user.find(id) and update_attributes worked as expected.

What is the difference between using .exists?, and .present? in Ruby?

I want to make sure I'm using them for the correct occasion and want to know of any subtleties. They seem to function the same way, which is to check to see if a object field has been defined, when I use them via the console and there isn't a whole lot information online when I did a google search. Thanks!
To clarify: neither present? nor exists? are "pure" ruby—they're both from Rails-land.
present?
present? is an ActiveSupport extension to Object. It's usually used as a test for an object's general "falsiness". From the documentation:
An object is present if it’s not blank?. An object is blank if it’s false, empty, or a whitespace string.
So, for example:
[ "", " ", false, nil, [], {} ].any?(&:present?)
# => false
exists?
exists? is from ActiveResource. From its documentation:
Asserts the existence of a resource, returning true if the resource is found.
Note.create(:title => 'Hello, world.', :body => 'Nothing more for now...')
Note.exists?(1) # => true
The big difference between the two methods, is that when you call present? it initializes ActiveRecord for each record found(!), while exists? does not
to show this I added after_initialize on User. it prints: 'You have initialized an object!'
User.where(name: 'mike').present?
User Load (8.1ms) SELECT "users".* FROM "users" WHERE "users"."name" = $1 ORDER BY users.id ASC [["name", 'mike']]
You have initialized an object!
You have initialized an object!
User.exists?(name: 'mike')
User Exists (2.4ms) SELECT 1 AS one FROM "users" WHERE "users"."name" = $1 ORDER BY users.id ASC LIMIT 1 [["name", 'mike']]
There is a huge difference in performance, and .present? can be up to 10x slower then .exists? depending on the relation you are checking.
This article benchmarks .present? vs .any? vs .exists? and explains why they go from slower to faster, in this order.
In a nutshell, .present? (900ms in the example) will load all records returned, .any? (100ms in the example) will use a SQLCount to see if it's > 0 and .exists? (1ms in the example) is the smart kid that uses SQL LIMIT 1 to just check if there's at least one record, without loading them all neither counting them all.
SELECT COUNT(*) would scan the records to get a count.
SELECT 1 would stop after the first match, so their exec time would be very different.
The SQL generated by the two are also different.
present?:
Thing.where(name: "Bob").present?
# => SELECT COUNT(*) FROM things WHERE things.name = "Bob";
exists?:
Thing.exists?(name: "Bob")
# => SELECT 1 AS one from things WHERE name ="Bob" limit 1;
They both seem to run the same speed, but may vary given your situation.
You can avoid database query by using present?:
all_endorsements_11 = ArtworkEndorsement.where(user_id: 11)
ArtworkEndorsement Load (0.3ms) SELECT "artwork_endorsements".* FROM "artwork_endorsements" WHERE "artwork_endorsements"."user_id" = $1 [["user_id", 11]]
all_endorsements_11.present?
=> true
all_endorsements_11.exists?
ArtworkEndorsement Exists (0.4ms) SELECT 1 AS one FROM "artwork_endorsements" WHERE "artwork_endorsements"."user_id" = $1 LIMIT 1 [["user_id", 11]]
=> true

Resources