Module: Redis::Search

Extended by:
ActiveSupport::Concern
Defined in:
lib/redis-search/base.rb,
lib/redis-search/index.rb,
lib/redis-search/config.rb,
lib/redis-search/finder.rb,
lib/redis-search/railtie.rb,
lib/redis-search/version.rb

Overview

nodoc

Defined Under Namespace

Modules: ClassMethods Classes: Config, Index, Railtie

Constant Summary collapse

DOT =
'.'.freeze
VERSION =
'1.0.3'

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.configObject

Returns the value of attribute config.



4
5
6
# File 'lib/redis-search/config.rb', line 4

def config
  @config
end

.indexed_modelsObject

Returns the value of attribute indexed_models.



4
5
6
# File 'lib/redis-search/config.rb', line 4

def indexed_models
  @indexed_models
end

Class Method Details

.complete(type, w, options = {}) ⇒ Object Also known as: query

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”]



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 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

.configure {|@config ||= Config.new| ... } ⇒ Object

Yields:



6
7
8
# File 'lib/redis-search/config.rb', line 6

def configure
  yield @config ||= Config.new
end

Instance Method Details

#redis_search_alias_value(field) ⇒ Object



26
27
28
29
30
31
32
# 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) ⇒ Object



18
19
20
21
22
23
24
# 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_saveObject



106
107
108
109
110
111
# 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_updateObject



96
97
98
99
100
101
102
103
104
# 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_destroyObject



60
61
62
63
64
65
66
# 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_createObject

Rebuild search index with create



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 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) ⇒ Object



51
52
53
54
55
56
57
58
# 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_reindexObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# 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