Rails - How to group_by with many models? - ruby-on-rails

I have this chain of models:
And I have a list of suboptions. I would like to group_by the suboptions by each of the above fathers.
So, it would be divided into Categories, which would be divided into activities, which would be divided into options. Do you see?
How can I do this??
Suboptions.all.group_by(???)
# or
Suboptions.all.order_by(????)
# or ????
Example:
Suboptions
+----+-----------+
| id | option_id |
+----+-----------+
| 1 | 1 |
| 2 | 2 |
| 3 | 2 |
| 4 | 4 |
| 5 | 3 |
+----+-----------+
Options
+----+-------------+
| id | activity_id |
+----+-------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 4 | 3 |
+----+-------------+
Activities
+----+-------------+
| id | category_id |
+----+-------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
+----+-------------+
Categories
+----+------+
| id | name |
+----+------+
| 1 | cat1 |
| 2 | cat2 |
+----+------+
then the search should return the suboptions with ids in the following order: [1,5,4,2,3]
( try to visualize it from the category to the suboption )

Depending on the size of the data set, sounds like you want to do a join, followed by an order
Suboption.joins(option: {activity: :category}).order('categories.name')
Will that work for you?

Related

How to correct query object to shows data properly in Rails 5

I need some fresh eye to my object query. The idea is to show from db all users which have only one client (belongs_to :user), which are not subscribed.
models
user
has_many :customers
has_many :customers_users
customer
has_many :customers_users
belongs_to :user
customers_user
belongs_to :user
belongs_to :customer
customer_subscription
belongs_to :customer
query
Customer.
joins("LEFT JOIN customer_subscriptions ON customer_subscriptions.customer_id = customers.id").
where(customer_subscriptions: { id: nil }).
joins("RIGHT JOIN customers_users ON customers_users.customer_id = customers.id").
group(:user_id).
having("count(user_id) = ?", 1).
count
But after all I've got only user_id without data such as name, email, nickname etc. I thought the problem is with count at the end but without count I received an error NoMethodError: undefined method 'having' for main:Object
Is it possible to reach these data in one query?
In your case I guess a negative condition in the join would work. So the query would be like:
SELECT * FROM users
INNER JOIN customers
ON customers.user_id = users.id
INNER JOIN customers_subscriptions
ON customers_subscriptions.customer_id != customers.user_id
GROUP BY users.id
HAVING COUNT(users.id) = 1
So having:
Users:
+----+-------+------------+----------------------------+----------------------------+
| id | email | name | created_at | updated_at |
+----+-------+------------+----------------------------+----------------------------+
| 1 | NULL | seb | 2019-04-27 19:16:09.043251 | 2019-04-27 19:16:09.043251 |
| 2 | NULL | sab | 2019-04-27 19:16:09.150315 | 2019-04-27 19:16:09.150315 |
| 3 | NULL | washington | 2019-04-27 19:58:01.737446 | 2019-04-27 19:58:01.737446 |
+----+-------+------------+----------------------------+----------------------------+
Customers:
+----+------------+-------+---------+----------------------------+----------------------------+
| id | name | email | user_id | created_at | updated_at |
+----+------------+-------+---------+----------------------------+----------------------------+
| 1 | Sab | NULL | 1 | 2019-04-27 19:16:09.254955 | 2019-04-27 20:02:47.636143 |
| 2 | Seb | NULL | 2 | 2019-04-27 19:16:09.313268 | 2019-04-27 20:02:55.741603 |
| 3 | Washington | NULL | 3 | 2019-04-27 19:58:22.711897 | 2019-04-27 19:58:45.213720 |
| 4 | Eminem | NULL | 3 | 2019-04-27 19:58:52.820731 | 2019-04-27 20:03:02.465681 |
+----+------------+-------+---------+----------------------------+----------------------------+
Customers Subscriptions:
+----+-------------+----------------------------+----------------------------+
| id | customer_id | created_at | updated_at |
+----+-------------+----------------------------+----------------------------+
| 1 | 1 | 2019-04-27 19:16:10.041788 | 2019-04-27 19:16:10.041788 |
| 2 | 3 | 2019-04-27 20:04:16.464446 | 2019-04-27 20:04:16.464446 |
+----+-------------+----------------------------+----------------------------+
The result should be:
+----+-------+------+----------------------------+----------------------------+----+------+-------+---------+----------------------------+----------------------------+----+-------------+----------------------------+----------------------------+
| id | email | name | created_at | updated_at | id | name | email | user_id | created_at | updated_at | id | customer_id | created_at | updated_at |
+----+-------+------+----------------------------+----------------------------+----+------+-------+---------+----------------------------+----------------------------+----+-------------+----------------------------+----------------------------+
| 1 | NULL | seb | 2019-04-27 19:16:09.043251 | 2019-04-27 19:16:09.043251 | 1 | Sab | NULL | 1 | 2019-04-27 19:16:09.254955 | 2019-04-27 20:02:47.636143 | 2 | 3 | 2019-04-27 20:04:16.464446 | 2019-04-27 20:04:16.464446 |
+----+-------+------+----------------------------+----------------------------+----+------+-------+---------+----------------------------+----------------------------+----+-------------+----------------------------+----------------------------+
So an AR code for that could be:
User.
joins(:customers).
joins('INNER JOIN customers_subscriptions ON customers_subscriptions.customer_id != customers.user_id').
group(:id).
having('COUNT(users.id) = 1')

How to join with a polymorphic model using the PublicActivity gem

I'm using the PublicActivity gem: https://github.com/pokonski/public_activity
All of the models I use PublicActivity to track use the column edition_id. And I'm wondering how I can scope by that column since it's polymorphic relation.
Eg PublicActivity looks like so:
PublicActivity::Activity.limit(50)
+----+--------------+----------------+----------+------------+--------------------+------------+--------------+----------------+---------------------------+---------------------------+
| id | trackable_id | trackable_type | owner_id | owner_type | key | parameters | recipient_id | recipient_type | created_at | updated_at |
+----+--------------+----------------+----------+------------+--------------------+------------+--------------+----------------+---------------------------+---------------------------+
| 1 | 42 | QuizMaster | 6 | User | quiz_master.update | {} | | | 2013-04-12 17:33:14 +0100 | 2013-04-12 17:33:14 +0100 |
| 2 | 25 | Place | 6 | User | place.update | {} | | | 2013-04-12 17:42:42 +0100 | 2013-04-12 17:42:42 +0100 |
| 3 | 25 | Event | 6 | User | event.update | {} | | | 2013-04-12 17:45:08 +0100 | 2013-04-12 17:45:08 +0100 |
| 4 | 20 | QuizMaster | 6 | User | quiz_master.update | {} | | | 2013-04-12 17:49:09 +0100 | 2013-04-12 17:49:09 +0100 |
| 5 | 20 | QuizMaster | 6 | User | quiz_master.update | {} | | | 2013-04-12 17:50:51 +0100 | 2013-04-12 17:50:51 +0100 |
+----+--------------+----------------+----------+------------+--------------------+------------+--------------+----------------+---------------------------+---------------------------+
Where edition_id is on the polymorphic trackable relation.
What I would like to do it something like:
PublicActivity::Activity.limit(50).includes(:trackable)# where trackable edition = 1
I'm not sure how or even if it's possible to join or include or preload a polymorphic model across multiple "trackable" types.
I don't think this is possible directly with SQL because of the polymorphism, but you could do something with ruby pretty easily.
PublicActivity::Activity.limit(50).select {|c| c.trackable.edition_id = 1 }
It won't be as efficient as a SQL query but using Rails preload it'll probably be good enough depending on the number of records you're fetching.
PublicActivity::Activity.limit(50).preload(:trackable).select {|c| c.trackable.edition_id = 1 }

Generate unique id for a new item in models on Rails 3

I populate book items into my Book model,
But I found there are many items have the same id.
So, how to create unique id for items. To prevent many items from having the same id ?
Here is the book model code
# encoding: utf-8
class Book < ActiveRecord::Base
attr_accessible :name, :isbn ,:price ,:comment ,:author ,:sale_type ,:publisher ,:sn ,:category
attr_accessible :location, :category, :release_date
validates_uniqueness_of :sn
Here are the part of my items
irb(main):058:0> Book.all[1..10]
+-----+------+-----+-----+------+------+-----+------+-----+-----+------+------+-----+------+
| id | pric | com | cre | upda | rele | loc | sn | isb | aut | sale | name | cat | publ |
+-----+------+-----+-----+------+------+-----+------+-----+-----+------+------+-----+------+
| 118 | 4543 | 作 | 201 | 2013 | 2006 | --- | 2124 | 978 | 趙 | prom | 求索 | 商 | 聯經 |
| 118 | 872 | 馬 | 201 | 2013 | 2013 | --- | 2124 | 978 | 黎 | prom | 告別 | 政 | 聯經 |
| 118 | 2105 | 某 | 201 | 2013 | 2012 | --- | 2124 | 978 | 吳 | prom | 複眼 | 政 | 夏日 |
| 118 | 301 | 作 | 201 | 2013 | 2006 | --- | 2124 | 978 | 王 | norm | 天香 | 歷 | 麥田 |
| 118 | 411 | 少 | 201 | 2013 | 2008 | --- | 2124 | 978 | 韓 | norm | 鞋癖 | 商 | 聯經 |
| 119 | 3751 | 有 | 201 | 2013 | 2010 | --- | 2124 | 978 | 紀 | prom | 私家 | 體 | 印刻 |
| 119 | 3361 | 文 | 201 | 2013 | 2010 | --- | 2124 | 978 | 林 | fix_ | 我不 | 體 | 印刻 |
| 119 | 1140 | 何 | 201 | 2013 | 2012 | --- | 2124 | 978 | 邁 | norm | 正義 | 體 | 雅言 |
| 119 | 888 | 一 | 201 | 2013 | 2007 | --- | 2124 | 978 | 福 | fix_ | 生命 | 商 | 究竟 |
| 119 | 3283 | 近 | 201 | 2013 | 2011 | --- | 2124 | 978 | 芮 | norm | 海拉 | 政 | 遠流 |
+-----+------+-----+-----+------+------+-----+------+-----+-----+------+------+-----+------+
here the rake code to generate my data
16 bk = Book.new(:sn => real_sn,:name => book_name, :isbn=>isbn,
17 :price =>Random.rand(200..5000), :location=>location, :category=>["商業","歷史","體育","政治"].sample,
18 :author => author, :sale_type => [:fix_priced, :normal, :promotion].sample, :publisher => publisher,
19 :release_date => rand(10.years).ago, :comment => comment
20 )
Columns in the table I use the Postgre DB
Column | Type | Modifiers
--------------+-----------------------------+----------------------------------------------------
id | integer | not null default nextval('books_id_seq'::regclass)
price | integer |
comment | text |
created_at | timestamp without time zone | not null
updated_at | timestamp without time zone | not null
release_date | text |
location | text |
sn | bigint |
isbn | bigint |
author | text |
sale_type | text |
name | text |
category | text |
publisher | text |
Indexes:
"books_pkey" PRIMARY KEY, btree (id)
The code above, does not save any record in the database, it just instantiate objects of Book model. You should either save the object after initialization bk.save or use the create method instead of new.
bk = Book.new(:sn => real_sn,:name => book_name, :isbn=>isbn,
:price =>Random.rand(200..5000), :location=>location,
:category=>["商業","歷史","體育","政治"].sample, :author => author,
:sale_type => [:fix_priced, :normal, :promotion].sample,
:publisher => publisher, :release_date => rand(10.years).ago,
:comment => comment)
bk.save
Or alternatively you can use the create method
bk = Book.create(:sn => real_sn,:name => book_name, :isbn=>isbn,
:price =>Random.rand(200..5000), :location=>location,
:category=>["商業","歷史","體育","政治"].sample, :author => author,
:sale_type => [:fix_priced, :normal, :promotion].sample,
:publisher => publisher, :release_date => rand(10.years).ago,
:comment => comment)
Once saved in the database, it will automatically gain a unique id.

OOP programming method question

I have 2 objects, Area and SurfBreak. Area has many SurfBreaks and a SurfBreak publishes its conditions based on wind,wave,tide info from Area. This bit I've done an it works well:-)
I now have list of forecast data for Area - future changes to Area's attributes.
Whats the best OOP method to show the Surfbreaks conditions using forecast data for Area ?
Many thanks
Andy
----Updated---
Its a rails app
class Spot < ActiveRecord::Base
belongs_to :area
has_many :forecasts, :through => :area
def has_swell
wind = "#{area.swelldir}"
beachstart = "#{breakstr}"
beachend = "#{breakend}"
if ( ((wind.to_i) + 360 - (beachstart.to_i)) % 360 <= ((beachend.to_i) + 360 - (beachstart.to_i)) % 360 )
"#{area.swelldir} Has Incoming swell "
else
"#{area.swelldir} No Swell"
end
end
class Area < ActiveRecord::Base
has_many :spots
has_many :forecasts
class Forecast < ActiveRecord::Base
belongs_to :area
The DB tables are the objects in rails. I've got Area and Spot working nicely but I now want to display forecasts for a spot. This is the bit I'm not sure about.
mysql> desc areas;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| descrip | varchar(255) | YES | | NULL | |
| winddir | int(11) | NO | | NULL | |
| windspd | int(11) | NO | | NULL | |
| swelldir | int(11) | NO | | NULL | |
| swellhgt | float | NO | | NULL | |
| tide | int(11) | NO | | NULL | |
| lat | float | YES | | NULL | |
| lng | float | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
mysql> desc spots;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| descrip | varchar(255) | NO | | NULL | |
| breakstr | int(11) | NO | | NULL | |
| breakend | int(11) | NO | | NULL | |
| offstr | int(11) | NO | | NULL | |
| offend | int(11) | NO | | NULL | |
| besttide | int(11) | NO | | NULL | |
| area_id | int(11) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
mysql> desc forecasts;
+--------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| forecastdate | datetime | YES | | NULL | |
| area_id | int(11) | NO | | NULL | |
| winddir | int(11) | NO | | NULL | |
| windspd | int(11) | NO | | NULL | |
| swelldir | int(11) | NO | | NULL | |
| swellhgt | float | NO | | NULL | |
| tide | int(11) | NO | | NULL | |
+--------------+----------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
So say an Area has 24 Forecast rows in a DB , one for every hour in the future. In my app
what is the best way to output a spots forecast conditions. Without changing the relevant values in the Area as Areas hold the current conditions. I could just pull all the forecast data into an array an loop through it changing the Area object data, but this doesn't seem very OOP to me ?
As output I'm after something like
Current Spot Details (Using spot methods on Area attributes)
xxx
Forecast Details for this spot (Using spot methods on Forecast attributes )
Hour 1 xxx
Hour 2 xxx
Hour 3 xxx
..
Sorry if this is not very well explained.
Regards
Andy
Your class Area sounds like it is doing too many things, and it is changing for different reasons. Separate it out so the Area has a list of WeatherData or something, so your forecasting code can iterate through the WeatherData without Area having to change. Your WeatherData object can include a flag saying whether it's real data or a forecast.
Class Area{
Wind wind;
Wave wave;
Tide tide;
}
Class SurfBreak extends Area{
//some SurfBreaks' field
public ForecastDetail getForecastDetail(){
//operate directly onwind wave tide fields and calculate
}
}
You haven't explained exactly how you developed first part of the problem (relations between Area and SurfBreaks), but, I would consider using of Observer design pattern here. So SurfBreaks would be Observers of Area changes.

Rails, if instance is in a scope?

I'm using rails 3 and I can't seem to check if a given instance is in a scope, see here:
p = Post.find 6
+----+----------+-------------------------+-------------------------+-------------------------+-----------+
| id | title | publish_date | created_at | updated_at | published |
+----+----------+-------------------------+-------------------------+-------------------------+-----------+
| 6 | asfdfdsa | 2010-03-28 22:33:00 UTC | 2010-03-28 22:33:46 UTC | 2010-03-28 22:33:46 UTC | true |
+----+----------+-------------------------+-------------------------+-------------------------+-----------+
I have a menu scope which looks like:
scope :menu, where("published != ?", false).limit(4)
When I run it I get:
Post.menu.all
+----+------------------+------------------+------------------+-------------------+-----------+
| id | title | publish_date | created_at | updated_at | published |
+----+------------------+------------------+------------------+-------------------+-----------+
| 1 | Lorem ipsum | 2010-03-23 07... | 2010-03-23 07... | 2010-03-28 21:... | true |
| 2 | fdasf | 2010-03-28 21... | 2010-03-28 21... | 2010-03-28 21:... | true |
| 3 | Ruby’s Imple... | 2010-03-28 21... | 2010-03-28 21... | 2010-03-28 21:... | true |
| 4 | dsaD | 2010-03-28 22... | 2010-03-28 22... | 2010-03-28 22:... | true |
+----+------------------+------------------+------------------+-------------------+-----------+
Which is correct, but if I try to check if p is in the the menu scope using: Post.menu.exists?(p) I get true when it should be false
What is the proper way to find out if a given instance of something is in a scope?
Actually, I was able to solve it using that Array method of include? instead of exists?

Resources