redis-search 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f06b8b05df240f527992050fa3174ae7726cb0e
4
- data.tar.gz: 1f37d2e08628b4a5fe6a9a5416a88f5c6b1b989d
3
+ metadata.gz: 715f373324e8d649a86d38d05ce25e044b4ebacc
4
+ data.tar.gz: bc32f91272a550a62f697366df1c807d2b7c58c2
5
5
  SHA512:
6
- metadata.gz: ec74e499cf300aeab20a0851c2228f0bacbbae75683d4517f44894bb4c331f83c753ae98c112b342762e59457b9adf36b645728dcf495b890d516a52351871fe
7
- data.tar.gz: c6ea104e95e3d83ba984a18522925dc881eb37fb021750c5b1476b14c8ae90eab70d07b14fe88c56a3bd3e298f3923a8fb83236b52c0a5033370e382be9bf63a
6
+ metadata.gz: dc3272074ff70fd82998bd46d7b566a6072ceb7c3820411e8169386f395cecf98896100934edae3e15e1bf50406a7e3556de1c15f0f32e402deb01d548f882ad
7
+ data.tar.gz: c115f7733556b42db81f707d0a857697e9881cf78b5e7dabbd3b2d67dd3d72d953e84a4b04a98af23190aae3546287abaf9c8902f36c93b0a19ec82d0a5385fe
data/README.md CHANGED
@@ -107,7 +107,7 @@ And there is an [Example App](https://github.com/huacnlee/redis-search-example)
107
107
  field :followers_count
108
108
 
109
109
  redis_search_index(:title_field => :name,
110
- :alias_field => :alias_names,
110
+ :alias_field => :alias_names,
111
111
  :prefix_index_enable => true,
112
112
  :score_field => :followers_count,
113
113
  :ext_fields => [:email,:tagline])
@@ -130,10 +130,32 @@ And there is an [Example App](https://github.com/huacnlee/redis-search-example)
130
130
 
131
131
  ## Index data to Redis
132
132
 
133
- If you are first install it in you old project, or your Redis cache lose, you can use this command to rebuild indices.
133
+ ### Specify Model
134
+
135
+ Redis-Search index data to Redis from your model (pass name as CLASS environment variable).
136
+
137
+ ```bash
138
+ $ rake redis_search:index:model CLASS='MyModel'
139
+ ```
140
+
141
+ Customize the batch size:
134
142
 
135
143
  ```bash
136
- $ rake redis_search:index
144
+ $ rake redis_search:index:model CLASS='MyModel' BATCH=100
145
+ ```
146
+
147
+ ### All Models
148
+
149
+ Redis-Search all index data to Redis from `app/models` (or use DIR environment variabl).
150
+
151
+ ```bash
152
+ $ rake redis_search:index DIR=app/models
153
+ ```
154
+
155
+ Customize the batch size:
156
+
157
+ ```bash
158
+ $ rake redis_search:index DIR=app/models BATCH=100
137
159
  ```
138
160
 
139
161
  ## Documentation
@@ -148,7 +170,6 @@ There is my performance test result.
148
170
 
149
171
  * [https://gist.github.com/1150933](https://gist.github.com/1150933)
150
172
 
151
-
152
173
  ## License
153
174
 
154
175
  * MIT
@@ -1,134 +1,168 @@
1
1
  # coding: utf-8
2
-
3
2
  class Redis
4
3
  module Search
5
4
  autoload :PinYin, 'ruby-pinyin'
6
5
 
7
6
  extend ::ActiveSupport::Concern
8
7
 
8
+ included do
9
+ cattr_reader :redis_search_options
10
+
11
+ before_destroy :redis_search_index_before_destroy
12
+ after_update :redis_search_index_after_update
13
+ after_save :redis_search_index_after_save
14
+ end
15
+
16
+ def redis_search_fields_to_hash(ext_fields)
17
+ exts = {}
18
+ ext_fields.each do |f|
19
+ exts[f] = instance_eval(f.to_s)
20
+ end
21
+ exts
22
+ end
23
+
24
+ def redis_search_alias_value(field)
25
+ return [] if field.blank? || field == "_was".freeze
26
+ val = (instance_eval("self.#{field}") || "".freeze).clone
27
+ return [] if !val.class.in?([String,Array])
28
+ if val.is_a?(String)
29
+ val = val.to_s.split(",")
30
+ end
31
+ val
32
+ end
33
+
34
+ # Rebuild search index with create
35
+ def redis_search_index_create
36
+ s = Search::Index.new(title: self.send(self.redis_search_options[:title_field]),
37
+ aliases: self.redis_search_alias_value(self.redis_search_options[:alias_field]),
38
+ id: self.id,
39
+ exts: self.redis_search_fields_to_hash(self.redis_search_options[:ext_fields]),
40
+ type: self.redis_search_options[:class_name] || self.class.name,
41
+ condition_fields: self.redis_search_options[:condition_fields],
42
+ score: self.send(self.redis_search_options[:score_field]).to_i,
43
+ prefix_index_enable: self.redis_search_options[:prefix_index_enable])
44
+ s.save
45
+ # release s
46
+ s = nil
47
+ true
48
+ end
49
+
50
+ def redis_search_index_delete(titles)
51
+ titles.uniq!
52
+ titles.each do |title|
53
+ next if title.blank?
54
+ Search::Index.remove(id: self.id, title: title, type: self.class.name)
55
+ end
56
+ true
57
+ end
58
+
59
+ def redis_search_index_before_destroy
60
+ titles = []
61
+ titles = redis_search_alias_value(self.redis_search_options[:alias_field])
62
+ titles << self.send(self.redis_search_options[:title_field])
63
+
64
+ redis_search_index_delete(titles)
65
+ true
66
+ end
67
+
68
+ def redis_search_index_need_reindex
69
+ index_fields_changed = false
70
+ self.redis_search_options[:ext_fields].each do |f|
71
+ next if f.to_s == "id".freeze
72
+ field_method = "#{f}_changed?"
73
+ if self.methods.index(field_method.to_sym) == nil
74
+ Redis::Search.warn("#{self.class.name} model reindex on update need #{field_method} method.")
75
+ next
76
+ end
77
+
78
+ index_fields_changed = true if instance_eval(field_method)
79
+ end
80
+
81
+ begin
82
+ if self.send("#{self.redis_search_options[:title_field]}_changed?")
83
+ index_fields_changed = true
84
+ end
85
+
86
+ if self.send(self.redis_search_options[:alias_field]) || self.send("#{self.redis_search_options[:title_field]}_changed?")
87
+ index_fields_changed = true
88
+ end
89
+ rescue
90
+ end
91
+
92
+ return index_fields_changed
93
+ end
94
+
95
+ def redis_search_index_after_update
96
+ if self.redis_search_index_need_reindex
97
+ titles = []
98
+ titles = redis_search_alias_value("#{self.redis_search_options[:alias_field]}_was")
99
+ titles << self.send("#{self.redis_search_options[:title_field]}_was")
100
+ redis_search_index_delete(titles)
101
+ end
102
+
103
+ true
104
+ end
105
+
106
+ def redis_search_index_after_save
107
+ if self.redis_search_index_need_reindex || self.new_record?
108
+ self.redis_search_index_create
109
+ end
110
+ true
111
+ end
112
+
9
113
  module ClassMethods
10
114
  # Config redis-search index for Model
11
115
  # == Params:
12
116
  # title_field Query field for Search
13
- # alias_field Alias field for search, can accept multi field (String or Array type), it type is String, redis-search will split by comma
117
+ # alias_field Alias field for search, can accept multi field (String or Array type) it type is String, redis-search will split by comma
14
118
  # prefix_index_enable Is use prefix index search
15
119
  # ext_fields What kind fields do you need inlucde to search indexes
16
120
  # score_field Give a score for search sort, need Integer value, default is `created_at`
17
- def redis_search_index(options = {})
18
- title_field = options[:title_field] || :title
19
- alias_field = options[:alias_field] || nil
20
- prefix_index_enable = options[:prefix_index_enable] || false
21
- ext_fields = options[:ext_fields] || []
22
- score_field = options[:score_field] || :created_at
23
- condition_fields = options[:condition_fields] || []
121
+ def redis_search_index(opts = {})
122
+ opts[:title_field] ||= :title
123
+ opts[:alias_field] ||= nil
124
+ opts[:prefix_index_enable] ||= false
125
+ opts[:ext_fields] ||= []
126
+ opts[:score_field] ||= :created_at
127
+ opts[:condition_fields] ||= []
128
+ opts[:class_name] ||= nil
24
129
 
25
130
  # Add score field to ext_fields
26
- ext_fields |= [score_field]
27
- # Add condition fields to ext_fields
28
- ext_fields |= condition_fields
131
+ opts[:ext_fields] += [opts[:score_field]]
29
132
 
30
- if RUBY_VERSION.start_with?('1.8')
31
- condition_fields = "[#{condition_fields.collect { |c| "'#{c}'" }.join(',')}]"
32
- end
133
+ # Add condition fields to ext_fields
134
+ opts[:ext_fields] += opts[:condition_fields] if opts[:condition_fields].is_a?(Array)
33
135
 
34
136
  # store Model name to indexed_models for Rake tasks
35
137
  Search.indexed_models = [] if Search.indexed_models == nil
36
138
  Search.indexed_models << self
37
- # bind instance methods and callback events
38
- class_eval %(
39
- def redis_search_fields_to_hash(ext_fields)
40
- exts = {}
41
- ext_fields.each do |f|
42
- exts[f] = instance_eval(f.to_s)
43
- end
44
- exts
45
- end
46
-
47
- def redis_search_alias_value(field)
48
- return [] if field.blank? || field == "_was"
49
- val = (instance_eval("self.\#{field}") || "").clone
50
- return [] if !val.class.in?([String,Array])
51
- if val.is_a?(String)
52
- val = val.to_s.split(",")
53
- end
54
- val
55
- end
56
-
57
- def redis_search_index_create
58
- s = Search::Index.new(:title => self.#{title_field},
59
- :aliases => self.redis_search_alias_value(#{alias_field.inspect}),
60
- :id => self.id,
61
- :exts => self.redis_search_fields_to_hash(#{ext_fields.inspect}),
62
- :type => self.class.to_s,
63
- :condition_fields => #{condition_fields},
64
- :score => self.#{score_field}.to_i,
65
- :prefix_index_enable => #{prefix_index_enable})
66
- s.save
67
- # release s
68
- s = nil
69
- true
70
- end
71
-
72
- def redis_search_index_delete(titles)
73
- titles.uniq.each do |title|
74
- next if title.blank?
75
- Search::Index.remove(:id => self.id, :title => title, :type => self.class.to_s)
76
- end
77
- true
78
- end
79
-
80
- before_destroy do
81
- titles = []
82
- titles = redis_search_alias_value("#{alias_field}")
83
- titles << self.#{title_field}
84
139
 
85
- redis_search_index_delete(titles)
86
- true
87
- end
140
+ class_variable_set("@@redis_search_options".freeze, opts)
141
+ end
88
142
 
89
- def redis_search_index_need_reindex
90
- index_fields_changed = false
91
- #{ext_fields.inspect}.each do |f|
92
- next if f.to_s == "id"
93
- field_method = f.to_s + "_changed?"
94
- if self.methods.index(field_method.to_sym) == nil
95
- Search.warn("#{self.class.name} model reindex on update need "+field_method+" method.")
96
- next
97
- end
98
- if instance_eval(field_method)
99
- index_fields_changed = true
100
- end
143
+ def redis_search_index_batch_create(batch_size = 1000, progressbar = false)
144
+ count = 0
145
+ if self.ancestors.collect { |klass| klass.to_s }.include?("ActiveRecord::Base".freeze)
146
+ find_in_batches(:batch_size => batch_size) do |items|
147
+ items.each do |item|
148
+ item.redis_search_index_create
149
+ count += 1
150
+ print "." if progressbar
101
151
  end
102
- begin
103
- if self.#{title_field}_changed?
104
- index_fields_changed = true
105
- end
106
- if self.#{alias_field || title_field}_changed?
107
- index_fields_changed = true
108
- end
109
- rescue
110
- end
111
- return index_fields_changed
112
152
  end
113
-
114
- after_update do
115
- if self.redis_search_index_need_reindex
116
- titles = []
117
- titles = redis_search_alias_value("#{alias_field}_was")
118
- titles << self.#{title_field}_was
119
- redis_search_index_delete(titles)
153
+ elsif self.included_modules.collect { |m| m.to_s }.include?("Mongoid::Document".freeze)
154
+ all.each_slice(batch_size) do |items|
155
+ items.each do |item|
156
+ item.redis_search_index_create
157
+ count += 1
158
+ print "." if progressbar
120
159
  end
121
- true
122
160
  end
161
+ else
162
+ puts "skiped, not support this ORM in current."
163
+ end
123
164
 
124
- after_save :redis_search_index_update
125
- def redis_search_index_update
126
- if self.redis_search_index_need_reindex || self.new_record?
127
- self.redis_search_index_create
128
- end
129
- true
130
- end
131
- )
165
+ count
132
166
  end
133
167
  end
134
168
  end
@@ -19,7 +19,7 @@ class Redis
19
19
  # * Redis::Search.complete("Tag","red") => ["Redis", "Redmine"]
20
20
  # * Redis::Search.complete("Tag","redi") => ["Redis"]
21
21
  def complete(type, w, options = {})
22
- limit = options[:limit] || 10
22
+ limit = options[:limit] || 10
23
23
  conditions = options[:conditions] || []
24
24
  return [] if (w.blank? && conditions.blank?) || type.blank?
25
25
 
@@ -89,9 +89,9 @@ class Redis
89
89
  end
90
90
 
91
91
  ids = self.config.redis.sort(temp_store_key,
92
- :limit => [0,limit],
93
- :by => self.mk_score_key(type,"*"),
94
- :order => "desc")
92
+ limit: [0,limit],
93
+ by: self.mk_score_key(type,"*"),
94
+ order: "desc")
95
95
  return [] if ids.blank?
96
96
  self.hmget(type,ids)
97
97
  end
@@ -105,10 +105,9 @@ class Redis
105
105
  # h3. usage:
106
106
  # * Redis::Search.query("Tag","Ruby vs Python")
107
107
  def query(type, text, options = {})
108
- tm = Time.now
109
- result = []
110
-
111
- limit = options[:limit] || 10
108
+ tm = Time.now
109
+ result = []
110
+ limit = options[:limit] || 10
112
111
  sort_field = options[:sort_field] || "id"
113
112
  conditions = options[:conditions] || []
114
113
 
@@ -164,10 +163,10 @@ class Redis
164
163
 
165
164
  # 根据需要的数量取出 ids
166
165
  ids = self.config.redis.sort(temp_store_key,
167
- :limit => [0,limit],
168
- :by => self.mk_score_key(type,"*"),
169
- :order => "desc")
170
- result = self.hmget(type,ids, :sort_field => sort_field)
166
+ limit: [0,limit],
167
+ by: self.mk_score_key(type,"*"),
168
+ order: "desc")
169
+ result = self.hmget(type, ids, sort_field: sort_field)
171
170
  self.info("{#{type} : \"#{text}\"} | Time spend: #{Time.now - tm}s")
172
171
  result
173
172
  end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  class Redis
2
3
  module Search
3
4
  class Index
@@ -70,7 +71,7 @@ class Redis
70
71
  return if @title.blank?
71
72
 
72
73
  self.redis.pipelined do
73
- data = {:title => @title, :id => @id, :type => @type}
74
+ data = {title: @title, id: @id, type: @type}
74
75
  self.exts.each do |f|
75
76
  data[f[0]] = f[1]
76
77
  end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  class Redis
2
3
  module Search
3
4
  class Railtie < ::Rails::Railtie
@@ -1,42 +1,73 @@
1
1
  # coding: utf-8
2
2
  require "redis-search"
3
3
  namespace :redis_search do
4
- desc "Redis-Search index data to Redis"
5
- task :index => :environment do
6
- tm = Time.now
7
- count = 0
8
- puts "redis-search index".upcase.rjust(120)
9
- puts "-"*120
10
- puts "Now indexing search to Redis...".rjust(120)
11
- puts ""
12
- Redis::Search.indexed_models.each do |klass|
13
- print "[#{klass.to_s}]"
14
- if klass.superclass.to_s == "ActiveRecord::Base"
15
- klass.find_in_batches(:batch_size => 1000) do |items|
16
- items.each do |item|
17
- item.redis_search_index_create
18
- item = nil
19
- count += 1
20
- print "."
21
- end
22
- end
23
- elsif klass.included_modules.collect { |m| m.to_s }.include?("Mongoid::Document")
24
- klass.all.each_slice(1000) do |items|
25
- items.each do |item|
26
- item.redis_search_index_create
27
- item = nil
28
- count += 1
29
- print "."
30
- end
31
- end
32
- else
33
- puts "skiped, not support this ORM in current."
4
+ task index: 'index:all'
5
+
6
+ namespace :index do
7
+ index_model_desc = <<-DESC.gsub(/ /, '')
8
+ Redis-Search index data to Redis from your model (pass name as CLASS environment variable).
9
+
10
+ $ rake environment redis_search:index:model CLASS='MyModel'
11
+
12
+ Customize the batch size:
13
+
14
+ $ rake environment redis_search:index:model CLASS='Article' BATCH=100
15
+ DESC
16
+
17
+ index_all_desc = <<-DESC.gsub(/ /, '')
18
+ Redis-Search all index data to Redis from `app/models` (or use DIR environment variabl).
19
+
20
+ $ rake environment redis_search:index:all DIR=app/models
21
+
22
+ Customize the batch size:
23
+
24
+ $ rake environment redis_search:index:all DIR=app/models BATCH=100
25
+ DESC
26
+
27
+ desc index_model_desc
28
+ task model: :environment do
29
+ if ENV['CLASS'].to_s == ''
30
+ puts '='*90, 'USAGE', '='*90, index_model_desc, ""
31
+ exit(1)
34
32
  end
33
+
34
+ klass = eval(ENV['CLASS'].to_s)
35
+ batch = ENV['BATCH'].to_i > 0 ? ENV['BATCH'].to_i : 1000
36
+ tm = Time.now
37
+ puts "Redis-Search index data to Redis from [#{klass.to_s}]"
38
+ count = klass.redis_search_index_batch_create(batch, true)
35
39
  puts ""
40
+ puts "Indexed #{count} rows | Time spend: #{(Time.now - tm)}s"
41
+ puts "Rebuild Index done."
42
+ end
43
+
44
+ desc index_all_desc
45
+ task all: :environment do
46
+ tm = Time.now
47
+ count = 0
48
+ dir = ENV['DIR'].to_s != '' ? ENV['DIR'] : 'app/models'
49
+ batch = ENV['BATCH'].to_i > 0 ? ENV['BATCH'].to_i : 1000
50
+
51
+ Dir.glob(File.join("#{dir}/**/*.rb")).each do |path|
52
+ model_filename = path[/#{Regexp.escape(dir.to_s)}\/([^\.]+).rb/, 1]
53
+
54
+ next if model_filename.match(/^concerns\//i) # Skip concerns/ folder
55
+
56
+ begin
57
+ klass = model_filename.camelize.constantize
58
+ rescue NameError
59
+ require(path) ? retry : raise(RuntimeError, "Cannot load class '#{klass}'")
60
+ end
61
+ end
62
+
63
+ puts "Redis-Search index data to Redis from [#{dir}]"
64
+ Redis::Search.indexed_models.each do |klass|
65
+ puts "[#{klass.to_s}]"
66
+ count += klass.redis_search_index_batch_create(batch, true)
67
+ puts ""
68
+ end
69
+ puts "Indexed #{count} rows | Time spend: #{(Time.now - tm)}s"
70
+ puts "Rebuild Index done."
36
71
  end
37
- puts ""
38
- puts "-"*120
39
- puts "Indexed #{count} rows | Time spend: #{(Time.now - tm)}s".rjust(120)
40
- puts "Rebuild Index done.".rjust(120)
41
72
  end
42
73
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason Lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-01 00:00:00.000000000 Z
11
+ date: 2014-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-pinyin