Rails ActiveRecord Join Confusion - ruby-on-rails

In the console this code:
Patient.joins(:notes,:recordings).find(1)
Returns
ActiveRecord::RecordNotFound: Couldn't find Patient with id=1
Which is bizarre because in the same console Patient.find(1) works without any issue and retrieves the record of the patient with id 1.
My understanding is that I should be able to do:
a = Patient.joins(:notes,:recordings).find(1)
a.notes
a.recordings
And a.notes should return all the notes associated with the patient with id 1 and same for a.recordings. It's clear I'm missing something here...any ideas?

That's because rails does INNER JOIN by default. your patient 1 doesn't have either notes or recordings
If can do a left join instead.
Patient.joins("LEFT JOIN notes on notes.patient_id = patients.id")
.joins("LEFT JOIN recordings on recordings.patient_id = patients.id")
.find(1)
or load the patient and then load the associations
a = Patient.find(1)
a.notes
a.recordings

You may be interested in include type functionality (for eager loading). See: Rails :include vs. :joins

Related

How can I merge two active record relation with OR condition in rails 3 and return result also an active relation not array?

I have two associations like surgical_diseases and eye_disease.I want to get the Ored result of the two active relation.But the below code gave me an array.
has_many :surgical_diseases
has_many :eye_disease
scope :all_disease ->(name) { joins(:surgical_diseases).where('surgical_diseases.name IN (?)') | joins(:eye_disease).where('eye_disease.name IN (?)') }
I have seen active-record-union gem but that would only work with active-record 4.I am currently using rails 3.2 so not able to use that.
I also saw that this functionality will come with rails5 with dhh's commit.But not sure how will I fix this with rail3 now.
I tried my best to make understanding of my question.Please let me know if anything else information is require.
Thanks in advance!
You would probably need to get the ids using find_by_sql and then find those ids to get ActiveRecord::Relation.
scope :all_disease ->(name) {
ids = YourTable.find_by_sql <<-SQL
SELECT your_table.id FROM your_table INNER JOIN surgical_diseases sd ON sd.your_table_id=your_table.id WHERE sd.name IN (#{name})
UNION
SELECT your_table.id FROM your_table INNER JOIN eye_diseases ed ON ed.your_table_id=your_table.id WHERE ed.name IN (#{name})
SQL
YourTable.where(id: ids)
}
Perhaps, left outer join can help you:
scope :all_disease ->(name) {
joins('LEFT OUTER JOIN surgical_diseases ON surgical_diseases.whatever_table_for_your_models_id = whatever_table_for_your_models.id')
.joins('LEFT OUTER JOIN eye_diseases ON eye_diseases.whatever_table_for_your_models_id = .whatever_table_for_your_models.id')
.where('surgical_diseases.name IN (?) OR eye_diseases.name IN (?)', name)

Rails includes association with condition in left join

Can I add some condition to the LEFT JOIN sql that Rails generate for the includes method? (Rails 4.2.1, postresql).
I need to get all(!) the users with preloading ( not N+1 when I will puts in a view count of comments, posts and etc) of associations, but associations need to be filtered by some conditions.
Example:
User.includes(:comments)
# => SELECT * FROM users LEFT JOIN comments ON ...
This will return all the users and preload comments if they exists.
If I will add some conditions for the "comments" association in where, then SQL doesn't return ALL the users, for example:
User.includes(:comments).where(comments: {published_at: Date.today})
# => SELECT * FROM users LEFT JOIN comments ON ... WHERE comments.published_at = ...
This will return only users, that have comments, published today.
I need to put conditions inside LEFT JOIN AND save preloading (load objects to the memory - simple left join with joins method doesn't preload associations).
SELECT * FROM users LEFT JOIN comments ON (... AND comments.published_at = ...)
Those SQL will return right what I need (all the users, and their comments, published in requested date, if they exists)! But ... I cant generate it with the Rails includes method, and `joins' doesn't preload associations.
What do you advice me? Thanks!
Rails doesn't have methods in the framework library to do what you want.
This might work, though
class User < ActiveRecord::Base
has_many :comments
has_many :recent_comments, -> { where(published_at: Date.today) }, class_name: "Comment"
end
Then query for Users and preload recent_comments
#users = User.preload(:recent_comments)

Rails 4: Joins in ActiveRecord relation lambdas not include when doing a join

I'm creating a Revision system for a project where a base table contains the current revision for a given id, and a revision table contains the data tagged with a given revision, eg:
foos
- id
- revision
foo_revisions
- foo_id
- revision
{data}
For relations between these I have used the lamda syntax to specify conditions on the relation like this:
class Article
belongs_to :product, ->{ joins(:base).where("products.revision = product_revisions.revision") }, :class_name=> "Product::Revision", :primary_key => :product_id
Where article is not revisioned, but product is (Product::Revision is the model that contains the actual data, and is a ActiveRecord::Base mapping to product_revisions, while Product maps to products).
The :base relation is from Product::Revision to Product
This works fine for the normal things like
a = Article.find(..)
a.product
which products the sql (a.product only)
SELECT `product_revisions`.* FROM `product_revisions`
INNER JOIN `products` ON `products`.`id` = `product_revisions`.`product_id`
WHERE `product_revisions`.`product_id` = 406
AND (products.revision = product_revisions.revision) ORDER BY `product_revisions`.`id` ASC LIMIT 1
But when I do Article.joins(:product) it fails, since it doesn't join in the products table:
SELECT `articles`.* FROM `articles` INNER JOIN `product_revisions`
ON `product_revisions`.`product_id` = `articles`.`product_id`
AND (products.revision = product_revisions.revision)
with the error:
Mysql2::Error: Unknown column 'products.revision' in 'on clause'
To me it seems like ActiveRecord simply ignores the joins in the lamba when it does the joins query, which seems stupid. Is this a bug, or is there a better/correct way to do this?
I've encountered a similar problem. Any joins specified in a lambda for a has_many are silently ignored.
I found this in the Rails issues that solves the problem for me:
https://github.com/rails/rails/pull/11518
The author mentions the problem occurring when there is an order clause but I think this muddies the water - it makes no difference whether there is an order clause or not.
I cannot say whether this is a bug or intended behaviour but I suspect the former.

How to write query in active record to select from two or more tables in rails 3

I don't want to use join
I want to manually compare any field with other table field
for example
SELECT u.user_id, t.task_id
FROM tasks t, users u
WHERE u.user_id = t.user_id
how can i write this query in Rails ??
Assuming you have associations in your models, you can simply do as follow
User.joins(:tasks).select('users.user_id, tasks.task_id')
you can also do as follow
User.includes(:tasks).where("user.id =tasks.user_id")
includes will do eager loading check the example below or read eager loading at here
users = User.limit(10)
users.each do |user|
puts user.address.postcode
end
This will run 11 queries, it is called N+1 query problem(first you query to get all the rows then you query on each row again to do something). with includes Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Now when you do;
users = User.includes(:address).limit(10)
user.each do |user|
puts user.address.postcode
end
It will generate just 2 queries as follow
SELECT * FROM users LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10))
Plus if you don't have associations then read below;
you should be have to look at http://guides.rubyonrails.org/association_basics.html
Assuming your are trying to do inner join, by default in rails when we associate two models and then query on them then we are doing inner join on those tables.
You have to create associations between the models example is given below
class User
has_many :reservations
...# your code
end
And in reservations
class Reservations
belongs_to :user
... #your code
end
Now when you do
User.joins(:reservations)
the generated query would look like as follow
"SELECT `users`.* FROM `users` INNER JOIN `reservations` ON `reservations`.`user_id` = `users`.`id`"
you can check the query by doing User.joins(:reservations).to_sql in terminal
Hopefully it would answer your question
User.find_by_sql("YOUR SQL QUERY HERE")
You can use as follows..
User.includes(:tasks).where("user.id =tasks.user_id").order(:user.id)

How to eager load a polymorphic model?

I'm trying to optimize some of our queries. One particular challenge was trying to eager load a polymorphic model. In this case, UserView is polymorphic and has the following columns:
user_id, user_viewable_id, user_viewable_type
When I try to run this query.
#user_views = UserView.select('user_views.* AS user_views, songs.* AS songs, users.* AS users')
.joins('INNER JOIN users AS users ON user_views.user_id = users.id')
.joins('INNER JOIN songs AS songs ON user_views.user_viewable_id = songs.id')
.where(:user_viewable_type => 'Song').order('user_views.id DESC').limit(5)
It doesn't seem to eager load the query. I am using a gem called MiniProfiler, which indicates that it is actually running n+1 queries, instead of just one.
The following AR query returns this SQL:
SELECT user_views.* AS user_views, songs.* AS songs, users.* AS users FROM "user_views" INNER JOIN users AS users ON user_views.user_id = users.id INNER JOIN songs AS songs ON user_views.user_viewable_id = songs.id WHERE "user_views"."user_viewable_type" = 'Song' ORDER BY user_views.id DESC LIMIT 5
Which returns the records all in one query. Strange why this is not working as expected in AR.
Any ideas how to get this to work?
Rails documentation about eager loading says includes works with polymorphic associations. So you should just write:
#user_views = UserView.includes(:user_viewable).
where(:user_viewable_type => 'Song').order('id DESC').limit(5)

Resources