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

Defined Under Namespace

Modules: ClassMethods Classes: Config, Index, Railtie

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configObject

Returns the value of attribute config.



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

def config
  @config
end

.indexed_modelsObject

Returns the value of attribute indexed_models.



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

def indexed_models
  @indexed_models
end

Class Method Details

.complete(type, w, options = {}) ⇒ Object

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

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



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
97
# File 'lib/redis/search/finder.rb', line 21

def complete(type, w, options = {})
  limit = options[:limit] || 10
  conditions = options[:conditions] || []
  return [] if (w.blank? && conditions.blank?) || type.blank?

  prefix_matchs = []
  # This is not random, try to get replies < MTU size
  rangelen = self.config.complete_max_length
  prefix = w.downcase
  key = self.mk_complete_key(type)

  if start = self.config.redis.zrank(key,prefix)
    count = limit
    max_range = start + (rangelen * limit) - 1
    range = self.config.redis.zrange(key,start,max_range)
    while prefix_matchs.length <= count
      start += rangelen
      break if !range || range.length == 0
      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| self.mk_sets_key(type,w) }

  # 组合特别 key ,但这里不会像 query 那样放入 words, 因为在 complete 里面 words 是用 union 取的,condition_keys 和 words 应该取交集
  condition_keys = []
  if !conditions.blank?
    conditions = conditions[0] if conditions.is_a?(Array)
    conditions.keys.each do |c|
      condition_keys << self.mk_condition_key(type,c,conditions[c])
    end
  end

  # 按词语搜索
  temp_store_key = "tmpsunionstore:#{words.join("+")}"
  if words.length > 1
    if !self.config.redis.exists(temp_store_key)
      # 将多个词语组合对比,得到并集,并存入临时区域
      self.config.redis.sunionstore(temp_store_key,*words)
      # 将临时搜索设为1天后自动清除
      self.config.redis.expire(temp_store_key,86400)
    end
    # 根据需要的数量取出 ids
  else
    temp_store_key = words.first
  end

  # 如果有条件,这里再次组合一下
  if !condition_keys.blank?
    condition_keys << temp_store_key if !words.blank?
    temp_store_key = "tmpsinterstore:#{condition_keys.join('+')}"
    if !self.config.redis.exists(temp_store_key)
      self.config.redis.sinterstore(temp_store_key,*condition_keys)
      self.config.redis.expire(temp_store_key,86400)
    end
  end

  ids = self.config.redis.sort(temp_store_key,
                              :limit => [0,limit],
                              :by => self.mk_score_key(type,"*"),
                              :order => "desc")
  return [] if ids.blank?
  self.hmget(type,ids)
end

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

Yields:



7
8
9
10
11
12
13
14
15
# File 'lib/redis/search/config.rb', line 7

def configure
  yield @config ||= Config.new

  if not @config.disable_rmmseg
    require "rmmseg"
    # loading RMMSeg chinese word dicts.
    RMMSeg::Dictionary.load_dictionaries
  end
end

.query(type, text, options = {}) ⇒ Object

Search items, this will split words by Libmmseg

h3. params:

type      model name
text         search text
:limit    result limit

h3. usage:

  • Redis::Search.query(“Tag”,“Ruby vs Python”)



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/redis/search/finder.rb', line 107

def query(type, text, options = {})
  tm = Time.now
  result = []

  limit = options[:limit] || 10
  sort_field = options[:sort_field] || "id"
  conditions = options[:conditions] || []

  # 如果搜索文本和查询条件均没有,那就直接返回 []
  return result if text.strip.blank? && conditions.blank?

  words = self.split(text)
  words = words.collect { |w| self.mk_sets_key(type,w) }

  condition_keys = []
  if !conditions.blank?
    conditions = conditions[0] if conditions.is_a?(Array)
    conditions.keys.each do |c|
      condition_keys << self.mk_condition_key(type,c,conditions[c])
    end
    # 将条件的 key 放入关键词搜索集合内,用于 sinterstore 搜索
    words += condition_keys
  end

  return result if words.blank?

  temp_store_key = "tmpinterstore:#{words.join("+")}"

  if words.length > 1
    if !self.config.redis.exists(temp_store_key)
      self.config.redis.pipelined do
        # 将多个词语组合对比,得到交集,并存入临时区域
        self.config.redis.sinterstore(temp_store_key,*words)
        # 将临时搜索设为1天后自动清除
        self.config.redis.expire(temp_store_key,86400)

        # 拼音搜索
        if self.config.pinyin_match
          pinyin_words = self.split_pinyin(text)
          pinyin_words = pinyin_words.collect { |w| self.mk_sets_key(type,w) }
          pinyin_words += condition_keys
          temp_sunion_key = "tmpsunionstore:#{words.join("+")}"
          temp_pinyin_store_key = "tmpinterstore:#{pinyin_words.join("+")}"
          # 找出拼音的
          self.config.redis.sinterstore(temp_pinyin_store_key,*pinyin_words)
          # 合并中文和拼音的搜索结果
          self.config.redis.sunionstore(temp_sunion_key,*[temp_store_key,temp_pinyin_store_key])
          # 将临时搜索设为1天后自动清除
          self.config.redis.expire(temp_pinyin_store_key,86400)
          self.config.redis.expire(temp_sunion_key,86400)
        end
        temp_store_key = temp_sunion_key
      end
    end
  else
    temp_store_key = words.first
  end

  # 根据需要的数量取出 ids
  ids = self.config.redis.sort(temp_store_key,
                              :limit => [0,limit],
                              :by => self.mk_score_key(type,"*"),
                              :order => "desc")
  result = self.hmget(type,ids, :sort_field => sort_field)
  self.info("{#{type} : \"#{text}\"} | Time spend: #{Time.now - tm}s")
  result
end

.split(text) ⇒ Object

use rmmseg to split words



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

def split(text)
  _split(text)
end