How to order by descending value in controller? - ruby-on-rails

I am trying to show the list of jobs ordered by median_salary by descending order. So far, it seems to only take into account the first number of median_salary. So something like 900 is listed above 1000, even though the value of 1000 > 900.
homes_controller.rb:
def index
nyc_highest = Highestpaidjob.where("city = ?", "NYC")
#nyc_highest = nyc_highest.order("median_salary DESC")
end
index.html.erb:
<%= #nyc_highest.inspect %>
returns:
#<ActiveRecord::Relation [#<Highestpaidjob id: 11, job: "Architect, City Planner", median_salary: "95928.48", count: 237, margin_of_error: "", city: "NYC", state: "New York", created_at: "2016-07-25 18:17:17", updated_at: "2016-07-25 18:17:17">, #<Highestpaidjob id: 7, job: "Medical", median_salary: "170507.69", count: 128, margin_of_error: "", city: "NYC", state: "New York", created_at: "2016-07-25 18:09:30", updated_at: "2016-07-25 18:09:30">]>
It is listing 95928.48 as higher than 170507.69. Am I missing a condition?
I've looked at Best way to implement sort asc or desc in rails and it seemed to suggest the way I am currently writing the sort.

It's because your median_salary database field is string and it's sorted as string. You need to cast it to integer in order clause, or create a migration, which will change field datatype.
Difference between strings being sorting and floats being sorted:
irb(main):001:0> ["95928.48", "170507.69"].sort
=> ["170507.69", "95928.48"]
irb(main):002:0> [95928.48, 170507.69].sort
=> [95928.48, 170507.69]
In postgres your order clause should looks like this:
#nyc_highest = nyc_highest.order("CAST(median_salary as FLOAT) DESC")

As #teksisto said, you should change the median_salary for float or some type that accepts decimals. Also, I would suggest to create a scope on your model, something like
scope :nyc_highest, -> { where("city = ?", "NYC").order("median_salary DESC") }
on your Highestpaidjob model. Then, you just call Highestpaidjob.nyc_highest in any place of your application you like.
For changing the median_salary data type:
rails g migration ChangeMedianSalaryType
then edit your migration file:
class ChangeMedianSalaryType < ActiveRecord::Migration
def up
change_column :highestpaidjobs, :median_salary, :float
end
def down
change_column :highestpaidjobs, :median_slary, :string
end
end

Related

Compare two records and get the names of the changed columns in rails active record

i need to compare two active records and get the name of the changed column
#<Evaluation id: 1, name: "Foo", status: "yes", comments: "can be better", created_at: "2017-05-09 12:00:00", updated_at: "2017-05-09 12:00:00">
#<Evaluation id: 2, name: "Foo", status: "yes", comments: "keep up the good work", created_at: "2017-05-09 12:05:00", updated_at: "2017-05-09 12:05:00">
I need to compare these two records and get the names of the changed columns. In this case :comments, :created_at, :updated_at
I have tried using methods like eql?, record1.attributes.except('id') == record2.attributes.except('id')
These only return either true or false I need to get the column names as well.
This can be done by comparing each column but i am looking for any other option.
You can use a small method to do this for you :
def diff_active_record(record_a, record_b)
(record_a.attributes.to_a - record_b.attributes.to_a).map(&:first)
end
This method will take the two active_record objects(in this case : record_a , record_b ) to compare and generate an array of attribute names that have changed.
You may use it like this:
diff_active_record(Evaluation.find(1), Evaluation.find(2))
The above would result in [:id, :comments, :created_at, :updated_at]

rails : How to omit a particular property from an array of objects in a JSON response?

I've been trying to get a model that doesn't include the id column. I think that the method select allows this, but when I access models, I see the id field with nil value.
When I use:
module API
class MyController < ActionController::API
def index
response = MyModel
.where("value > ?", 0)
.select('code','value')
render json: response, status: 200
end
When I inspect the result using each like this
MyModel
.where("value > ?", 0)
.select('code','value')
.each{|m| puts m}
I get this
<MyModel id: nil, code: "110", value: 100>
<MyModel id: nil, code: "111", value: 100>
<MyModel id: nil, code: "112", value: 100>
and in my response I get this:
[{id: null, code: "110", value: 100},{id: null, code: "111", value: 100},{id: null, code: "112", value: 100}]
How to omit the id column?
I believe it's something with the puts command. If you only do Model.select(:attributes), it shouldn't return id column if it is not in the list (did the test here and works fine). I believe the puts command retrieve the object from database or something like that. By the way, you can override what is printed when you do puts object if you override the to_s method in the object class
In Rails, when using #select method, even if you are not using id in select method arguments, it will show it because in Rails by default primary key is id. If you overwrite the primary key, to something else, it will then show up that column. Look example:
Coupon.select(:active)
# Coupon Load (0.6ms) SELECT "coupons"."active" FROM "coupons"
# => #<ActiveRecord::Relation [#<Coupon active: true, code: nil>, #<Coupon active: true, code: nil>...
This is because in my Coupon model I have primary key set to code.
class Coupon < ActiveRecord::Base
self.primary_key = 'code'
# ..
end
Although it is showing up in the inspect result, it is not there until, you are fetching it really. Now if you set up the self.primary_key = '' for testing, you will see that #select not showing anything. But don't set your primary_key to '' like I did to remove the id display from select result. I just wanted to show from where it comes, nothing else.
Coupon.select(:active).order(:active)
# Coupon Load (2.8ms) SELECT "coupons"."active" FROM "coupons" ORDER BY "coupons"."active" ASC
# => #<ActiveRecord::Relation [#<Coupon active: true>, #<Coupon active: true>...

Rails order records by column and custom method

For one of my models I'm trying to set a default scope that sorts by year and season. Since year is an integer, it's easy to order by that. My trouble is ordering by season (if the year is the same). Here's just ordering by year:
class League < ActiveRecord::Base
def self.default_scope
order(:year)
end
# The season's that are allowed to be used
# This is also the order I'd like to use
def self.season_collection
{
"Spring" => "Spring",
"Summer" => "Summer",
"Fall" => "Fall"
}
end
end
If I try order(:year, :season) then that will just do it alphabetically. Is there any way to use order (so it's done on the database end)?
You can order them in the database, but it isn't going to be very efficient as you'll need to coerce the value of the season field into a integer, and then use that to order the records. See this answer for an example:
SQL: ORDER BY using a substring within a specific column... possible?
A better way would be to store the season as an integer, not a string, in the database. The easiest way to use this would be ActiveRecord::Enum available in Rails 4.1+. In your model add this:
class League < ActiveRecord::Base
enum season: %w{Spring Summer Autumn Winter}
end
Then you can create records like this:
0> league1 = League.create!(season: 'Summer')
=> #<League id: 1>
1> league2 = League.create!(season: 'Spring')
=> #<League id: 2>
2> league3 = League.create!(season: 'Autumn')
=> #<League id: 3>
3> league3.season
=> "Autumn"
Under the hood ActiveRecord doesn't store the string, but an integer referring to it. You can find the integers as follows:
4> League.seasons
=> {"Spring"=>0, "Summer"=>1, "Autumn"=>2, "Winter"=>3}
To get them in order it's then just a case of ordering the field:
5> League.order(:season)
SELECT * FROM leagues ORDER BY season
=> #<ActiveRecord::Relation [#<League id: 2>, #<League id: 1>, #<League id: 3>]>
If you want to query for a specific season ActiveRecord will automatically map the name to the ID:
6> League.where(season: 'Summer')
SELECT * FROM leagues WHERE season = 1
=> #<ActiveRecord::Relation [#<League id: 1>]>
If you try and set an invalid season, ActiveRecord will let you know:
7> league3.season = 'Tomato'
ArgumentError: 'Tomato' is not a valid season

Sorting local variable

My Rails app is getting the history of changes for two models, using the auditor gem like so:
#audit = Audit.where( :auditable_id => current_user.posts,
:auditable_type => "Post") +
Audit.where( :auditable_id => #comments,
:auditable_type => "Comment")
This works, but then I need to sort the whole #audit variable by the time the change was made.
I have two issues to solve.
the following methods have not worked: sort, sort_by, order
I need to figure out which of the following fields I need to sort by:
=> Audit(id: integer, auditable_id: integer, auditable_type: string, owner_id: integer, owner_type: string, user_id: integer, user_type: string, action: string, audited_changes: text, version: integer, comment: text, **created_at**: datetime)
1.9.3-p194 :002 > Audit.last
Audit Load (168.0ms) SELECT "audits".* FROM "audits" ORDER BY version DESC, created_at DESC LIMIT 1
=> #<Audit id: 5, auditable_id: 58, auditable_type: "Post", owner_id: 58, owner_type: "Post", user_id: 1, user_type: "User", action: "update", audited_changes: {"status"=>["to approve", "to review"], " **updated_at** "=>[2012-08-24 15:29:26 UTC, 2012-08-24 19:29:52 UTC]}, version: 2, comment: "post modified by Bruno Amaral ", created_at : "2012-08-24 19:29:52">
You should be able to build a single query to load all of the Audit objects you're interested in. Since it's a single query, the database can handle the sorting too.
The SQL you want to execute looks something like this:
SELECT *
FROM audits
WHERE
auditable_type = 'Post' AND auditable_id = … OR
auditable_type = 'Comment' AND auditable_id IN (…)
ORDER BY created_at
Which you should be able to build using Arel with something like this (assuming you're using Rails 3):
t = Audit.arel_table
for_post = t[:auditable_type].eq('Post').and(t[:auditable_id].eq(post.id))
for_comments = t[:auditable_type].eq('Comment').and(t[:auditable_id].in(comment_ids))
audits = Audit.where(for_post.or(for_comments)).order(:created_at)
For more information about building complex queries with Arel, see ASCIIcasts episode 215 and the Arel README.
If you don't like the Arel syntax, you can also use find_by_sql or pass a string to where, but bear in mind that using Arel will shield you from various kinds of errors and subtle difference between databases.
#audit = Audit.where( "auditable_id IN (?) OR auditable_id IN (?)",
current_user.posts,
#comments ).order_by( :created_at )

Ruby/Rails - Check If Child Id Exists Within Record of HABTM Relationship

I have a set of resources called Tasks and Posts and there are in a has_and_belongs_to_many (HABTM) relationship with each other.
There is also a join table connecting their values.
create_table 'posts_tasks', :id => false do |t|
t.column :post_id, :integer
t.column :task_id, :integer
end
So my question is how do I check to see if the id of a specific task exists within the array created from #post.tasks?
irb(main):011:0> #post = Post.find(1)
=> #<Post id: 2, comment: "blah blah", created_at: "2011-10-18 03:40:30", updated_at:
irb(main):012:0> #post.tasks
=> [#<Task id: 1, description: "Test 1", created_at: "2011-10-18 03:
22:05", updated_at: "2011-10-18 03:22:05">, #<Task id: 3, description: "Test 3",
created_at: "2011-10-18 03:22:21", updated_at: "2011-10-18 03:22:21
">]
So my question is whats the ruby way for writing does "#task = Task.find(2)" exist within #post.tasks and if so return true or false?
#post.tasks.where(:id => task_id).present?
Is much lighter compared to what Gabelsman has suggested.
#post.tasks.map(&:id).include?(task_id)
Where task_id is the id of the task you want to check. To explain this a little bit, you're taking the list of tasks, mapping them to an array of their ids, and then checking to see if the id in question is in that array. If you're not familiar with map you should check it out, it's awesome.
ruby-docs
Assuming you named your has_and_belongs_to_many to :posts then doing something like this in Rails is very optimized:
#post.task_ids.include?(task.id)
I noticed that the two answer provided here result in n select statements. But doing it with task_ids caches the first query and the rest does not require a SELECT statement from the DB.
You can use exists? method for that:
#post.tasks.exists?(id: task_id)

Resources