How do a search a table for similar records and displaying count - Ruby on Rails
I have created a table in my Ruby on Rails application that I am building called Tags. It is a blog application so I allow the user to associate tags with a post and do this through a :posts, :has_many => tags and Tag belongs_to :post association.
Now that I have my Tags table I am trying to see how I would render the view such that it displays the tag and tag count. (it should be noted that I am trying to render this in the /views/posts/index.html.erb file).
For instance, if there are 10 entries in the Tag table for tag_name Sports. How can I display Sports (10) in the view. I am not looking to do this for a specific tag but rather, somehow search the table, combine like tags and display a list of all tags with counts next to them. (I really want these to be a link to a list of posts that contain that tag but I learned early on only to ask one question at a time).
Hope that makes sense.
UPDATE FOR COMMENTS
view
<% @tag_counts.each do |tag_name, tag_count| %>
<tr>
开发者_如何学JAVA <td><%= link_to(tag_name, posts_path(:tag_name => tag_name)) %> </td>
<td>(<%=tag_count%>)</td>
</tr>
<% end %>
postsController:
def index
@tag_counts = Tag.count(:group => :tag_name)
@posts = Post.all :order => "created_at DESC"
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @posts }
format.json { render :json => @posts }
format.atom
end
end
Do the following:
Tag.count(:group => :name).each do |tag_name, tag_count|
puts "tag_name=#{tag_name}, tag_count=#{tag_count}"
end
You might improve the performance if you add an index on thename
column in the tags
table.
To display the posts associated with a tag name do the following:
In the controller method set the count hash:
Set @tag_counts
in the controller action associated with the view displaying the tag names.
@tag_counts = Tag.count(:group => :tag_name)
In the view show each tag as a link:
<% @tag_counts.each do |tag_name, tag_count| %>
<%= link_to(tag_name, posts_path(:tag_name => tag_name)) %> (<%=tag_count%>)
<% end %>
The link points to the index method of your PostsController
. Each link has a tag_name
parameter.
In the index
method of PostsController
:
class PostsController < ApplicationController
def index
@tag_counts = Tag.count(:group => :tag_name)
conditions, joins = {}, nil
unless (params[:tag_name] || "").empty?
conditions = ["tags.tag_name = ? ", params[:tag_name]]
joins = :tags
end
@posts=Post.all(:joins => joins, :conditions => conditions)
end
end
Edit
Updated the code to change the name
field to tag_name
.
First -- you appear to have your associations wrong. Tags and Posts are a many-to-many association, not one-to-many.
I strongly suggest looking at one of the acts_as_taggable implementations to do this for you. Otherwise you can create a has_many :through
association if you wish, but this would be reinventing the wheel.
Calling count()
on a collection for every page load is a very bad idea, because that will make you hit the database for every tag; a very expensive operation at scale. Also, the number of Posts with a given Tag is not a figure you need to calculate at request time, so this approach is both expensive and unnecessary.
Rails has a built-in feature called counter_cache
that will take care of this for you, by caching the number of related records in an integer field and updating it whenever new records are created. Here's how you set it up:
Create a migration for for the tags
table:
def up
add_column :tags, :taggings_count, :integer, :default => 0
Tag.reset_column_information
Tag.all.each do |t|
Tag.update_counters t.id, :taggings_count => t.taggings.length
end
end
def down
remove_column :tags, :taggings_count
end
And change the belongs_to
association:
class Tagging < ActionRecord::Base
belongs_to :tag, :counter_cache => true
belongs_to :post
end
If you're building your own Tagging system you'd wire up the remaining two models like so:
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings
end
But, again, it'd be much easier to use a pre-built solution.
From now on, every time you create a new Post
association to a Tag
through the Tagging
model, it will update the taggings_count
column for you automatically.
In your views, you can display the count while iterating like any other column:
<%= link_to "#{tag.name} (#{tag.taggings_count})", posts_path(:tag_name => tag.name) %>
Some further reading:
ActiveRecord has_many :through associations
ActiveRecord association basics (:counter_cache discussed in section 4.1.2.4)
精彩评论