开发者

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)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜