Manually building active record objects - ruby-on-rails

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)

Related

Rails 5 query returns :ActiveRecord_Relation instead of data

I am trying to access some specific data from the database using active records, and I am getting <ScrapedPage::ActiveRecord_Relation:0x00007f1c4502ef78> instead of the actual data. I bet its something easy I'm missing but I can't figure it out.
Queries:
#domain = params[:domain].to_s
version_one = ScrapedPage.select("html").where(domain: #domain ,created_at: params[:version_one]).to_s
version_two = ScrapedPage.select("html").where(domain: #domain ,created_at:params[:version_one]).to_s
Use find_by instead of where.
where returns ActiveRecord::Relation and find_by returns only one record.
If your request will return one record, you can use where with first like the below:
version_one = ScrapedPage.select("html").where(domain: #domain, created_at: params[:version_one]).first
version_one is a relation object, you can access the html data like the below:
version_one.html
You can see result of the query with both find_by and where the below.

How do you access an object's (ActiveRecord::Relation) value by key in Ruby on Rails?

tl;dr How do I get the corresponding value with the key of an object?
I'm confused why
Atag.where(tag:'brand') gives me what I would call an object for lack of a better term: #<ActiveRecord::Relation [#<Atag id: 1, tag: "brand", created_at: "2015-01-31 04:29:20", updated_at: "2015-01-31 04:29:20">]>
But I'm having the basic difficult of accessing the corresponding value for the key :id.
Atag.where(tag:'brand').id and Atag.where(tag:'brand')[:id] and Atag.where(tag:'brand')(:id) all throw errors, while in this case I'm just trying to have the integer 1 returned.
I seem to be unable to ruby, nor find a succinct answer to this basic question with my google searching skills (or lack there of).
Thanks
From great documentation at the Odin Project.
The key thing to note is that #find returns the actual record while #where returns an ActiveRecord::Relation which basically acts like an array.
So if you're using #where to find a single record, you still need to remember to go into that "array" and grab the first record, e.g. User.where(:email => "foo#bar.com")[0] or User.where(:email => "foo#bar.com").first.
This gets me all the time...
Get the id of your tag = 'brand' with following query:
Atag.find_by(tag:'brand').id
Check following variations:
Atag.find(1)
#gives you the object with the Atag id = 1
Atag.find(100) #let's say this record does not exist then you will
get ActiveRecord::RecordNotFound exception.
Better option :
Atag.where(id: 1)
#this returns you a relation and it's true you are trying to access
only a single object.
Hence, you just need to modify it to :
Atag.where(id: 1).first
#Above one will give you an object of Atag not an association result.
# to verfiy you can execute, Atag.where(id: 1).first.class
Atag.where(id: 999).first
# In this case if there is no record found with id = 999, then it'll
return nil which can be easily handled than an exception found
while using find method.
Get the same flavor using the dynamic finders.
Atag.find_by(id: 1) #gives the Atag with id 1
Atag.find_by_id(1). # same as above.
Atag.find_by(id: 999) #if not found then simply returns nil.
Atag.find_by(name: 'ruby') #return Atag object with name: 'ruby'
Atag.find_by_name('ruby') #same as above.
Yep, looks like you figured it out. For reference, you can use Atag.where(tag:'brand').first to get the first result, and Atag.where(tag:'brand').to_a to get an array of all the matching results.
where return instance of ActiveRecord::Relation which can be treated like an array with records as its members. Even if the result is single it should be accessed like a member of array with single element
Atag.where(tag: 'brand')
returns the array of results and to access id we should get the record from the array first i.e.
Atag.where(tag: 'brand')[0].id
In order to get id of all the matching records we need to use pluck with where. pluck returns an array of attribute that is plucked.
Atag.where(tag: 'brand').pluck(:id)
This would return an array of id from the collection returned by where only.
The find_by method finds the first record matching some conditions. Since find_by returns the record (not an array) , we can do:
Atag.find_by(tag: 'brand').id
PS: No one had mentioned pluck that's why I wrote this answer. Hope its helpful.

How to efficiently retrieve all record in rails 3.2 and assign them to a query-able object?

In our rails 3.2 app, we need to retrieve all customer records out of customer table and assign them to a variable customers and do query (such as .where(:active => true) on variable customers late on. There are 2 questions here:
what's the better way to retrieve all records?
Customer.all works. However according to rails document, it may have performance issue when Customer table gets large. We tried Customer.find_each and it has error "no block given (yield)".
How to make the variable customers query_able?
When performing query on variable customers (like customers.where(:active => true)), there is an error: undefined methodwhere' for #. It seems that thecustomersis an array object and can't takewhere. How can we retrievecustomers` in such a way it can be query-able?
Thanks for help.
In Rails < 4 .all makes database call immediately, loads records and returns array. Instead use "lazy" scoped method which returns chainable ActiveRecord::Relation object. E.g.:
customers = Customer.scoped
...
customers = customers.where(:active => true)
customers = customers.where(...)
etc...
And at the moment when you will need to load records and iterate over them you can call find_each:
customers.find_each do |customer|
...
end

How to execute a raw update sql with dynamic binding in rails

I want to execute one update raw sql like below:
update table set f1=? where f2=? and f3=?
This SQL will be executed by ActiveRecord::Base.connection.execute, but I don't know how to pass the dynamic parameter values into the method.
Could someone give me any help on it?
It doesn't look like the Rails API exposes methods to do this generically. You could try accessing the underlying connection and using it's methods, e.g. for MySQL:
st = ActiveRecord::Base.connection.raw_connection.prepare("update table set f1=? where f2=? and f3=?")
st.execute(f1, f2, f3)
st.close
I'm not sure if there are other ramifications to doing this (connections left open, etc). I would trace the Rails code for a normal update to see what it's doing aside from the actual query.
Using prepared queries can save you a small amount of time in the database, but unless you're doing this a million times in a row, you'd probably be better off just building the update with normal Ruby substitution, e.g.
ActiveRecord::Base.connection.execute("update table set f1=#{ActiveRecord::Base.sanitize(f1)}")
or using ActiveRecord like the commenters said.
ActiveRecord::Base.connection has a quote method that takes a string value (and optionally the column object). So you can say this:
ActiveRecord::Base.connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{ActiveRecord::Base.connection.quote(baz)}
EOQ
Note if you're in a Rails migration or an ActiveRecord object you can shorten that to:
connection.execute(<<-EOQ)
UPDATE foo
SET bar = #{connection.quote(baz)}
EOQ
UPDATE: As #kolen points out, you should use exec_update instead. This will handle the quoting for you and also avoid leaking memory. The signature works a bit differently though:
connection.exec_update(<<-EOQ, "SQL", [[nil, baz]])
UPDATE foo
SET bar = $1
EOQ
Here the last param is a array of tuples representing bind parameters. In each tuple, the first entry is the column type and the second is the value. You can give nil for the column type and Rails will usually do the right thing though.
There are also exec_query, exec_insert, and exec_delete, depending on what you need.
None of the other answers showed me how to use named parameters, so I ended up combining exec_update with sanitize_sql:
User.connection.exec_update(
User.sanitize_sql(
[
"update users set name = :name where id = :id and name <> :name",
{
id: 123,
name: 'My Name'
}
]
)
)
This works for me on Rails 5, and it executes this SQL:
update users set name = 'My Name' where id = 123 and name <> 'My Name'
You need to use an existing Rails model instead of User if you don't have that.
I wanted to use named parameters to avoid issues with the ordering when I use ? or $1/$2,etc. Positional ordering is kind of frustrating when I have more than a handful of parameters, but named parameters allow me to refactor the SQL command without having to update the parameters.
You should just use something like:
YourModel.update_all(
ActiveRecord::Base.send(:sanitize_sql_for_assignment, {:value => "'wow'"})
)
That would do the trick. Using the ActiveRecord::Base#send method to invoke the sanitize_sql_for_assignment makes the Ruby (at least the 1.8.7 version) skip the fact that the sanitize_sql_for_assignment is actually a protected method.
Sometime would be better use name of parent class instead name of table:
# Refers to the current class
self.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
For example "Person" base class, subclasses (and database tables) "Client" and "Seller"
Instead using:
Client.where(self.class.primary_key => id).update_all(created _at: timestamp)
Seller.where(self.class.primary_key => id).update_all(created _at: timestamp)
You can use object of base class by this way:
person.class.unscoped.where(self.class.primary_key => id).update_all(created _at: timestamp)
Here's a trick I recently worked out for executing raw sql with binds:
binds = SomeRecord.bind(a_string_field: value1, a_date_field: value2) +
SomeOtherRecord.bind(a_numeric_field: value3)
SomeRecord.connection.exec_query <<~SQL, nil, binds
SELECT *
FROM some_records
JOIN some_other_records ON some_other_records.record_id = some_records.id
WHERE some_records.a_string_field = $1
AND some_records.a_date_field < $2
AND some_other_records.a_numeric_field > $3
SQL
where ApplicationRecord defines this:
# Convenient way of building custom sql binds
def self.bind(column_values)
column_values.map do |column_name, value|
[column_for_attribute(column_name), value]
end
end
and that is similar to how AR binds its own queries.
I needed to use raw sql because I failed at getting composite_primary_keys to function with activerecord 2.3.8. So in order to access the sqlserver 2000 table with a composite primary key, raw sql was required.
sql = "update [db].[dbo].[#{Contacts.table_name}] " +
"set [COLUMN] = 0 " +
"where [CLIENT_ID] = '#{contact.CLIENT_ID}' and CONTACT_ID = '#{contact.CONTACT_ID}'"
st = ActiveRecord::Base.connection.raw_connection.prepare(sql)
st.execute
If a better solution is available, please share.
In Rails 3.1, you should use the query interface:
new(attributes)
create(attributes)
create!(attributes)
find(id_or_array)
destroy(id_or_array)
destroy_all
delete(id_or_array)
delete_all
update(ids, updates)
update_all(updates)
exists?
update and update_all are the operation you need.
See details here: http://m.onkey.org/active-record-query-interface

Unable to read values from object returned from ActiveRecord.find

I make the following call to the DB.
#patientRegistration = PatientRegistration.find(:all,
:conditions=>["name = '#{patientName}'"])
This searches for a patient registration based on a given name. I get a valid #patientRegistration object. When I invoke #patientRegistration.inspect it prints correctly all the values for the object in the DB.
But when I try to read a particular attribute (say id or name) by doing the following: #patientRegistration.id or #patientRegistration.name, I get invalid values. Either its blank or some junk values. I don't understand how inspect is able to retrieve all the values correctly but reading individual attributes gives invalid values.
Thanks
find(:all) returns an array of all the records that match the conditions (inspect probably shows you the result in square brackets). #patientRegistration.first.name will return you the name of the first record in the array. However, if you are only interested in the first or only record that matches the conditions, then you can use find(:first) instead:
#patientRegistration = PatientRegistration.find(:first,
:conditions => ["name = ?", patientName])
Note that I've also changed your condition to use a parameter so that it is no longer at risk from SQL injection attacks.
You can also re-write this code using an attribute-based finder:
#patientRegistration = PatientRegistration.find_by_name(patientName)
This will do a find(:first). For the equivalent of find(:all), use find_all_by instead of find_by.

Resources