module Redis::Search
nodoc
Constants
- DOT
- VERSION
Attributes
config[RW]
indexed_models[RW]
Public Class Methods
complete(type, w, options = {})
click to toggle source
Use for short title search, this method is search by chars, for example Tag, User, Category …
h3. params:
type model name w search char :limit result limit :order result order
h3. usage:
-
Redis::Search.complete
(“Tag”,“r”) => [“Ruby”,“Rails”, “REST”, “Redis”, “Redmine”] -
Redis::Search.complete
(“Tag”,“re”) => [“Redis”, “Redmine”] -
Redis::Search.complete
(“Tag”,“red”) => [“Redis”, “Redmine”] -
Redis::Search.complete
(“Tag”,“redi”) => [“Redis”]
# File lib/redis-search/finder.rb, line 19 def complete(type, w, options = {}) limit = options[:limit] || 10 conditions = options[:conditions] || [] order = options[:order] || 'desc' return [] if (w.blank? && conditions.blank?) || type.blank? prefix_matchs = [] # This is not random, try to get replies < MTU size rangelen = config.complete_max_length prefix = w.downcase key = mk_complete_key(type) if start = config.redis.zrank(key, prefix) count = limit max_range = start + (rangelen * limit) - 1 range = config.redis.zrange(key, start, max_range) while prefix_matchs.length <= count start += rangelen break if !range || range.empty? range.each do |entry| minlen = [entry.length, prefix.length].min if entry[0...minlen] != prefix[0...minlen] count = prefix_matchs.count break end if entry[-1..-1] == '*' && prefix_matchs.length != count prefix_matchs << entry[0...-1] end end range = range[start..max_range] end end prefix_matchs.uniq! # 组合 words 的特别 key 名 words = prefix_matchs.collect { |w| mk_sets_key(type, w) } # 组合特别 key ,但这里不会像 query 那样放入 words, 因为在 complete 里面 words 是用 union 取的,condition_keys 和 words 应该取交集 condition_keys = [] unless conditions.blank? conditions = conditions[0] if conditions.is_a?(Array) conditions.keys.each do |c| condition_keys << mk_condition_key(type, c, conditions[c]) end end # 按词语搜索 temp_store_key = "tmpsunionstore:#{words.join('+')}" if words.length > 1 unless config.redis.exists(temp_store_key) # 将多个词语组合对比,得到并集,并存入临时区域 config.redis.sunionstore(temp_store_key, *words) # 将临时搜索设为1天后自动清除 config.redis.expire(temp_store_key, 86_400) end # 根据需要的数量取出 ids else temp_store_key = words.first end # 如果有条件,这里再次组合一下 unless condition_keys.blank? condition_keys << temp_store_key unless words.blank? temp_store_key = "tmpsinterstore:#{condition_keys.join('+')}" unless config.redis.exists(temp_store_key) config.redis.sinterstore(temp_store_key, *condition_keys) config.redis.expire(temp_store_key, 86_400) end end ids = config.redis.sort(temp_store_key, limit: [0, limit], by: mk_score_key(type, '*'), order: order) return [] if ids.blank? hmget(type, ids) end
Also aliased as: query
configure() { |config ||= config| ... }
click to toggle source
# File lib/redis-search/config.rb, line 6 def configure yield @config ||= Config.new end
Private Class Methods
hmget(type, ids, options = {})
click to toggle source
# File lib/redis-search/finder.rb, line 142 def self.hmget(type, ids, options = {}) result = [] return result if ids.blank? config.redis.hmget(type, *ids).each do |r| begin result << JSON.parse(r) unless r.blank? rescue => e warn("Search.query failed: #{e}") end end result end
info(msg)
click to toggle source
# File lib/redis-search/finder.rb, line 115 def self.info(msg) return unless Redis::Search.config.debug msg = "\e[32m[redis-search] #{msg}\e[0m" if defined?(Rails) == 'constant' && Rails.class == Class ::Rails.logger.debug(msg) else puts msg end end
mk_complete_key(type)
click to toggle source
# File lib/redis-search/finder.rb, line 138 def self.mk_complete_key(type) "Compl#{type}" end
mk_condition_key(type, field, id)
click to toggle source
# File lib/redis-search/finder.rb, line 134 def self.mk_condition_key(type, field, id) "#{type}:_by:_#{field}:#{id}" end
mk_score_key(type, id)
click to toggle source
# File lib/redis-search/finder.rb, line 130 def self.mk_score_key(type, id) "#{type}:_score_:#{id}" end
mk_sets_key(type, key)
click to toggle source
生成 uuid,用于作为 hashes 的 field, sets 关键词的值
# File lib/redis-search/finder.rb, line 126 def self.mk_sets_key(type, key) "#{type}:#{key.downcase}" end
warn(msg)
click to toggle source
# File lib/redis-search/finder.rb, line 105 def self.warn(msg) return unless Redis::Search.config.debug msg = "\e[33m[redis-search] #{msg}\e[0m" if defined?(Rails) == 'constant' && Rails.class == Class ::Rails.logger.warn(msg) else puts msg end end
Public Instance Methods
redis_search_alias_value(field)
click to toggle source
# File lib/redis-search/base.rb, line 26 def redis_search_alias_value(field) return [] if field.blank? || field == '_was'.freeze val = (instance_eval("self.#{field}") || ''.freeze).clone return [] unless val.class.in?([String, Array]) val = val.to_s.split(',') if val.is_a?(String) val end
redis_search_fields_to_hash(ext_fields)
click to toggle source
# File lib/redis-search/base.rb, line 18 def redis_search_fields_to_hash(ext_fields) exts = {} ext_fields.each do |f| exts[f] = instance_eval(f.to_s) end exts end
redis_search_index_after_save()
click to toggle source
# File lib/redis-search/base.rb, line 106 def redis_search_index_after_save if redis_search_index_need_reindex || new_record? redis_search_index_create end true end
redis_search_index_after_update()
click to toggle source
# File lib/redis-search/base.rb, line 96 def redis_search_index_after_update if redis_search_index_need_reindex titles = redis_search_alias_value("#{redis_search_options[:alias_field]}_was") titles << send("#{redis_search_options[:title_field]}_was") redis_search_index_delete(titles) end true end
redis_search_index_before_destroy()
click to toggle source
# File lib/redis-search/base.rb, line 60 def redis_search_index_before_destroy titles = redis_search_alias_value(redis_search_options[:alias_field]) titles << send(redis_search_options[:title_field]) redis_search_index_delete(titles) true end
redis_search_index_create()
click to toggle source
Rebuild search index with create
# File lib/redis-search/base.rb, line 35 def redis_search_index_create opts = { title: send(redis_search_options[:title_field]), aliases: redis_search_alias_value(redis_search_options[:alias_field]), id: id, exts: redis_search_fields_to_hash(redis_search_options[:ext_fields]), type: redis_search_options[:class_name] || self.class.name, condition_fields: redis_search_options[:condition_fields], score: send(redis_search_options[:score_field]).to_i } s = Search::Index.new(opts) s.save true end
redis_search_index_delete(titles)
click to toggle source
# File lib/redis-search/base.rb, line 51 def redis_search_index_delete(titles) titles.uniq! titles.each do |title| next if title.blank? Search::Index.remove(id: id, title: title, type: self.class.name) end true end
redis_search_index_need_reindex()
click to toggle source
# File lib/redis-search/base.rb, line 68 def redis_search_index_need_reindex index_fields_changed = false redis_search_options[:ext_fields].each do |f| next if f.to_s == 'id'.freeze field_method = "#{f}_changed?" if methods.index(field_method.to_sym).nil? Redis::Search.warn("#{self.class.name} model reindex on update need #{field_method} method.") next end index_fields_changed = true if instance_eval(field_method) end begin if send("#{redis_search_options[:title_field]}_changed?") index_fields_changed = true end if send(redis_search_options[:alias_field]) || send("#{redis_search_options[:title_field]}_changed?") index_fields_changed = true end rescue end index_fields_changed end