how to find the most popular tags

As a faithful follower of the growing up article, you now have a pretty nice custom tagging system for your app. But how do you find which tags are the most popular? After all, your tag cloud is waiting!

late for the sky

Here’s a find_popular class method for you:

class Tag < ActiveRecord::Base
  # [snip]
  def self.find_popular(args = {})
    find(:all, :select => 'tags.*, count(*) as popularity',
      :limit => args[:limit] || 10,
      :joins => "JOIN taggings ON taggings.tag_id = tags.id",
      :conditions => args[:conditions],
      :group => "taggings.tag_id",
      :order => "popularity DESC"  )
  end
end

usage note

Note that because of the count(*) as popularity clause, the returned tags will have an extra field with the number of tagged objects:

>> Tag.find_popular[0]
=> #<Tag:0x3790d18 @attributes={
  "name"=>"love",
  "id"=>"2",
  "popularity"=>"3"}
>> _.popularity
=> "3"

Pretty neat.

conclusion

Implementing this will become easier once I add custom finders to the polymorphic collection, but for now, it’s not so painful to just drop to SQL.

Thanks to defunkt for working some of this out a while back and to dbii for bugging me about an explanation.

10 responses

  1. Hi! How would I find related tags for some model?

    For example:

    >> Place.related_tags([:food, :bar])
    => [<Tag:restaurant>, <Tag:beer>, ...]
    

    Ordered by count and limited to 50. For tag cloud, of course.

  2. I am not totally sure what you mean, but something like:

    >> r = Recipe.find(:first)
    >> r.tags.map(&:taggables).flatten.uniq.map(&:tags
      ).flatten.inject(Hash.new(0)) do |hist,x|
        hist[x] += 1
        hist
      end.sort_by(&:last).map(&:first).reverse[0..50]
    

    will give you the most popular tags (taken as a whole) of other items that are tagged with the same tags as your initial item. However, figuring out how to do it in SQL so that it runs efficiently will require some work. Let’s both think about it for a while.

  3. I’m interested in a tag cloud too.

    Any clues as to how I should filter by object class, as in:

    Tag.find_popular(:limit => 50,
      :order => "name").restaurants
    

    PS. Interested in the genome stuff to. Not to pry into early findings, but is your work more likely to cure cancer… or invent a liger?

  4. And THAT doesn’t make any SENSE.

    Oh. Never mind. Upon reflection, I’ve found that the Juixe tags-by-model is compatible.

    Cool. Keep up the good work!

  5. Hey, do you have any tips on finding tags that are related to a set of given tags?

    It’s about the only thing that I can’t see how to recreate after my move from the old acts_as_taggable gem, which has a method like:

    find_related_tags(tags, options = {})
    

    Or is this a horribly inefficient thing to try to do?

  6. The whole setup is pretty different since it uses a join table for each tag type and model rather than polymorphic relationships. Here’s the rdoc comments and the method that does the work, though.

  7. I too would like to know how to find related tags. This is something I’ve Googled for and can’t seem to find at all. Unfortunately, a good chunk of intended functionality for my app is relying on this, I just don’t have the wits to find out on my own.

    The basic idea is to find objects that have a given tag and then count their other tags, which I would think would be a complex join along the lines of: finding a Tag, using that Tag’s id to find Taggings, then using those related taggable_id’s to count up any tag_id’s that are not the original Tag’s id. Can this even be done in one query?