Rails Active Record Query Results - ruby-on-rails-3

following a rails 3 tutorial, I have a scope defined to limit the orders column and a helper method to sum the results
Scope
class LineItem < ActiveRecord::Base
attr_accessible :cart_id, :product_id, :cart, :product, :quantity
belongs_to :order
belongs_to :product
belongs_to :cart
scope :order, -> {where("order != nil")}
end
Helper
module StoreHelper
def total_product_sold (product)
product.line_items.total_product.sum("quantity")
end
problem is when i call total_product_sold from my view it sums all data in orders column as opposed to only the ones where the order number is !=nil.
I also tried defining a class method as opposed to scope
def self.total_product
where(order !=nil)
end
but this gives me the same exact result. what am i doing wrong? how do i get it to add only the items whose order column are not nil?

You can't find records that are not NULL using != nil. You need to use the SQL syntax IS NOT NULL:
scope :order, -> { where("line_items.order_id IS NOT NULL") }
or:
def self.total_product
where("line_items.order_id IS NOT NULL")
end
Alternatively, you can join the :order association which effectively does the filtering for you. However, there is a performance penalty for this:
def total_product_sold (product)
product.line_items.joins(:order).sum("quantity")
end

Related

Rails active record order by sum of a column

So I have 2 Models Posts and Topics
Posts has number of "viewed".
Posts has a topic.
Here I want to get the most view topics and order it DESC (the highest total views of
all posts tagged with that topic) using rails active record. Here is my current code of what I am trying to do but it is not correct :-
class Topic < ApplicationRecord
has_many :posts
scope :ordered, -> {
joins(:posts).order("sum(posts.viewed) DESC").limit(2).uniq
}
end
You need to group your topics_id
class Topic < ApplicationRecord
has_many :posts
scope :ordered, -> {
joins(:posts).group("topics.id").order('SUM(posts.viewed) desc').limit(2).uniq
}
end
This would work
It is a bad pattern to sum childer and than order by the sum.
I would advise you to add a total_views:integer column to your Topic.rb and update it whenever the sum of post.views changes.
When a child post's viewed value increases, you can call a callback to automatically update the total_views column.
Post.rb can have something like:
after_create do
topic.update_total_views
end
after_update do
topic.update_total_views
end
after_destroy do
topic.update_total_views
end
Topic.rb:
def update_total_views
update_column :total_views, (posts.map(&:viewed).sum)
end
Than in your controller you can call Topic.all.order(total_views: :desc)

Find all records which have a count of an association of zero and none-zero

class Gallery < ApplicationRecord
has_many :associated_images, as: :imageable
end
class Event < ApplicationRecord
has_many :associated_images, as: :imageable
end
class Image < ApplicationRecord
has_many :associated_images
end
class AssociatedImage < ApplicationRecord
belongs_to :imageable, polymorphic: true
belongs_to :image
end
I'd like to get all the images which are being used, and a different query to get all the images that are not being used (based on AssociatedImage).
I tried Image.joins(:associated_images).group('images.id').having('count(image_id) > 0') and it returns the correct result. But when I run Image.joins(:associated_images).group('images.id').having('count(image_id) = 0'), it returns an empty #<ActiveRecord::Relation []> and I'm not sure why is that.
My query is based off Find all records which have a count of an association greater than zero's discussion
The reason is that in SQL a count of zero happens when there are no rows. So if there are no rows, even group, there is no result.
What you want is
Image.left_joins(:associated_images).where(associated_images: {id: nil}).group('images.id')
When SQL does a left join, for an image which does not have an associated image, it fills in NULL for all the columns in the associated_images table. So the ones where the associated_images.id is nil, are the ones we want.

How to query an STI-driven model using nested associations?

My requirement is to write a query to retrieve records based of a model on STI based on the nested associations of the model.
Here’s what my modeling looks like:
class Loan < ApplicationRecord
belongs_to :borrower
end
class BusinessLoan < Loan
belongs_to :business, inverse_of: :business_loans
end
class HousingLoan < Loan
end
class Borrower < ApplicationRecord
has_many :loans
has_one :address, as: :addressable
end
class Business < ApplicationRecord
has_many :business_loans, inverse_of: :business
has_one :address, as: :addressable
end
class Address < ApplicationRecord
# COLUMN city, :string
belongs_to :addressable, polymorphic: true
end
I would like to write to retrieve the list of all loans whose business or borrower is in a particular city.
Here’s what I have at the moment:
cities = ["New York", "Washington"]
query_string = [
Loan.select(:id).joins(borrower: :address).where(city: cities).to_sql,
BusinessLoan.select(:id).joins(business: :address).where(city: cities).to_sql
].join(" UNION ")
Loan.where(id: Loan.find_by_sql(query_string))
I require the result as an ActiveRecord relation hence the last query
Is there a better way to write this query?
It doesn't look like there is a way to directly achieve this without writing a few new scopes in the associated models. Rails does not permit any operations in scopes that are not possible on the model on which the scopes are initiated.
You can however still solve the problem to yield a single query as follows:
Define from_cities scopes in Borrower and Business as shown below:
scope :from_cities, ->(cities) { joins(:address).where("addresses.city IN (?)", cities) }
Define from_cities in Loan to use these new scopes:
scope :from_cities, ->(cities) do
where(borrower_id: Borrower.from_cities(cities)).
or(where(business_id: Business.from_cities(cities)))
end

Rails query through multiple models

It's been 2 days by now that I'm struggling to write query. So, I'm trying to query within these 3 related models:
class Company...
has_many :programs
end
class Program...
belongs_to :company
has_many :transactions
end
class Transaction...
belongs_to :program
end
As output, I need a list of the amount of all Transactions each Company made and on what date.
Your query should be something like this.
Company.includes(:programs => :transactions).map do |company|
{:company_name => company.name,
:transactions => company.programs.map{|program| program.transactions.map{|t| {:amount => t.amount, :date => t.created_at.strftime("%d-%m-%Y")}}.flatten
}
end
You could change the format of the date by referring to strftime method of Time class from here - http://ruby-doc.org/core-2.2.0/Time.html#method-i-strftime.

Order by polymorphic belongs_to attribute

How do I make an ActiveRecord query that orders by an attribute of a polymorphic belongs_to association?
For example I have model called Tagging that has a polymorphic belongs_to association named tagged_item.
Unfortunately Tagging.joins(:tagged_item) throws a ActiveRecord::EagerLoadPolymorphicError. So I can't do something like Tagging.joins(:tagged_item).order("tagged_item.created_at DESC").
Any suggestions?
You can't make a join directly with a polymorphic relationship because the polymorphic objects' data are in different tables. Still you could try to do it manually as in the following example.
class Tagging < ActiveRecord::Base
belongs_to :tagged_item, :polymorphic => true
end
class Post
has_many :image_tagging, :as => :tagged_item
end
class Comment
has_Many :image_tagging, :as => :tagged_item
Tagging.select("taggins.*, COALESCE(posts.created_at, comments.created_at) AS tagged_item_created_at").
joins("LEFT OUTER JOIN posts ON posts.id = tagging.tagged_item_id AND tagging.tagged_item_type = 'Post'").
joins("LEFT OUTER JOIN comments ON comments.id = tagging.tagged_item_id AND tagging.tagged_item_type = 'Comment'").
order("tagged_item_created_at DESC")
COALESCE chooses the first column provided if exists otherwise the other one. It's the same as IFNULL in mysql or you could even use CASE WHEN ... IS NULL THEN ... ELSE ... END