Active Record "where" query from parent resource - ruby-on-rails

I need to create a Query that evaluates if the parent object has receives at least two likes. If he has, then include it in the random query.
I tried something like this but it didn't work.
#attachments = Attachment.joins('LEFT OUTER JOIN users ON users.id = attachments.user_id').where("received_likes_count > ?",2).order("random()")

Does this work for you? I'm envisioning your User is the parent, and the attachments table has a user_id, but I'm not entirely sure based on your wording of the question.
The main trick of this is it uses a sub-select to gather ids for use in the IN clause.
#attachments = Attachment.where(
'user_id IN (
SELECT id FROM users WHERE received_likes_count > ?
)', 2
).order('random()')

MySQL?
#attachments = Attachment.joins(:users).where("received_likes_count > ?",2).order("rand()")

Related

Retrive records which are not referenced in other table, ActiveRecord query

There are 2 tables : User and Teacher. Teacher.user_id is from User. So, how do I find in a single query, all the users who are not in teachers.
I meant something along the lines :
User.not_in(Teacher.all)
You can use where.not query from ActiveRecord try something like below:
User.where.not(id: Teacher.pluck(:user_id).reject {|x| x.nil?})
Note: used reject method, in case you have nil values in some records.
The other users seem to have neglected the rails 3 tag (since removed based on the approved answer. My answer left for posterity) : Please try this
User.where("id NOT IN (?)",Teacher.pluck(:user_id).join(","))
This will become SELECT * FROM users WHERE id NOT IN (....) (two queries one to get the user_id from teachers and another to get the user(s) not in that list) and may fail based on the size of teacher table.
Other option is an arel table:
users = User.arel_table
User.where(users[:id].not_in(Teacher.select(:user_id).where("user_id IS NOT NULL")))
This should produce a single query similar to
SELECT * FROM users
WHERE id NOT IN ( SELECT user_id FROM teachers WHERE user_id IS NOT NULL)
(one query better performance) * syntax was not fully tested
Another single query option might be
User.joins("LEFT OUTER JOIN teachers ON teachers.user_id = users.id").
where("teachers.user_id IS NULL")
I think you should be able to do something like this
User.where.not(id: Teacher.ids)

rails fetch records with no has many relationship and ones with a condition

I have a HomeMaker and PerDateUnavailability models. HomeMaker has_many per_date_unavailabilities. I want all home makers who don't have a record in per_date_unavailabilities and home makers who have record but not when the per_date_unavailabilties.unavailable_date = somedate
I usually do the first part when I want HomeMakers without a PerDateUnavailability record using HomeMaker.includes(:per_date_unavailabilities).where(per_date_unavailabilities: {id: nil})
and the second part of the condition using HomeMaker.joins(:per_date_unavailabilities).where.not(per_date_unavailabilities: {unavailable_date: Date.today})
How do I mix these?
The sql you need is somewhat like this:
select a.*
from home_makers a left join per_date_unavailabilities b
on a.id = b.home_maker_id
where b.home_maker_id is NULL
or b.unavailable_date IS NOT ?;
Expressing the or clause is a little tough in ActiveRecord, and its best we don't fight it. (Edited after OP's comment)
HomeMaker.joins("LEFT JOIN per_date_unavailabilities on per_date_unavailabilities.home_maker_id = home_makers.id")
.where("per_date_unavailabilities.home_maker_id IS NULL OR per_date_unavailabilities.unavailable_date != ?", somedate)

How to find a record that is associated to only certain other record?

I am searching for users in my system who are associated to only a specific list of reports.
Each user can be associated to many different reports. I have three reports that I need to treat differently than others. Let's say they have ids of 1, 2, and 3.
I tried this:
User.joins(:user_reports).where(active: true, user_reports: {report_id: [1,2,3]})
This is close, but it finds users who are associated to the reports I am looking for, but it also finds those who are associated to those reports and other reports as well.
How do I find those users who are associated to only those specific reports, or any combination thereof?
The following will help:
disallowed_user_ids = UserReport.
where("report_id NOT IN (?)", report_ids).
pluck("DISTINCT user_id")
User.
joins(:reports).
where("users.id NOT IN (?)", disallowed_user_ids).
group("users.id")
Might also be possible with an ugly subquery:
User.
joins(:reports).
where("users.id NOT IN (
SELECT user_id FROM user_reports WHERE report_id IN (?)
)", report_ids).
group("users.id")
If each user in your DB has at least one report, then you can drop the joins and group.
Update:
The following is also an option:
report_ids_str = report_ids.join(',')
User.
select("COUNT(r2.id) AS r2c, #{User.column_names.join(',')}").
joins("INNER JOIN reports AS r1 ON (
r1.user_id = users.id AND r1.id IN (#{report_ids_str}))").
joins("LEFT OUTER JOIN reports AS r2 ON (
r2.user_id = users.id AND r2.id NOT IN (#{report_ids_str}))").
group("users.id").
where("r2c = 0")
You might be able to replace the select and where with a having("COUNT(r2.id) = 0").

How to build inner join in Rails with conditions?

I've a model StockUpdate which keeps track of stocks for every product for a store. Table attributes are: :product_id, :stock, :store_id. I was trying to find out last entry for every product for a given store. According to that I build my query in PGAdmin which is given below and it's working fine. I'm new in Rails and I don't know how to represent it in Model. Please help.
SELECT a.*
FROM stock_updates a
INNER JOIN
(
SELECT product_id, MAX(id) max_id
FROM stock_updates where store_id = 9 and stock > 0
GROUP BY product_id
) b ON a.product_id = b.product_id AND
a.id = b.max_id
I does not clearly understand what you want to do, but I think you can do something like this:
class StockUpdate < ActiveRecord::Base
scope :a_good_name, -> { joins(:product).where('store_id = ? and stock > ?', 9, 0) }
end
You can all call StoclUpdate.a_good_name.explain to check the generated sql
What you need is really simple and can be easily accomplished with 2 queries. Otherwise it becomes very complicated in a single query (it's still doable though):
store_ids = [0, 9]
latest_stock_update_ids = StockUpdate.
where(store_id: store_ids).
group(:product_id).
maximum(:id).
values
StockUpdate.where(id: latest_stock_update_ids)
Two queries, without any joins necessary. The same could be possible with a single query too. But like your original code, it would include subqueries.
Something like this should work:
StockUpdate.
where(store_id: store_ids).
where("stock_updates.id = (
SELECT MAX(su.id) FROM stock_updates AS su WHERE (
su.product_id = stock_updates.product_id
)
)
")
Or perhaps:
StockUpdate.where("id IN (
SELECT MAX(su.id) FROM stock_updates AS su GROUP BY su.product_id
)")
And to answer your original question, you can manually specify a joins like so:
Model1.joins("INNER JOINS #{Model2.table_name} ON #{conditions}")
# That INNER JOINS can also be LEFT OUTER JOIN, etc.

Rails 3 Comparing foreign key to list of ids using activerecord

I have a relationship between two models, Registers and Competitions. I have a very complicated dynamic query that is being built and if the conditions are right I need to limit Registration records to only those where it's Competition parent meets a certain criteria. In order to do this without select from the Competition table I was thinking of something along the lines of...
Register.where("competition_id in ?", Competition.where("...").collect {|i| i.id})
Which produces this SQL:
SELECT "registers".* FROM "registers" WHERE (competition_id in 1,2,3,4...)
I don't think PostgreSQL liked the fact that the in parameters aren't surrounded by parenthesis. How can I compare the Register foreign key to a list of competition ids?
you can make it a bit shorter and skip the collect (this worked for me in 3.2.3).
Register.where(competition_id: Competition.where("..."))
this will result in the following sql:
SELECT "registers".* FROM "registers" WHERE "registers"."competition_id" IN (SELECT "competitions"."id" FROM "competitions" WHERE "...")
Try this instead:
competitions = Competition.where("...").collect {|i| i.id}
Register.where(:competition_id => competitions)

Resources