ActiveRecord query with group and average - ruby-on-rails

I have two models in a Rails application: a home has many speed_tests.
Every speed_test has as attributes: hostname and download.
I want to group hostnames and display the average of the download.
First of all I filter by city:
sts = SpeedTest.joins(:home).where("homes.city": city)
I have a collection of speed_tests, now I want to group them by hostname and get the average of download for every hostname
result = sts.select("hostname, AVG(download) as avg_download").group(:hostname)
The results are as following:
=> #<ActiveRecord::Relation [#<SpeedTest id: nil, hostname: "46.128.35.112.dynamic.cablesurf.de">]>
Why I can't see the column avg_download? Am I force to use the column speed_test id? I am not interested in this attribute.

You can't see it because that field is not part of your model, it's a virtual attribute. Anyway, it's present. Just call it. Try
result.map(&:avg_download)

Related

Reference another fixture without using label rails

I have this model called Region and Admin
# regions.yml
one:
name: test
two:
name: test2
# admins.yml
one:
name: admin1
two:
name: admin2
There's a column on admin.rb defined as json column (store_accessor: :region_ids). How can I reference regions on admins.yml?
# admins.yml
one:
name: admin1
region_ids: ?????
This is an updated answer using erb to get the id for the various regions.
This creates structure json and queries the database to get the first and second Region IDs. These would be loaded in to the DB from the regions.yml when you run your tests.
# admins.yml
one:
name: admin1
region_ids: >
[{"region_one_id":<%= Region.first.id.to_json.inspect %>},
{"region_two_id":<%= Region.second.id.to_json.inspect %>}]
I looked into Fixture label interpolation to get the id but can't figure it out with your table holding json instead of a single id for another model.
Personally I would pre-define your ids on your regions.yml and then directly reference them in your admins.yml. This will help ensure you know the IDs and you have accurate data you've structured to test against.
I asked my colleague and got the answer. This will work:
# admins.yml
one:
region_ids: <%= Region.pluck(:id) %>
rails will load regions.yml when it gets to this point.

rails can you reference a model within a model?

I seem to have run into a problem with trying to use a model in another model in Rails.
I am pulling a list of users from Active Directory with LDAP in a dropdown. I want to parse the cn that I get from Ldap into a firstname and lastname.
The problem I am running into is that I need to find a record in the users model. The parsing is being done in observations.rb.
Observation.rb:
def parse_employee
#emp_name = '' #initialize
self.employee_raw = self.employee_raw[2...-2] # get rid of the quotes and brackets
#emp_name = self.employee_raw.split(' ') # split first/last names
#emp_first_name = #emp_name[0] #Grab the first name
#emp_last_name = #emp_name[1] # grab the surname
#user = User.where("last_name like ?", #emp_last_name)
self.employee_id = #user.id
end
I've played with this quite a bit and it appears that I can't reference other models from within a model.
To sum up, what I am trying to do is
1. Have the user select the appropriate person from a dropdown that is pulled via LDAP from active directory.
2. Use the first and last names to find the appropriate user in my user table (Right now I'm just trying to get it to work with the last name as that is unique enough)
3. When I find the correct user in the user table, enter that id in the employee_id field in my observations table.

Rails 4 how to know if update has been executed

I have this code in Rails 4:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
The code is working ok, but now I would need to gather some statistics, like:
Number of bases.
Number of new bases.
Number of loaded (updated) bases.
Number of bases is not a problem. Number of new bases, I think I could check if the object mmkBase returned by find_or_initialize comes with more attributes than just the id, for example with a name.
However, I don´t know how can I check if the update operation is run.
Is there any way to know this? Is there a better way to achieve this? Maybe, I should use a more traditional way to do it, with a select first?
However, I don´t know how can I check if the update operation is run
You can simply use...
if mmkbase.update mkbase_params
...
else
...
This will invoke the update method, generating a response determined by whether it was successful or not (true = success; false = validation failure)
Number of new bases
As mentioned, the new_record? method is what you need here...
if mmkbase.new_record? #-> mmkbase hasn't been saved to db yet
I'd do this:
mmkBase.find_or_create_by(mmkbase_params) do |base|
#do something if new base
end
private
def mmkbase_params
params.require(:mmkbase).permit(:name, :city, :country, :address, :region_ids, :latitude, :longitude)
end
If you're having to pull this type of data by its primary key (find_or_initialize_by id:), you're in trouble... how do you know if the primary key will remain constant?
Of course, this is not always the case. Changing user details would require you to reference the user id - however that data is rigid. IE a user registered (with name and email), and you're now looking for that user (it can't be created on the fly).
I don't know your schema; anything which can be created like this should be referenced by its core data (name, city, etc)...
mmkbase.find_by(name: ....)
In relational databases, you need to be dealing with the data, not the primary keys. In other types of db, you can deal with indexes, but with this type of setup, it's much better to work with the pure data.
You want to distinguish between an already existing record and a newly created record. On a new record which was only initialized by find_or_initialize_by, new_record? returns true. So for example you could write:
mmkBase = Mmk::Base.find_or_initialize_by(id: id)
new_base = mmkBase.new_record?
if mmkBase.update(name: name, city: city, country: country, address: address, region_ids: regionIds, latitude: latitude,
longitude: longitude)
# update was successful
if new_base
new_bases += 1
else
updated_bases += 1
end
else
# error handling
end
Note that it is probably not a good idea to use find_or_initialize_by with the ID of the record. See Rich Peck's answer.
You could probably check the updated_at timestamp on the object to see if it has been updated recently but the Rails 4 update method doesn't tell you whether an object was updated or not.
http://apidock.com/rails/ActiveRecord/Base/update/class

Manually building active record objects

I have an active record query that is not using an index, resulting in app timeouts.
domain.services.where.not(parent_service_id: nil).group('services.type').select('services.type, count(services.id) as user_count')
=> [#<ServiceModel id: nil, type: "ServiceModelName">]
I have a custom sql command that forces use of the index on parent_service_id
ActiveRecord::Base.connection.execute("SELECT services.type, count(services.id) as user_count FROM services where domain_id = 21227 AND parent_service_id IS NOT NULL GROUP BY services.type")
=> {"type"=>"ServiceModelName", "user_count"=>"2810"}
Which returns the information I need quickly. However, it returns a hash instead of a model. How do I build out an activerecord object so my method can return a similar result?
Instead of connection.execute, you should just be able to do:
ServiceModel.find_by_sql(sql)

How to use a postgresql function in Rails

I have a table in postgres with date_of_birth and would like to use the Postgres age function to return the age, rather than calculate the age in Rails (this is a simple example, I will want to do more complicated calculations in postgres).
What is the best way to get the age back from postgres with the rest of my record, ideally as standard without having to modify every select?
EDIT:
I've decided to use views because:
My database will be used by other applications other than rails and I want to define common functions that all of them can use.
I'd like to control access to the data over multiple applications.
It takes some of the processing away from the application server.
It is more scalable if I use a lot of calculated fields.
people = Person.select('*, age(date_of_birth)')
people.each { |person| puts person.age }
Yes, this will add a method, age, that did not previously exist on Person
You can also alias the new method to something other than the function name:
people = Person.select('*, age(date_of_birth) as foo')
people.each { |person| puts person.foo }

Resources