Using HStore to store your tags in Rails 3

Given the hundreds of ways to store tags in a database, one of the original and oldest is to create a one-to-many relation with a possible join with ActiveRecord and another is a way that I haven’t ever seen before that I like to think I came up with, it uses hstore to reduce the need for an extra query in some cases.

Why?

By using hstore you require less queries, 2 less than ActiveRecord::Model’s and you still in theory have the same amount of duplication with less required space (because now you have 2 less tables and less rows.) I have not tested the theory on space, but the queries is what intrigues me the most about using hstore for tags. Now instead of needing relations you should create a serializer and a method on the singleton.

The code.

class Post < ActiveRecord::Base
  serialize :tags, Tags

  class Tags
    class << self

      # --
      # Load the serialized data.
      # @return [Array]
      # --
      def load(data)
        return [] if data.blank?
        data = data.split(%r!"([^"]+)"[^"]+!).reject { |v| v.blank? }
        data.map do |v|
          v =~ /^\d+$/ ? v.to_i : v
        end
      end

      # --
      # Dup the serialized data.
      # @return [true,false]
      # --
      def dump(data)
        return false unless data.is_a?(Array)
        data.inject([]) { |a, t| a << "\"#{t.to_slug}\" => NULL" } \
          .join(",\s")
      end
    end
  end

  class << self

    # --
    # Find by the tags you decide.
    # @return [ActiveRecord]
    # --
    def find_by_tags(*tags)
      limit(1).find_all_by_tags(*tags).first
    end

    # --
    # Return all the recrods that have the tag.
    # @return [ActiveRecord]
    # --
    def find_all_by_tags(*tags)
      raise ArgumentError, "tag required" if tags.count < 1
      where(tags.inject("") do |s, t|
        s += " AND tags ? #{connection.quote(
          t
        )}"
      end)
    end
  end
end

# --
# Post.new(tags: [:test1, :test2])
# Post.find_by_tag(:test1).tags => [
#   :test1
# ]
# --