#399 Autocomplete Search Terms pro
Dec 31, 2012 | 17 minutes | Performance, Caching
Learn how to add autocompletion to a search form and improve performance using Rack middleware, caching and switching from SQL to Redis.
- Download:
- source codeProject Files in Zip (93.2 KB)
- mp4Full Size H.264 Video (31 MB)
- m4vSmaller H.264 Video (17.6 MB)
- webmFull Size VP8 Video (21.5 MB)
- ogvFull Size Theora Video (38.3 MB)
Resources
- jQuery UI Autocomplete
- Redis
- Soulmate
- Episode 102: Auto-Complete Association (revised)
- Episode 390: Turbolinks
- Episode 368: MiniProfiler
- Episode 150: Rails Metal (revised)
- Episode 317: Rack App from Scratch
- Episode 380: Memcached & Dalli
terminal
rails g resource search_suggestion term popularity:integer rake db:migrate rake search_suggestions:index brew install redis
app/middleware/search_suggestions.rb
class SearchSuggestions def initialize(app) @app = app end def call(env) if env["PATH_INFO"] == "/search_suggestions" request = Rack::Request.new(env) terms = SearchSuggestion.terms_for(request.params["term"]) [200, {"Content-Type" => "appication/json"}, [terms.to_json]] else @app.call(env) end end end
config/application.rb
config.middleware.insert_before 0, "SearchSuggestions"
models/search_suggestion.rb
class SearchSuggestion < ActiveRecord::Base attr_accessible :popularity, :term def self.terms_for(prefix) Rails.cache.fetch(["search-terms", prefix]) do suggestions = where("term like ?", "#{prefix}_%") suggestions.order("popularity desc").limit(10).pluck(:term) end end def self.index_products Product.find_each do |product| index_term(product.name) index_term(product.category) product.name.split.each { |t| index_term(t) } end end def self.index_term(term) where(term: term.downcase).first_or_initialize.tap do |suggestion| suggestion.increment! :popularity end end end
lib/tasks/search_suggestions.rake
namespace :search_suggestions do desc "Generate search suggestions from products" task :index => :environment do SearchSuggestion.index_products end end
Gemfile
gem 'rack-mini-profiler'
Redis Variation
Gemfile
gem 'redis'
config/initialiers/redis.rb
$redis = Redis.new
models/search_suggestion.rb
class SearchSuggestion def self.terms_for(prefix) $redis.zrevrange "search-suggestions:#{prefix.downcase}", 0, 9 end def self.index_products Product.find_each do |product| index_term(product.name) index_term(product.category) product.name.split.each { |t| index_term(t) } end end def self.index_term(term) 1.upto(term.length-1) do |n| prefix = term[0, n] $redis.zincrby "search-suggestions:#{prefix.downcase}", 1, term.downcase end end end