Pass conditions to subqueries - rails activerecords - sql

I have a QuestionCategory model who hasMany Question.
When I do this query #quizzs = QuestionsCategory.find(:all) everything is ok. It's querying all the catégories and associated questions:
QuestionsCategory Load (0.2ms) SELECT `questions_categories`.* FROM `questions_categories`
Question Load (0.3ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 1
Question Load (0.4ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 2
Question Load (0.3ms) SELECT `questions`.* FROM `questions` WHERE `questions`.`questions_category_id` = 3
But, I'd like to pass a condition to the questions subqueries. Something like
#quizzs = QuestionsCategory.find(:all, :questions => [ :conditions => {MY CONDITIONS} ])
And then get subqueries looking like this:
SELECT questions.* FROM questions WHERE questions.questions_category_id = 2 AND MY CONDITIONS
Possible?
Cheers mates

To get result for your SQL ( SELECT questions.* FROM questions WHERE questions.questions_category_id = 2 AND MY CONDITIONS) you can do as follows:
QuetionsCategory.find(2).questions.all(:conditions => [Your conditions])

How about a scope?
Question category controller:
def your_action_name
#quizzs = QuestionsCategory.all.collect{|qc| qc.questions.published == true}
Question model:
class Question < ActiveRecord::Base
scope :published, where(:published => true)
end
This way you can chain all the scopes (conditions) you'd like to apply to a Question

You Need Default scope In your model.
puts this In you QuestionCategory model.
default_scope joins(:questions).where(conditions)
Then
QuestionCategory.all Should give you expected result.

Related

Select model based on amount of related models with certain conditions

I have a Post that has_many :comments. Let's say Comment has the following field: another_model_id. I would like to select Posts, that have from 2 to 5 comments with another_model_id = 10 (for example). I tried several constructions but with no success :(
Example of what I tried:
# Invalid SQL syntax error
Post
.joins(:comments)
.select("count(comment.another_model_id = 10) as comments_count)
.where("comments_count BETWEEN 2 AND 5")
I have literally no idea where to dig. Is it possible to achieve that in a single query? Thanks!
Post
.joins(:comments)
.where(comments: { another_model_id: 10 })
.group('posts.id')
.having('count(comments.id) > 2 AND count(comments.id) < 5')
Using counter_cache is the best practice for your scenario:
In your Comment model, you need to set the counter_cache:
belongs_to :post, counter_cache: true
Then simply you can use this query:
Post.joins(:comments)
.where(comments: { another_model_id: 10 })
.where('comments_count > ? AND comments_count < ?', 2, 5)

Ransack no implicit conversion of Ransack::Search into Array

I am currently implementing Ransack for searching functionality.
I have a model Campaigns which collaborates campaigns that the user directly created as well as others so long as the user belongs to the same vendor.
I can combine the results as such:
#search = current_user.campaigns + current_user.vendor.campaigns.where.not(:user_id => current_user.id)
Problem with this is that Ransack will not accept this combination and spits out
no implicit conversion of Ransack::Search into Array
Can someone point me in the direction on how to refactor this code?
TIA
Adding Addition Data
When looking at my console I can see *current_user.campaigns*:
Campaign Load (0.3ms)
SELECT DISTINCT "campaigns".* FROM "campaigns"
WHERE "campaigns"."user_id" = ? [["user_id", 2]]
Running *current_user.vendor.campaigns* give me:
Campaign Load (0.4ms)
SELECT DISTINCT "campaigns".* FROM "campaigns"
INNER JOIN "weeks" ON "campaigns"."id" = "weeks"."campaign_id"
INNER JOIN "products" ON "weeks"."product_id" = "products"."id"
INNER JOIN "locations" ON "products"."location_id" = "locations"."id"
WHERE "locations"."vendor_id" = ? [["vendor_id", 2]]
I can get the first filter of current_user achieved with:
#search = Campaign.where("campaigns.user_id" => current_user.id).search(params[:q])
But I am lost of how I go about building the rest of the join tables to include both elements of data
Solved
#search = Campaign.includes(:weeks).where('(campaigns.user_id LIKE ?) OR (weeks.vendor_id LIKE ?)', current_user.id, current_user.vendor.id).search(params[:q])

Which of these two AR queries is more efficient? (ie. is it better to outsource some query work to Ruby )

#users = Hash.new
#users[:count] = User.count(:all, :joins => my_join, :conditions => my_conditions)
#users[:data] = User.find(:all, :joins => my_join, :conditions => my_conditions)
or
#users = Hash.new
#users[:data] = User.find(:all, :joins => my_join, :conditions => my_conditions)
#users[:count] = #users[:data].count
It seems like the first option consists of two database queries (which from what I read is expensive) while in the second one, we only make one database query and do the counting work at the Ruby level.
Which one is more efficient?
The second one is better, since, just like you said, it saves a database query.
p.s.
Please be careful if you use some new finder methods introduced in Rails 3, then calling count after would fire a COUNT(*) query:
users = User.where(...) # SELECT "users".* FROM "users" WHERE ...
users_count = users.count # SELECT COUNT(*) FROM "users" WHERE ...
To prevent that, you might want to call size:
users = User.where(...) # SELECT "users".* FROM "users" WHERE ...
users_count = users.size # No database query

Rails 3.0 One-One Association Using associated model in WHERE clause

When I do:
conditions = {:first_name => 'Chris'}
Patient.joins(:user).find(:all, :conditions => conditions)
It Produces (and fails because the first_name is not in the patients table)
SELECT "patients".* FROM "patients" INNER JOIN "users" ON "users"."id" = "patients"."user_id" WHERE "patients"."first_name" = 'Chris'
I need to be able to query the User model's fields also and get back Patient objects. Is this possible?
Try this:
conditions = ['users.first_name = ?', 'Chris']
Patient.joins(:user).find(:all, :conditions => conditions)
Try changing you conditions hash to:
conditions = {'users.first_name' => 'Chris'}
I've used this style in Rails 2.3, and it worked great for me. Cheers!

Rails SQL Query with find

I want this SQL query to be written in rails controller using find:
select id,name from questions
where id not in (select question_id from levels_questions where level_id=15)
How will I do this? I am using Rails framework and MySQL.
Thanks in advance.
Question.find_all_by_sql('select id,name from questions where id not in (select question_id from levels_questions where level_id=15)')
This is admittedly non-ActiveRecord-ish, but I find that complicated queries such as this tend to be LESS clear/efficient when using the AR macros. If you already have the SQL constructed, you might as well use it.
Some suggestions: encapsulate this find call in a method INSIDE the Question class to hide the details from the controller/view, and consider other SQL constructions that may be more efficient (eg, an OUTER JOIN where levels_questions.question_id is null)
Simple way:
ids = LevelsQuestion.all(:select => "question_id",
:conditions => "level_id = 15").collect(&:question_id)
Question.all(:select => "id, name", :conditions => ["id not in (?)", ids])
One shot:
Question.all(:select => "id, name",
:conditions => ["id not in (select question_id from levels_questions where level_id=15)"])
And the rails 3 way:
ids = LevelsQuestion.select(:question_id).where(:level_id => 15).collect(&:question_id)
Question.select(:id, :name).where("id not in (?)", ids)