Rails search serialized records - ruby-on-rails

In the model I serialize the column category in Event model to be array, so i have
serialize :category
The thing is, i need to provide search function to users. So how do i search if a category is inside an event.
Event.find(:all, :conditions => ['category = ?', params[:category]])
This won't work as the category is stored as serialized array. Any idea?
The only way i can think of get all the events and filter out each instance.
Event.all.select{|e| e.category.include? params[:category]}
This is not efficient at all.
Or else, i can use like statement
Event.find(:all, :conditions => ['category = ?', "%-#{params[:category]}%"])

I would create a separate table and model for categories. Then you can get all events in a category by using a has_many association.

You can use .to_yaml to match the serialize format stored in the DB.
Event.where(category: params[:category].to_yaml)

Event.where("category IN (?)" , params[:category].to_yaml)

Related

can you force ActiveRecord find to include nil when associated record not found

Is there a way to force :include in ActiveRecord find to output nil in results where conditions are not met?
For example if I have classes: Parent and Children. Parent has many childrens and I do something like this:
children_ids = [1,2,3]
my_parent = Parent.find(:all,
:include => :children,
:conditions => ['parent.id = 1 AND children.id IN (?)', children_ids])
Assuming that I have only childrens with id 2 and 3 statement:
my_parent.children
will return array with two childrens. But I would like to know which one they are (second and third in my children_ids array). So is it possible for :include to input nil for child that I'm missing?
If this description is too confiusing then let me know and I will try to present it better.
If you want to find out what records were missing, you can do it in rubyland by processing retrieved records.
retrieved_ids = my_parent.children.map(&:id)
unretrieved_ids = children_ids - retrieved_ids

Rails: Get only certain attributes of submodel with method 'attributes'

Is there a way to get only certain fields of a foreign model like this:
#user = User.find(:first, :select => ['`users`.`id`, `users`.`nickname`, `users`.`birthdate`, `users`.`sex`'], :conditions => ['`users`.`id` = ?', id])
city = #user.profile.city.attributes
With attributes I retrieve all attributes of my city model. I'd like to get only some. Something like:
city = #user.profile.city.attributes[:name, :postcode]
Is it possible by keeping the syntax as simple as above? I want to use attributes to receive a Hash.
thanks a lot.
You could do this if you don't mind that it picks out fields after the SQL returns everything:
#user.profile.city.attributes.select{|k,v| ["name","postcode"].include?(k)}
It's not possible to select fields of foreign models when chaining in the way you have. The only way would be to do a query on the City model:
City.where(:profile_id => #user.profile.id, :select => ...)
You cannot give arguements after attributes otherwise it will raise ArguementError. In this case you can use inner join to fetch the records.
city = #user.profile.city.pluck(:name, :postcode)

Fetch COUNT(column) as an integer in a query with group by in Rails 3

I have 2 models Category and Article related like this:
class Category < ActiveRecord::Base
has_many :articles
end
class Article < ActiveRecord::Base
belongs_to :category
def self.count_articles_per_category
select('category_id, COUNT(*) AS total').group(:category_id)
end
end
I'm accessing count_articles_per_category like this
Article.count_articles_per_category
which will return articles that have 2 columns: category_id and total.
My problem is that total column is a string. So the question is: is there a method to fetch that column as an integer?
PS: I tried to do a cast in the database for COUNT(*) and that doesn't help.
I try to avoid doing something like this:
articles = Article.count_articles_per_category
articles.map do |article|
article.total = article.total.to_i
article
end
No, there is no support in ActiveRecord to automatically cast datatypes (which are always transferred as strings to the database).
The way ActiveRecord works when retrieving items is:
for each attribute in the ActiveRecord model, check the column type, and cast the data to that type.
for extra columns, it does not know what data type it should cast it to.
Extra columns includes columns from other tables, or expressions.
You can use a different query, like:
Article.group(:category_id).count
Article.count(:group => :category_id)
These return a hash of :category_id => count. So you might get something like {6=>2, 4=>2, 5=>1, 2=>1, 9=>1, 1=>1, 3=>1}.
Using the count method works because it implicitly lets ActiveRecord know that it is an integer type.
Article.group(:category_id).count might give you something you can use. This will return a hash where each key represents the category_id and each value represents the corresponding count as an integer.

Looping thru an array of objects to display content in an erb file

I have a model Comments. Comments has_many votes. While displaying the comments I have to to display the up or down arrow based on the votes given to the comments by the user. So in my erb file I have #comment. #comment.votes has all the votes for that comments irrespective of the user. So i have to loop thru the#comment.votes to check vote.user.id = #comment.user.id. how can i achieve this in an erb file?
One approach would be to try a named_scope/scope on Vote, something like:
named_scope :for_user, lambda do |user_id|
:conditions => ["comments.user_id = (?)", user_id]
end
Which lets you do something like:
#comments.votes.for_user(current_user.id)

Is it possible to delete_all with inner join conditions?

I need to delete a lot of records at once and I need to do so based on a condition in another model that is related by a "belongs_to" relationship. I know I can loop through each checking for the condition, but this takes forever with my large record set because for each "belongs_to" it makes a separate query.
Here is an example. I have a "Product" model that "belongs_to" an "Artist" and lets say that artist has a property "is_disabled".
If I want to delete all products that belong to disabled artists, I would like to be able to do something like:
Product.delete_all(:joins => :artist, :conditions => ["artists.is_disabled = ?", true])
Is this possible? I have done this directly in SQL before, but not sure if it is possible to do through rails.
The problem is that delete_all discards all the join information (and rightly so). What you want to do is capture that as an inner select.
If you're using Rails 3 you can create a scope that will give you what you want:
class Product < ActiveRecord::Base
scope :with_disabled_artist, lambda {
where("product_id IN (#{select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})")
}
end
You query call then becomes
Product.with_disabled_artist.delete_all
You can also use the same query inline but that's not very elegant (or self-documenting):
Product.where("product_id IN (#{Product.select("product_id").joins(:artist).where("artist.is_disabled = TRUE").to_sql})").delete_all
In Rails 4 (I tested on 4.2) you can almost do how OP originally wanted
Application.joins(:vacancy).where(vacancies: {status: 'draft'}).delete_all
will give
DELETE FROM `applications` WHERE `applications`.`id` IN (SELECT id FROM (SELECT `applications`.`id` FROM `applications` INNER JOIN `vacancies` ON `vacancies`.`id` = `applications`.`vacancy_id` WHERE `vacancies`.`status` = 'draft') __active_record_temp)
If you are using Rails 2 you can't do the above. An alternative is to use a joins clause in a find method and call delete on each item.
TellerLocationWidget.find(:all, :joins => [:widget, :teller_location],
:conditions => {:widgets => {:alt_id => params['alt_id']},
:retailer_locations => {:id => #teller_location.id}}).each do |loc|
loc.delete
end

Resources