Why are my database queries limited to 1? - ruby-on-rails

Here's my controller query:
#statuses = Status.find_by user_id: (params[:id])
and here's its output:
Status Load (0.2ms) SELECT "statuses".* FROM "statuses" WHERE "statuses"."user_id" = 2 LIMIT 1
Just curious as to where that LIMIT 1 is coming from...

From the documentation:
find_by finds the first record matching some conditions
use where instead to find all
Status.where(user_id: params[:id])
As a note, if you follow the logic of find_by, you'll see it basically does:
where(*args).limit(1).to_a.first

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

Rails `find_by` returning huge ID

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

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.

How to stack rails helper methods in controller?

I'd like to
#cart = Cart.find_by(id: session[:cart], :limit => 1)
but that throws out an error SQLite3::SQLException: no such column: carts.limit: SELECT "carts".* FROM "carts" WHERE "carts"."id" = 1 AND "carts"."limit" = 1 LIMIT 1 so how can I use multiple methods on a single global variable? Version: rails 4.0.1
It appears you mean to specify a limit of one record to be returned from find_by.
However, find_by only returns one record anyway, so this is not needed. Your :limit gets interpreted as a table column, which of course doesn't exist.
You can omit it entirely.
#cart = Cart.find_by(id: session[:cart])
If you're going to be querying by id, you can simply do:
ruby #cart = Card.find(session[:cart])
as find will do a SELECT ... LIMIT 1 by the primary key.

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