I have the following ActiveRecords
class Product < ActiveRecord::Base
has_many :reviews
end
class Review < ActiveRecord::Base
belongs_to :product
end
Each review object contains a field named 'rating'
I wish to get a list of all products whose average rating is larger than a specific bound.
I don't understand how to use the find command to do this.
Does find let us do things like that?
I'd use SQL when queries start asking for things like conditions on aggregated values as this one does. There are various ways of achieving the result you want - this feels most straightforward to me:
bound = 3
products = Product.where('id in
(
select product_id
from reviews
group by product_id
having avg(rating) > ?)', bound)
-Yes, it can do it. Somthing like this should do the trick ...
Product.find(:all, :include => 'reviews', :conditions => ['review.rating > ?', min_rating])
Edit - Just reread your question. You want to use the average rating. I'd resort to SQL to do this, or if it is a common operation, calculate the average rating and store it in the product every time a rating is saved.
Related
I have a User model which has a Scoring model which has a score value.
In my rails view I want to make an order of my users by score.
=> User.joins (: scoring) .order (: score)
So far, so good.
it gets complicated when I would dynamically change the score of some User without modifying them in the database according to certain attributes such as geolocation.
I tried the assign_attributes function but it does not change because the .order function calls the score fields in the database.
Use case: I do a user search by geolocation and the users near the geolocation appear in my search with their scores. I would like to weight the scores of users nearby since they are not on the exact geolocation
My code:
#Get scoring in other geolocation
#fiches_proxi = Fiche.joins(:user).merge(User.joins(:scoring)).near([#geo.lat_long_DMS.to_f, #geo.lat_long_grd.to_f], proxi_calcule(#geo.population_2012.to_i),units: :km, :order => 'scorings.score DESC').order('scorings.score DESC').where.not(geo: #geo.id).limit(10)
#Get scoring in real geolocation
#fiche_order_algo_all = Fiche.joins(:user).merge(User.joins(:scoring)).where(geo_id: #geo)
#Find all scores
#fiches_all = Fiche.where(id: #fiche_order_algo_all.pluck(:id) + #fiches_proxi.pluck(:id))
#pagy, #fiche_order_algo = pagy(#fiches_all.joins(:user).merge(User.joins(:scoring).order('scorings.score DESC')), items: 12)
#fiche_order_algo.each do |f|
if f.geo.id != #geo.id
f.user.scoring.assign_attributes(score: (f.user.scoring.score - 10.0))
else
f.user.scoring.score
end
end
My score is updated but my order is the same !
When you call .each on your relation, it returns an array, so you can use Array#sort_by
#fiche_order_algo.each do |f|
if f.geo.id != #geo.id
f.user.scoring.assign_attributes(score: (f.user.scoring.score - 10.0))
else
f.user.scoring.score
end
end
#fiche_order_algo.sort_by!{|f| f.scoring.score}
If you're working with large data sets, this might not be optimized, but won't be any less efficient than what you already have.
But you can also do it in one go with:
#fiche_order_algo.sort_by! do |f|
if f.geo.id != #geo.id
f.user.scoring.assign_attributes(score: (f.user.scoring.score - 10.0))
end
f.user.scoring.score
end
I am not quite sure how to title this question and I am having a hard time getting it to work so here goes.
I have a hash of users which is various sizes. This can be anywhere from 2 to 40. I also have a hash of tickets which I want to search through and find any entries that do not contain the user_id of my users hash. I am not quite sure how to accomplish this. My last attempt I used this:
#not_found = []
users.each do |u|
#not_found += #tickets.select{|t| t["user_id"] != u.user_id}
end
I know this is not the right result as it does a comparison for only the one user_id. What I need to do is run through all of the tickets and pull out any results that contain a user_id that is not in the users hash.
I hope I am explaining this properly and appreciate any help!
Try this
known_user_ids = users.map(&:user_id)
tickets_with_unknown_users = #tickets.reject{ |t| known_user_ids.include?(t["user_id"]) }
I'm working on a program that keeps a list of physical advertisement spots and their reservations (a date range). The program needs to be able to find "open spots" for ads within an ad category.
I have three tables: AdTypes, AdPlaces and Reservations. Currently, I'm implementing a query that searches for reservations where the dates don't collide with the date range the user selected, and returns the AdType items as a list. This method works if every AdType has had an reservation at some point, but it doesn't list AdTypes that are not found in the Reservations table.
The filtering is done in PreProcessQuery of an AdTypes query, as such:
query = query.Where(r => r.Reservations.Any(res => (res.Begindate > Begindate && Enddate < res.Enddate) || (res.Enddate < Begindate && Enddate > res.Begindate)));
How can I "extend" the query so that all those AdTypes that have no reservations would be listed alongside "expired" AdType reservations?
Maybe I'm mssing something, or not quite understanding what you want, but how can you expect see AdTypes that don't exist?
Is it that you want to see the AdTypes that don't have any reservations during the period you're testing for?
If so, I imagine you'd have to use the adType entity as the basis of your screen, not the Reservation entity (with a query based on AdType, not Reservation). That way you can produce a list of AdTypes that don't have any overlapping reservations.
Does that make any sense?
I'm having some difficulties creating a sum from values I have in a collection. The collection I have is from (in rails) a one to many relationship, an order has many products.
The products are hashes and have a price: value.
I've used the built in array.sum method before, so I was trying to grab all the prices from my products, and create a new array, and sum that, but have been unable to select just the price values from my products for this array.
Is there a better way I should be attempting to do this? I'm trying to build a method for my Order model that defines the order's total price from taking the sum of the product prices that belong to the order.
I would do the following:
class Order
has_many :products
def price
products.all.sum(&:price)
end
end
Now calling .price on an Order object will sum all product prices for you.
I am building a financial reporting app with Ruby on Rails. In the app I have monthly financial statement objects (with 'revenue' as an attribute). For any single financial statement, I want show (1) year-to-date revenue, and (2) last-year-to-date revenue (calendar years are fine). Each object also has a Date (not Datetime) attribute called 'month' (watch out for 'month' variable name vs. 'month' method name confusion...maybe I should change that variable name).
So...
I think I need to (1) 'find' the array of financial statements (i.e., objects) in the appropriate date range, then (2) sum the 'revenue' fields. My code so far is...
def ytd_revenue
# Get all financial_statements for year-to-date.
financial_statements_ytd = self.company.financial_statements.find(:all,
:conditions => ["month BETWEEN ? and ?", "self.month.year AND month.month = 1",
"self.month.year AND self.month.month" ])
# Sum the 'revenue' attribute
financial_statements_ytd.inject(0) {|sum, revenue| sum + revenue }
end
This does not break the app, but returns '0' which cannot be correct.
Any ideas or help would be appreciated!
This statement may do what you want:
financial_statements_ytd.inject(0) {|sum, statement| sum + statement.revenue }
You can also look into ActiveRecord's sum class method - you can pass in the field name and conditions to get the sum.
What is the name of the field in financial_statement object that holds the value you want?
Supposing that the field name is ammount then just modify the inject statement to be:
financial_statements_ytd.inject {|sum, revenue| sum + revenue.ammount }