Rails 4.1 upgrade, having trouble with group - ruby-on-rails

So my 3.2 code looks like this
AssocGenre.includes(:genre).where(attachable_type: Project).count(group: 'genres.name').sort_by{|k,v| -v}.each do
But now it's giving me this error
undefined method `sort_by' for 193:Fixnum
What is the correct syntax for this in rails 4.1 now?

You used to be able to specify the GROUP BY clause when calling count but not anymore. Now you have to specify the GROUP BY with a separate group call. From the fine manual:
count(column_name = nil, options = {})
Count the records.
[...]
If count is used with group, it returns a Hash whose keys represent the aggregated column, and the values are the respective amounts:
Person.group(:city).count
# => { 'Rome' => 5, 'Paris' => 3 }
You probably want to include a simple INNER JOIN in the SQL rather than all the extra stuff that includes adds so joins should work better.
So you want to write it this way now:
AssocGenre.joins(:genre)
.where(attachable_type: Project)
.group('genres.name')
.count
.sort_by ...

just have a try :
AssocGenre.includes(:genre).where(attachable_type: Project).count(group:'genres.name').**to_a**.sort_by{|k,v| -v}.each.....

Related

Delete from database with specific details in ruby

I try to delete from database all row has a id include in some array or (key,value)
"recp" => "1, 2, 3 , 6, 7 , ........."
ID in #recipient
I try this:
#v = NameOfDatabase.where.not(:id=> #recipient.split(',').map(&:to_i), :conditions => {:thread =>#dis_PI.m_id}).destroy_all
With specific condition i want to remove row with this condition and not include in #recipient
Error in this method :
NoMethodError (undefined method `where' for #<Class:0x7f447f57b140>):
I try multiple code but not working, i put this question multiple time but also not work yet!
From the comments, I learned that you are running a very old version of Ruby on Rails – probably more than 10 years old. With Rails 3.0 the finder methods change completely and therefore all current documentation for Rails will not be helpful anymore. Especially the where method did not exist before Rails 3.0
In such an old version the following should work:
YourModel.destroy_all("id in (?)", #recipient.split(','))
Here you will find the docs of older Rails versions.
The condition is basically just one SQL fragment. When you want to add more conditions then you need to write all conditions in one line like this:
YourModel.destroy_all(
"id IN (?) AND thread = ?", #recipient.split(','), #dis_PI.m_id
)

Using Rails ActiveRecord to Query Unsaved Objects

Is it possible to query unsaved changes using Rail's ActiveRecord or another similar approach?
An example of a Ruby interactive session is below. What I'd like to see, is the fourth line show a result of '999' instead of '10'. I'm use to using .NET and Entity Framework where something similar to this was possible. Perhaps in Ruby there is a better or different way to do the same thing. I could obviously add up the sum in a loop, but I find the query syntax more elegant. Any help is appreciated.
i = Inventory.where(:product_id => 1)
i.sum(:available) => 10
i.first.available = 999
i.sum(:available) => 10
No, since sum() is actually translated to SQL and run on the db, you must save the record to the db in order for the query to return the result you want.
Alternatively, you can use the Enumerable#sum method in ActiveSupport, which takes a block, like so:
all = Inventory.where(:product_id => 1).to_a
all.first.available = 999
all.sum(&:available)

Remove a 'where' clause from an ActiveRecord::Relation

I have a class method on User, that returns applies a complicated select / join / order / limit to User, and returns the relation. It also applies a where(:admin => true) clause. Is it possible to remove this one particular where statement, if I have that relation object with me?
Something like
User.complex_stuff.without_where(:admin => true)
I know this is an old question, but since rails 4 now you can do this
User.complex_stuff.unscope(where: :admin)
This will remove the where admin part of the query, if you want to unscope the whole where part unconditinoally
User.complex_stuff.unscope(:where)
ps: thanks to #Samuel for pointing out my mistake
I haven't found a way to do this. The best solution is probably to restructure your existing complex_stuff method.
First, create a new method complex_stuff_without_admin that does everything complex_stuff does except for adding the where(:admin => true). Then rewrite the complex_stuff method to call User.complex_stuff_without_admin.where(:admin => true).
Basically, just approach it from the opposite side. Add where needed, rather than taking away where not needed.
This is an old question and this doesn't answer the question per say but rewhere is a thing that exists.
From the documentation:
Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
So something like:
Person.where(name: "John Smith", status: "live").rewhere(name: "DickieBoy")
Will output:
SELECT `people`.* FROM `people` WHERE `people`.`name` = 'DickieBoy' AND `people`.`status` = 'live';
The key point being that the name column has been overwritten, but the status column has stayed.
You could do something like this (where_values holds each where query; you'd have to tweak the SQL to match the exact output of :admin => true on your system). Keep in mind this will only work if you haven't actually executed the query yet (i.e. you haven't called .all on it, or used its results in a view):
#users = User.complex_stuff
#users.where_values.delete_if { |query| query.to_sql == "\"users\".\"admin\" = 't'" }
However, I'd strongly recommend using Emily's answer of restructuring the complex_stuff method instead.
I needed to do this (Remove a 'where' clause from an ActiveRecord::Relation which was being created by a scope) while joining two scopes, and did it like this: self.scope(from,to).values[:joins].
I wanted to join values from the two scopes that made up the 'joined_scope' without the 'where' clauses, so that I could add altered 'where' clauses separately (altered to use 'OR' instead of 'AND').
For me, this went in the joined scope, like so:
scope :joined_scope, -> (from, to) {
joins(self.first_scope(from,to).values[:joins])
.joins(self.other_scope(from,to).values[:joins])
.where(first_scope(from,to).ast.cores.last.wheres.inject{|ws, w| (ws &&= ws.and(w)) || w}
.or(other_scope(from,to).ast.cores.last.wheres.last))
}
Hope that helps someone

How to get table column value?

I write follow code to get one record from the table webeehs:
webeehs_result = Webeeh.find(:all, :conditions=>["webeeh_project_id=#{project_id}"])
Then I want to get one column value from this record, how could I do?
For example, the column name is webeeh_date.
first of all, never EVER write code like that. Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. If you must do conditions, then do it like this:
:conditions => ["webeeh_project_id = ?", project_id]
if you have a Project model, you should rename the webeeh_project_id column from your Webeeh model into project_id and have an association in your Project model like has_many :webeehs
Then, you won't need to call that find anymore, just do a p = Project.find(id) and then p.webeehs will return the webeehs you need.
the result will be an array which you can iterate through. And to get your webeeh.webeeh_date member, just call it like this:
result.each do |webeeh|
date = webeeh.webeeh_date
end
webeehs_result = Webeeh.findwebeeh_dates
is enough to get all columnn values.
For a different method and performance issues check the following: http://www.stopdropandrew.com/2010/01/28/finding-ids-fast-with-active-record.html
webeeh_result will usually be an array of results for the database.
You can iterate throughit using
webeehs_result.each do |webeeh|
# use "webeeh.webeeh_date" to access the column_name or do whatever you want with it.
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

Resources