Rails retuning an array of ids with multiple conditions - ruby-on-rails

In my controller i'm looking to return an array with 2 conditions attached.
parent_ids = StudentGuardian.where(:guardian_id => current_user.school_user.id).pluck(:student_id)
classmodule_ids = SubjectStudent.pluck (:class_id)
#homework = Homework.where("subject in ?", classmodule_ids)
So in this case I need to find a student id from a table and then I need to get a class_id from another table.
Then I am trying to display results.
Can I get both in to the one query?
I also tried #homework = Homework.where("subject in ?", parent_ids, classmodule_ids)Of course this does not work!

You just want to know if subject is in parent_ids or classmodule_ids?
So can you just merge parent_ids and classmodule_ids into one array:
# you could merge these a bunch of different ways, here's one:
search_ids = (parent_ids + classmodule_ids).uniq
# don't forget the () around ? below
#homework = Homework.where("subject in (?)", search_ids)
Or if for some reason you didn't want to combine parent_ids and classmodule_ids:
#homework = Homework.where("subject in (?) OR subject in (?)", parent_ids, classmodule_ids)
But all of that would mean subject is an id also... Is that the case?

Related

How to get a unique set of parent models after querying on child

Order has_many Items is the relationship.
So let's say I have something like the following 2 orders with items in the database:
Order1 {email: alpha#example.com, items_attributes:
[{name: "apple"},
{name: "peach"}]
}
Order2 {email: beta#example.com, items_attributes:
[{name: "apple"},
{name: "apple"}]
}
I'm running queries for Order based on child attributes. So let's say I want the emails of all the orders where they have an Item that's an apple. If I set up the query as so:
orders = Order.joins(:items).where(items: {name:"apple"})
Then the result, because it's pulling at the Item level, will be such that:
orders.count = 3
orders.pluck(:email) = ["alpha#exmaple.com", "beta#example.com", "beta#example.com"]
But my desired outcome is actually to know what unique orders there are (I don't care that beta#example.com has 2 apples, only that they have at least 1), so something like:
orders.count = 2
orders.pluck(:email) = ["alpha#exmaple.com", "beta#example.com"]
How do I do this?
If I do orders.select(:id).distinct, this will fix the problem such that orders.count == 2, BUT this distorts the result (no longer creates AR objects), so that I can't iterate over it. So the below is fine
deduped_orders = orders.select(:id).distinct
deduped_orders.count = 2
deduped_orders.pluck(:email) = ["alpha#exmaple.com", "beta#example.com"]
But then the below does NOT work:
deduped_orders.each do |o|
puts o.email # ActiveModel::MissingAttributeError: missing attribute: email
end
Like I basically want the output of orders, but in a unique way.
I find using subqueries instead of joins a bit cleaner for this sort of thing:
Order.where(id: Item.select(:order_id).where(name: 'apple'))
that ends up with this (more or less) SQL:
select *
from orders
where id in (
select order_id
from items
where name = 'apple'
)
and the in (...) will clear up duplicates for you. Using a subquery also clearly expresses what you want to do–you want the orders that have an item named 'apple'–and the query says exactly that.
use .uniq instead of .distinct
deduped_orders = orders.select(:id).uniq
deduped_orders.count = 2
deduped_orders.pluck(:email) = ["alpha#exmaple.com", "beta#example.com"]
If you want to keep all the attributes of orders use group
deduped_orders = orders.group(:id).distinct
deduped_orders.each do |o|
puts o.email
end
#=> output: "alpha#exmaple.com", "beta#example.com"
I think you just need to remove select(:id)
orders = Order.joins(:items).where(items: {name:"apple"}).distinct
orders.pluck(:email)
# => ["alpha#exmaple.com", "beta#example.com"]
orders = deduped_orders
deduped_orders.each do |o|
puts o.email # loop twice
end

How do I get the first row in a record after ordering it? Ruby On Rails

I am working with Ruby on Rails.
I make a query to the model, but I want to get the one register that has the highest value for the average attribute. This is my code:
#dish = Dish.where("day = ? and week = ?", params[:day], params[:week])
#dish.order(:average)
#sug = #dish.first
#sug gets the record with the lowest id, no the one with the highest average.
I have also tried it this way:
#sug = #dish.order(:average).limit(1)
but it's not working either. How can I get that one register?
You need to chain the calls like follows.
#dish.order(:average).first
The call to .order does not change the #dish instance, so when you call #dish.first the ordering no longer is there.
#dish.order does not order already found dishes, it changes the query to include an order clause.
#dish = Dish.where("day = ? and week = ?", params[:day], params[:week]).order(:average) would do what you wanted, and so would
#dish = Dish.where("day = ? and week = ?", params[:day], params[:week])
#dish = #dish.order(:average)
#sug = #dish.first
The ActiveRecord::QueryMethods#order method sorts in ascending order by default, so try explicitly asking for a descending order, then taking the first:
Dish.order(average: :desc)
.where("day = ? and week = ?", params[:day], params[:week])
.first
This assumes your average attribute is stored in the database, and not computed.

rails getting attributes of associated model for many objects in one query

My title might be confusing, I wasn't sure what to write.
In rails I understand how to fetch Many Objects for One parent object
#first_user = User.first
#first_user_posts = #first_user.posts
But how can I fetch Many Objects for Many parent objects and select its attributes in one query?. I am trying to do something like that:
#many_posts = Post.all
#posts_by_user_gender = #many_posts.joins(:user).map(&:gender)
hoping it would give me an array that could look something like this:
#posts_by_user_gender => ["male", nil, "female", nil]
#I know I can do this map technique if I fetch it directly from the User model
# User.all.map(&:gender),
# but I want to start with those that posted in a specific category
# Post.where(:category_id => 1)
and then to count the males I could use the Ruby Array method .count
#males_count_who_posted = #posts_by_user_gender.count("male")
=> 1
I could always do 3 separate queries
#males_count_who_posted = #many_posts.select(:user_id).joins(:user)
.where("gender = ?", "male").count
#females_count_who_posted = ...
but I find that extremely inefficient, especially if I do the same for something like "industry" where you could have more than 3 options.
you can join model via SQL syntax
#posts_by_user_gender = #many_posts.joins("LEFT JOIN Users where users.id=posts.user_id").joins("LEFT JOIN Genders where genders.id=user.gender_id")

How do I sort in ruby/rails on two fields?

For example, I would like to sort by game_date, and then if the date is the same, sort it by team? What would be the best way to do this?
#teams = #user.teams
#games = #teams.reduce([]) { |aggregate, team| aggregate + team.games}.sort_by(&:game_date)
The best way would be to have your database do it, but if you want to use Ruby:
#games = #data.sort_by {|x| [x.game_date, x.team] }
The sorting behaviour of Array is to sort by the first member, then the second, then the third, and so on. Since you want the date as your primary key and the team as the second, an array of those two will sort the way you want.
#teams = #user.teams
#games = #teams.games.order("game_date ASC, team ASC")
#teams = #user.teams
#games = #teams.games.order(game_date: :asc, team: :asc)
Assuming that you have a model which have these two fields
Model.all(:order => 'attribute1, attribute2')
Incase the fields are in multiple tables, you can use joins.
For those looking to sort an array containing two different objects w/ a differently named date field, you can do this with an attribute alias method in the model.
note: using .sort_by! destructively doesn't work.
News.rb
def release_date
self.publish_date
end
Controller
#grid_elements = #news + #albums
#grid_elements = #grid_elements.sort_by(&:release_date)

rails where() sql query on array

I'll explain this as best as possible. I have a query on user posts:
#selected_posts = Posts.where(:category => "Baseball")
I would like to write the following statement. Here it is in pseudo terms:
User.where(user has a post in #selected_posts)
Keep in mind that I have a many to many relationship setup so post.user is usable.
Any ideas?
/EDIT
#posts_matches = User.includes(#selected_posts).map{ |user|
[user.company_name, user.posts.count, user.username]
}.sort
Basically, I need the above to work so that it uses the users that HAVE posts in selected_posts and not EVERY user we have in our database.
Try this:
user.posts.where("posts.category = ?", "Baseball")
Edit 1:
user.posts.where("posts.id IN (?)", #selected_posts)
Edit 2:
User.select("users.company_name, count(posts.id) userpost_count, user.username").
joins(:posts).
where("posts.id IN (?)", #selected_posts).
order("users.company_name, userpost_count, user.username")
Just use the following:
User.find(#selected_posts.map(&:user_id).uniq)
This takes the user ids from all the selected posts, turns them into an array, and removes any duplicates. Passing an array to user will just find all the users with matching ids. Problem solved.
To combine this with what you showed in your question, you could write:
#posts_matches = User.find(#selected_posts.map(&:user_id).uniq).map{ |user|
[user.company_name, user.posts.size, user.username]
}
Use size to count a relation instead of count because Rails caches the size method and automatically won't look it up more than once. This is better for performance.
Not sure what you were trying to accomplish with Array#sort at the end of your query, but you could always do something like:
#users_with_posts_in_selected = User.find(#selected_posts.map(&:user_id).uniq).order('username DESC')
I don't understand your question but you can pass an array to the where method like this:
where(:id => #selected_posts.map(&:id))
and it will create a SQL query like WHERE id IN (1,2,3,4)
By virtue of your associations your selected posts already have the users:
#selected_posts = Posts.where("posts.category =?", "Baseball")
#users = #selected_posts.collect(&:user);
You'll probably want to remove duplicate users from #users.

Resources