Ruby on Rails ActiveRecord: optimizing increment by one - ruby-on-rails

I have an entry ID, which popularity has to be increased by one.
A simple solution looks like this:
Tag.find(id).increment!(:popularity)
However it doesn't seem to be very efficient, because I select the entire entry (*) from the database (even though I don't need it at all) and then do the second query to update it.
Is there a more efficient way to do this? I think, one update statement (without "select") should be enough, but how do I write this?

Tag.increment_counter :popularity, id
Not only does this skip the select, but it increments atomically.

Maybe something like :
Tag.update_all("popularity = popularity + 1", {:id => id})
Or
Tag.where(:id => id).update_all("popularity = popularity + 1")
?

Related

activerecord: check first X chars of column

I'd like to check the first two chars of a number straight in my model. First I define the number of the current logged in user (devise):
user_number = current_user.number.first(2)
then I want to take that value and check it within a where statement in a "number" mobel, so I tried this
#numbers = Number.where(:number_value.first(2) => user_number)
which is obviously the same as
#numbers = Number.where(:number_value.first(2) => current_user.number.first(2))
No, that does not work.
How can I check the first 2 chars of the :number_value column in my model?
Any help is appreciated.
Many thanks.
Solution (SQLite)
#numbers = Number.where("number_value like '" + current_user.number.first(2) + "%'")
since this is not lazy loading I'm not convinced yet that it is the smartest solution. if you know any better, would be cool if you can share
First, you should read the ActiveRecord query guide. I'd also imagine that there's a much more straight forward way for you to accomplish your goal.
But, to answer your specific question, here's an approach that'd work with Postgresql.
Number.where("number_value::text like ?", current_user.number.to_s[0,2] + "%")

What is the fastest way to find the first record in Rails (ActiveRecord)?

I would like to know which method is fastest for return a record.
Class.where(:type => 4).first
Class.find(:first, :conditions => ["type = ?", 4])
Is the execution exactly the same?
The most performant, assuming you don't care about the order, is to use find_by:
Class.find_by(type: 4)
From https://github.com/rubocop-hq/rails-style-guide/issues/76
This method has been added on Rails 4 and it is defined like this:
def find_by(*args)
where(*args).take
end
So, take differs from first in regards to the order of your records. first will return the first record according to the order of the primary key while take will just return whatever the database spits out first.
So while using where().take is equivalent to find_by and choosing whether to use one of the other is a matter of taste, where().first differs from find_by in a subtle and not so obvious way.
Both would produce the same query.
According to the latest information, under Rails 3.1, passing in :conditions will be deprecated.
Hence, right now, the best way to execute the query is to use:
Class.where(:type => 4).first

Rails 3.2 Query - .exists?

I have about 500 outlets. Each outlet will be monitored a minimum of one time per day. I am trying to get a list of outlets that have been monitored each day.
I am having a problem with the query at the moment, any help is appreciated:
<% for outlet in #outlets %>
<% if Monitoring.exists?( :outlet_id => outlet.id, 'DATE(created_at) = ?', Date.today ) %>
The #outlets is an instance variable containing Outlet.all.
This query leaves me with a syntax error. What would be the correct way to do this? I'm trying to check that the Monitoring belongs to the Outlet, and that the Monitoring record was created today.
Also, I'm not entirely sure of the speed implications of this query. There will be a max of 2000 outlets on a page at one time (it's a dashboard, so they appear as either red or green dots).
Any help greatly appreciated.
You're getting a syntax error because you're trying to mix implicit-Hash and implicit-Array arguments:
Monitoring.exists?(:outlet_id => outlet.id, 'DATE(created_at) = ?', Date.today)
The exists? methods wants a Hash as its single argument. You want to use an SQL function in the query though, that means that you have to use the Model.where(...).exists? form:
Monitoring.where(:outlet_id => outlet.id).where('date(created_at) = ?', Date.today).exists?
That still leaves you hitting the database over and over again to light up your lights. You could precompute the whole mess with something like this:
counts = Monitoring.where('date(created_at) = ?', Date.today).count(:group => :outlet_id)
And then look use counts.has_key? outlet.id in your loop. Adding a where(:outlet_id => outlet_ids) (where outlet_ids are the IDs you're interested in) might make sense as well. You might be able to combine the count query with the query that is generating the #outlets too.

ActiveRecord find and only return selected columns

edit 2
If you stumble across this, check both answers as I'd now use pluck for this
I have a fairly large custom dataset that I'd like to return to be echoe'd out as json. One part is:
l=Location.find(row.id)
tmp[row.id]=l
but I'd like to do something like:
l=Location.find(row.id).select("name, website, city")
tmp[row.id]=l
but this doesn't seem to be working. How would I get this to work?
thx
edit 1
alternatively, is there a way that I can pass an array of only the attributes I want included?
pluck(column_name)
This method is designed to perform select by a single column as direct SQL query Returns Array with values of the specified column name The values has same data type as column.
Examples:
Person.pluck(:id) # SELECT people.id FROM people
Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
Person.where(:confirmed => true).limit(5).pluck(:id)
see http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-pluck
Its introduced rails 3.2 onwards and accepts only single column. In rails 4, it accepts multiple columns
In Rails 2
l = Location.find(:id => id, :select => "name, website, city", :limit => 1)
...or...
l = Location.find_by_sql(:conditions => ["SELECT name, website, city FROM locations WHERE id = ? LIMIT 1", id])
This reference doc gives you the entire list of options you can use with .find, including how to limit by number, id, or any other arbitrary column/constraint.
In Rails 3 w/ActiveRecord Query Interface
l = Location.where(["id = ?", id]).select("name, website, city").first
Ref: Active Record Query Interface
You can also swap the order of these chained calls, doing .select(...).where(...).first - all these calls do is construct the SQL query and then send it off.
My answer comes quite late because I'm a pretty new developer. This is what you can do:
Location.select(:name, :website, :city).find(row.id)
Btw, this is Rails 4

How can I combine record results in Rails?

So I have two separate queries:
tagged_items = Item.tagged_with(params[:s], :on => :tags)
searched_items = Item.find(:all, :conditions => ["MATCH(title) AGAINST (? IN BOOLEAN MODE)", "*#{params[:s]}*"])
The first tagged_items is using the acts_as_taggable_on plugin to find all the items tagged with XYZ.
The second, searched_items, is used to search the items table for the search term.
So, how could I combine (and avoid duplicates) the results of these two?
Check out named_scope. The second query can be converted to named_scope easily, I'm not sure about the first one, but if you can rewrite it using find, you're home.
http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html
items = (tagged_items + searched_items).unique
But it would be much better if you could fetch them with single query.
This approach...
#items = tagged_items | searched_items
...would make more sense if you're looking to use the results of these queries in a View, instead of working with an Array, and accomplishes the de-duplication as well.

Resources