order products by association count belongs_to - ruby-on-rails

Class Sale
belongs_to :product, :accessible => true
belongs_to :brand, :accessible => true
end
Class Product
has_many :sales
belongs_to :brand
end
Class Brands
has_many :products
has_many :sales
end
How do i get the brands that have the most product sales?

If you want to stay with activerecord you can use Calculations but it will take 2 queries to accomplish it:
>> brands = Sale.count(:id, :include => :brand, :group=> 'sales.brand_id', :limit => 5)
SQL (0.7ms) SELECT count(DISTINCT `sales`.id) AS count_id, sales.brand_id AS sales_brand_id FROM `sales` LEFT OUTER JOIN `brands` ON `brands`.id = `sales`.brand_id GROUP BY sales.brand_id LIMIT 5
=> #<OrderedHash {967032312=>3, 1392881137=>1}>
>> brands_with_most_sales = Brand.find(brands.keys)
Brand Load (0.5ms) SELECT * FROM `brands` WHERE (`brands`.`id` = 967032312)
=> [#<Brand id: 967032312, title: "brand-x", created_at: "2009-11-19 02:46:48", updated_at: "2009-11-19 02:46:48">]
Otherwise you might want to write you own query using find_by_SQL

Related

How to set up this belongs_to association in Rails / ActiveRecord?

I have User and Review models. A review can have an author and a subject, both pointing to a User:
class Review < ApplicationRecord
belongs_to :subject, class_name: 'User', optional: true
belongs_to :author, class_name: 'User', optional: true
end
class CreateReviews < ActiveRecord::Migration[5.0]
def change
create_table :reviews do |t|
t.references :subject
t.references :author
end
end
end
This works fine and now I can assign two separate User objects to the Review object to represent who wrote the review against whom.
The user though, doesn't "know" how many reviews he's associated with either as a subject or the author. I added has_and_belongs_to_many :users on reviews and vice-versa, and though doable, isn't exactly what I want.
How do I set up the associations to be able to do the following:
review.author = some_other_user
review.subject = user2
another_review.author = some_other_user
another_review.subject = user2
user2.a_subject_in.count
#=> 2
user2.a_subject_in
#=> [#<Review>, #<Review>]
some_other_user.an_author_in.count
#=> 2
In other words, how do I see how many times a User has been saved as an author or subject for a model with belongs_to?
IF you want to use has_many association on users side, you need to define two separate has_many relations like
class User < ApplicationRecord
has_many :reviews, foreign_key: :author_id
has_many :subject_reviews, class_name: 'Review', foreign_key: :subject_id
end
Now with this you can simply use
irb(main):033:0> s.reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."author_id" = ? [["author_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1, comment: "random", subject_id: 2, author_id: 1, created_at: "2016-07-12 01:16:23", updated_at: "2016-07-12 01:16:23">]>
irb(main):034:0> s.subject_reviews
Review Load (0.2ms) SELECT "reviews".* FROM "reviews" WHERE "reviews"."subject_id" = ? [["subject_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
Comment: subject_reviews is not a good name :), change it to your requirements.
I think you're looking for this query:
class User
def referenced_in
# this fetches you all the reviews that a user was referenced
Review.where("reviews.author_id = :user_id OR reviews.subject_id = :user_id", user_id: id).distinct
end
end
User.first.referenced_in #should give you all records a user was referenced

Rails ActiveRecord has_many through association query

When I query directly against my database I get the expected result, but not in rails. I'm guessing it has to do with my associations and something bad I said about Ruby 3 years ago.
Postgres SQL Query:
SELECT users.email, members.software, members.files
FROM users INNER JOIN members
ON members.user_id = users.id
WHERE members.region_id=2
Result: "dan#gmail.com";t;t "dan#test.com";t;t
BUT from rails c:
> ←[1m←[36mUser Load (1.0ms)←[0m ←[1mSELECT users.email,
> members.software, members.files FROM "users" INNER JOIN "members" ON
> "members"."user_id" = "users"."id" WHERE "members"."region_id" =
> 2←[0m> => #<ActiveRecord::Relation [#<User id: nil, email: "dan#gmail.com">, #<User id: nil, email: "dan#test.com">]>
That snippet was the resulting query from pasting in what I have tried to create in my controller and hard coding the region id:
User.joins(:members).select("users.email, members.software, members.files").where(members: {region_id: params[:id]})
These are my models:
class User < ActiveRecord::Base
has_many :members
has_many :regions, :through => :members
end
class Region < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :region
end
Is it the way I have associated my models or something else that I am missing?
Thanks!
What you are getting is active_relations object.
you can access the attributes like this
users = User.joins(:members).select("users.email, members.software as software, members.files as files").where(members: {region_id: params[:id]})
users.each do |u|
p u.email
p u.software
p u.files
end

Howto split up multiple joins and conditions

I want to display a list of all projects, where a project
has one or more tasks
AND
has one or more clients OR has flag 'can_have_clients = 0'
AND
current_user has assignment on client
My current query is working, but does not look like the right way:
Project.where('id IN (SELECT DISTINCT project_id FROM tasks)')
.where('id IN (SELECT DISTINCT project_id FROM clients WHERE id IN (
SELECT DISTINCT resource_id FROM assignments WHERE resource_type="Client" AND user_id=?))
OR can_have_clients = 0', current_user)
Is it possible to split up more (specially the last where/OR) and does this look like the way to go with rails?
# model
class Project < ActiveRecord::Base
has_many :tasks
has_many :clients
...
class Task < ActiveRecord::Base
belongs_to :project
...
class Client < ActiveRecord::Base
has_many :assignments, :as => :resource
...
Try this:
Project.joins(:tasks).joins( :clients => :assignments).where(
:projects => { :can_have_clients => 0},
:assignments => { :resource_type => "Client", :user_id => current_user}
).select("DISTINCT project.*")
If you want to eager load tasks and clients and assignments:
Project.include(:tasks).include( :clients => :assignments).
where("tasks.id IS NOT NULL AND clients.id IS NOT NULL AND
assignments.id IS NOT NULL").
where(
:projects => { :can_have_clients => 0},
:assignments => { :resource_type => "Client", :user_id => current_user}
)
I think you can use named_scope here to optimize your query in rails format .
And for performance vice you can make the query like :
Project.where('exists (SELECT 1 FROM tasks where tasks.project_id=project.id)')
.where('exists (SELECT 1 FROM clients WHERE exists (
SELECT 1 FROM assignments WHERE assignments.resource_id=clients.id AND resource_type="Client" AND user_id=?))
OR can_have_clients = 0', current_user)
because in is too costly than exists , check it.

rails complex search with ancestry

i have following model setup
class Category < ActiveRecord::Base
has_ancestry :cache_depth => true, :depth_cache_column => :depth
has_many :watches, :dependent => :destroy
has_many :products, :through => :watches
end
class Watch < ActiveRecord::Base
belongs_to :category
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :watch, :counter_cache => true
belongs_to :category
end
I need to find products through categories name. Category have 2 levels deep(tree structure). 1 - level is a make, 2 - serie. For now im build this type of search query with the help of meta_search gem
#products = (Product.search :watch_category_name_contains => params[:search]).all.paginate(:page => params[:page])
This works and return all products with serie_name. But watch table always contain only category_id of 2 level category(serie), and im need to be able to search products through makes(1 level category). How can i build this type of query? Thanks!
Well, i see some upvotes coming on my old question, so i will answer. Im finish with raw sql for makes and series queries. Here it is:
def self.makes_with_products
find_by_sql "
SELECT makes.* FROM categories makes
WHERE ancestry IS NULL AND makes.id IN (
SELECT series.ancestry FROM products p
INNER JOIN watches w ON w.id = p.watch_id
INNER JOIN categories series ON series.id = w.category_id
WHERE series.ancestry = makes.id AND p.active
)
"
end
def series_with_products
find_by_sql "
SELECT series.* FROM categories series
WHERE series.ancestry = '#{id}'
AND (
SELECT COUNT(*) FROM products p
INNER JOIN watches w ON w.id = p.watch_id
WHERE w.category_id = series.id AND p.active
) > 0
"
end
Hope this help someone.

Query for results ordered by column of association table, most recent association only

given the following schema
class User
has_many :page_views
#[id]
end
class Page
has_many :page_views
#[id]
end
class PageView
belongs_to :user
belongs_to :page
#[id, created_at, updated_at]
end
How can i query for a given page, all users most recent page views in order by created_at date. One row per user, showing only their most recent view.
You want the PageViews with the maximum created_at for each user_id for a particular page_id. This is a representation of this query in SQL:
SELECT max(created_at) AS last_visit, page_views.*
FROM page_views
WHERE (page_id = 1)
GROUP BY user_id
ORDER BY last_visit DESC
In Rails 3, you can write this like so:
Page.first.page_views.group(:user_id).select('max(created_at) AS last_visit, page_views.*').order('last_visit DESC')
Try this:
class Page
has_many :page_views
has_many :recent_page_views, :class_name => "PageView",
:joins => "INNER JOIN (
SELECT a.id, COUNT(*) AS ranknum
FROM page_views AS a
INNER JOIN page_views AS b ON a.user_id = b.user_id AND
a.page_id = b.page_id AND
a.created_at <= b.created_at
GROUP BY a.id
HAVING COUNT(*) = 1
) AS d ON (page_views.id = d.id)",
:order => "page_views.created_at"
has_many :recent_viewers, :through => :recent_page_views,
:source => :user, :order => "page_views.created_at"
end
Now
page.recent_viewers

Resources