Sort data in ruby on rails - ruby-on-rails

Association is like
class User < ActiveRecord::Base
has_many :products
has_many :ratings
I want to sort products according to user ratings.
Let suppose I want to sort all those product whose ratings is greater than 4.
I cant find any way to do that.
I do something like
User.joins(:ratings).where("ratings.rate > ?", 4).includes(:ratings)
From that I get all user whose ratings is greater than 4 but how join with product and sort them?

User.joins(:ratings).where("ratings.rate > ?", 4).order('ratings DESC')
I am not sure what includes(:ratings) doing at the last.
Should just use something like this and it should probably work:
User.includes(:ratings).where("ratings.rate > ?", 4).order('ratings DESC')
Reference: issue in order clause and limit in active record

User.joins(:ratings).where("ratings.rate > ?", 4).order('ratings.rate')
And if you want to find associated products then this should work:
Product.joins(user: :ratings).where("ratings.rate > ?", 4).order('ratings.rate')

Related

Ruby, query model with aggregate with model

In my rails application, at some point, I query my model simply. I want to query customers order information like how many orders were given by this customer within three months.
Just now, I query the model in that way:
#customer = Customer.all
customer.rb
class Customer < ApplicationRecord
audited
has_many :orders
end
And customer may have orders.
order.rb
class Order < ApplicationRecord
audited
belongs_to :customer
end
What I would like to do is to query customers model and to inject aggregate function result to every customer records.
EDİT
I tried to simulate every solution but couln't achieve.
I have the following query in mysql.
How do I need to code in ruby with activerecord to create that query ?
SELECT
(SELECT
COUNT(*)
FROM
orders o
WHERE
o.customer_id = c.id
AND startTime BETWEEN '2017.12.04' AND '2018.01.04') AS count_last_month,
(SELECT
COUNT(*)
FROM
orders o
WHERE
o.customer_id = c.id
AND startTime BETWEEN '2017.10.04' AND '2018.01.04') AS count_last_three_month,
c.*
FROM
customers c;
How can I achieve that?
Thanks.
Customer.
joins(:orders).
group('customers.id').
where('orders.created_at > DATE_SUB(NOW(), INTERVAL 3 MONTH)')
select('sum(orders.id), customers.*')
As my understanding of you question. I have this solution for you question. Please have a look and try it once. In below query, 'includes' used to solve N+1 problem.
Customer.includes(:orders).where('created_at BETWEEN ? AND ?', Time.now.beginning_of_day, Time.now.beginning_of_day-3.months).group_by{|c|c.orders.count}
If you are looking for particular customer's order count then you can try this one.
#customer.orders.where('created_at BETWEEN ? AND ?', Time.now.beginning_of_day, Time.now.beginning_of_day-3.months).group_by{|c|c.orders.count}

With Rails 5, how to left joins with selected associations and order by association count

here are my models:
class Article
has_many :votes
end
class Vote
belongs_to :article
belongs_to :user
end
Now I am trying to order the articles, by the count of votes in the past 24 hours. Any suggestions for how to do this?
I have tried this:
Article.left_joins(:votes).group("articles.id").order("count(votes.id) DESC")
However, this is ordering by all the votes, not the votes in last 24h. Any suggestions?
One more thing is, I still need to get the articles with no votes. So I am not sure how to use the where clause here...
You need to add the date when the vote was created for an article prior to the count of votes in your order.
Try this:
Article.left_joins(:votes)
.group("articles.id")
.order("DATE(votes.created_at) DESC, count(votes.id) DESC")
Then if you only want to get the articles that has been upvoted for the past 24hr, you can chain this to your query:
.where("votes.created_at >= ?", 1.day.ago)
Finally I got it work. It turns out left_joins is not necessary. My current solution is using select clause in the order() function:
Article.order("(select count(*) from votes where votes.article_id = articles.id and votes.created_at >= NOW() - '1 day'::INTERVAL ) desc")
Maybe not elegant, but works well.

Why is my controller not ordering by what Im telling to order by?

My controller action is calling all images belonging to a specific user and Im trying to order by its position (Im using the acts_as_list gem) but when I go to the page, the images are FIRST sorted by the created date, and then position (according to rails console). But because it orders by the creation date first my controller order is being ignored which is no good.
here is my action
def manage_tattoos
#tattoos = current_member.tattoos.order("position DESC")
end
and my server console shows:
Tattoo Load (0.6ms) SELECT `tattoos`.* FROM `tattoos` WHERE
(`tattoos`.member_id = 1) ORDER BY tattoos.created_at DESC, position DESC
Have you tried specifiing the order in the association?
class TodoList < ActiveRecord::Base
has_many :todo_items, :order => "position"
end
Does your association between Member and Tattoo have an order clause? E.g.
# Member class
has_many :tattoos, :order => "created_at DESC"
Is this the case? If so you might need to change your query to something like:
Tattoo.where(:member_id=>current_member.id).order("position DESC")
I'm unaware of a way to clear the order clause from an ActiveRecord association.
Or specify what to do with created_at:
current_member.tattoos.order("position DESC, created_at DESC")
Well it seems Im just very absent minded and had completely forgotten that I set a default scope on the model. Took that out and everything is fine

Rails: How do you sort a model by a column in a tabel two associations away?

The problem I'm having is like this: The model to sort is SchoolClass which has_many Students which in turn has_many Projects and each project has an end_date. I need to sort the SchoolClasses four ways: First by the earliest project end_date sort ascending and descending, and second by the latest project end_date sort ascending and descending. Does this make sense?
class SchoolClass < ActiveRecord::Base
has_many :students
end
class Student < ActiveRecord::Base
has_many :projects
belongs_to :school_class
end
class Project < ActiveRecord::Base
belongs_to :student
end
The only way I can think of doing it is very brute force and involves having a methods in the SchoolClass model that return the earliest and latest project dates for that instance like so:
students.collect(&:projects).flatten.select(&:end_date).sort.last
to find the latest project end_date for that class and then fetching out all the classes of the database and sorting them by that method. Surely this is just awful though, right? I would really like to find the rails way to get this ordering (with scopes maybe?). I thought something like SchoolClasses.joins(:students).joins(:projects).order('projects.end_date ASC') might work but that will crash rails (and looking at it now the logic is wrong anyway i think).
Any suggestions?
Try this:
scs = SchoolClass.joins({:students => :projects}).
select("school_classes.id,
MIN(projects.end_date) AS earliest_end_date,
MAX(projects.end_date) AS latest_end_date").
group("school_classes.id").
order("earliest_end_date ASC")
The objects in the scs array has following attributes:
id
earliest_end_date
latest_end_date
If you need additional attributes you can do the following
1) Add the additional attributes to the group and select methods
2) Query the full SchoolClass object using the id
3) Rewrite the query to use a nested JOIN
scs = SchoolClass.joins(
"JOIN (
SELECT a.id,
MIN(c.end_date) AS earliest_end_date,
MAX(c.end_date) AS latest_end_date
FROM school_classes a
JOIN students b ON b.class_id = a.id
JOIN projects c ON c.student_id = b.id
GROUP BY a.id
) d ON d.id = school_classes.id
").select("school_classes.*,
d.earliest_end_date AS earliest_end_date,
d.latest_end_date AS latest_end_date").
order("earliest_end_date ASC")

Rails HABTM query where condition is based on association attribute

I have User and Album models with HABTM relationship
class Album < ActiveRecord::Base
has_and_belongs_to_many :users
class User < ActiveRecord::Base
has_and_belongs_to_many(:albums)
I'd like to find all the albums that are stored in the database but not associated with a particular user.
So far my code is like this:
Album.all(:order => "albums.created_at DESC", :include => "users", :limit => limit, :conditions => ["users.id != ? AND users.id IS NOT NULL", current_user.id])
but for some reason this is not working. It's returning albums that are associated with current_user.
here take a look at this ouptput from the console.
Check the users id i first fetch.
Then i fetch albums which should not have the users id
I then find one of the listed albums and ask it to return the associated users
one of those associated users is the one from above and shouldnt be there.
Can anyone help with the above?
I usually try to stay away sub-selects but I can't seem to get it to work any other way:
class Album < ActiveRecord::Base
scope :without_user, lambda{|u| where("#{quoted_table_name}.id NOT IN (SELECT `albums_users`.album_id FROM `albums_users` WHERE `albums_users`.user_id = ?)", u.id) }
end
user = User.find(30)
Album.without_user(user)
Assuming you created table albums_users to hold relationship data:
Album.includes(:users).
where(["albums_users.user_id IS NULL OR albums_users.user_id != ?", user_id])
I think it will generate SQL along the lines of
SELECT *
FROM albums LEFT OUTER JOIN albums_users ON albums.id = albums_users.album_id
WHERE albums_users.album_id IS NULL OR albums_users.album_id != #{user_id}
Try:
:conditions => ["users.id <> ? AND users.id IS NOT NULL", current_user.id]
A non-sql solution would be:
Album.all.reject{|album| user.albums.include?(album)}
Obviously, if you have tens of thousands of rows in your database, you might not want to do this.
Might do something like this, too:
Album.where(["id NOT IN (?)", user.albums_ids])
But if your users have a lot (say hundreds) of albums, you shouldn't do this either.
Just throwing in easy solutions if you're out for one of those.

Resources