class RedisAutocomplete

Constants

DEFAULT_CASE_SENSITIVITY
DEFAULT_DISALLOWED_CHARS
DEFAULT_TERMINAL

Attributes

redis[R]
terminal[R]

Public Class Methods

new(opts = {}) click to toggle source
# File lib/redis_autocomplete.rb, line 10
def initialize(opts = {})
  @set_name = opts[:set_name] # optional
  @redis = Redis.new
  @disallowed_chars = opts[:disallowed_chars] || DEFAULT_DISALLOWED_CHARS
  @terminal = opts[:terminal] || DEFAULT_TERMINAL
  @case_sensitive = opts[:case_sensitive].nil? ? DEFAULT_CASE_SENSITIVITY : opts[:case_sensitive]
end

Public Instance Methods

add_word(word, set_name = nil) click to toggle source
# File lib/redis_autocomplete.rb, line 18
def add_word(word, set_name = nil)
  set_name ||= @set_name
  w = word.gsub(@disallowed_chars, '')
  w.downcase! if !@case_sensitive
  (1..(w.length)).each { |i| @redis.zadd(set_name, 0, w.slice(0, i)) }
  @redis.zadd(set_name, 0, "#{w}#{@terminal}")
end
add_words(words, set_name = nil) click to toggle source
# File lib/redis_autocomplete.rb, line 26
def add_words(words, set_name = nil)
  words.flatten.compact.uniq.each { |word| add_word word, set_name }
end
remove_word(word, set_name = nil, remove_stems = true) click to toggle source
# File lib/redis_autocomplete.rb, line 30
def remove_word(word, set_name = nil, remove_stems = true)
  set_name ||= @set_name
  @redis.zrem(set_name, "#{word}#{@terminal}")
  # remove_word_stem is inefficient and is best done later on with a cron job
  remove_word_stem(word, set_name) if remove_stems
end
suggest(prefix, count = 10, set_name = nil) click to toggle source
# File lib/redis_autocomplete.rb, line 45
def suggest(prefix, count = 10, set_name = nil)
  set_name ||= @set_name
  results = []
  rangelen = 50 # This is not random, try to get replies < MTU size
  start = @redis.zrank(set_name, prefix)
  return [] if !start
  while results.length != count
    range = @redis.zrange(set_name, start, start+rangelen-1)
    start += rangelen
    break if !range || range.length == 0
    range.each do |entry|
      minlen = [entry.length, prefix.length].min
      if entry.slice(0, minlen) != prefix.slice(0, minlen)
        count = results.count
        break
      end
      if entry[-1] == @terminal and results.length != count
        results << entry.chomp(@terminal)
      end
    end
  end
  return results
end

Protected Instance Methods

remove_word_stem(stem, set_name) click to toggle source
# File lib/redis_autocomplete.rb, line 37
def remove_word_stem(stem, set_name)
  if suggest(stem, 1, set_name).empty?
    @redis.zrem(set_name, stem)
    remove_word_stem(stem[0...-1], set_name)
  end    
end