redis-search 0.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.markdown CHANGED
@@ -6,23 +6,25 @@ High performance real-time search (Support Chinese), index in Redis for Rails ap
6
6
 
7
7
  * Real-time search
8
8
  * High performance
9
+ * Segment words search and prefix match search
10
+ * Support ActiveRecord, Mongoid and others ORM
9
11
 
10
12
  ## Requirements
11
13
 
12
- * Redis 2.2
13
- * Libmmseg
14
+ * Ruby 1.9.2
15
+ * Redis 2.2+
14
16
 
15
17
  ## Install
16
18
 
17
19
  in Rails application Gemfile
18
20
 
19
- gem 'redis','2.1.1'
20
- gem "rmmseg-cpp-huacnlee", "0.2.8"
21
- gem 'redis-search', '0.1'
21
+ gem 'redis','2.1.1'
22
+ gem "rmmseg-cpp-huacnlee", "0.2.8"
23
+ gem 'redis-search', '0.3'
22
24
 
23
25
  install bundlers
24
26
 
25
- $ bundle install
27
+ $ bundle install
26
28
 
27
29
  ## Configure
28
30
 
@@ -30,9 +32,11 @@ create file in: config/initializers/redis_search.rb
30
32
 
31
33
  require "redis_search"
32
34
  redis = Redis.new(:host => "127.0.0.1",:port => "6379")
33
- redis.select("app_name.search")
35
+ # change redis database to 3, you need use a special database for search feature.
36
+ redis.select(3)
34
37
  RedisSearch.configure do |config|
35
- config.redis = redis
38
+ config.redis = redis
39
+ config.complete_max_length = 100
36
40
  end
37
41
 
38
42
  ## Usage
@@ -56,13 +60,35 @@ bind RedisSearch callback event, it will to rebuild search indexes when data cre
56
60
  self.category.name
57
61
  end
58
62
  end
63
+
64
+ class User
65
+ include Mongoid::Document
66
+ include RedisSearch
67
+
68
+ field :name
69
+ field :tagline
70
+ field :email
71
+
72
+ redis_search_index(:title_field => :name,
73
+ :prefix_index_enable => true,
74
+ :ext_fields => [:email,:tagline])
75
+ end
59
76
 
60
- # GET /searchs?q=title
61
77
  class SearchController < ApplicationController
78
+ # GET /searchs?q=title
62
79
  def index
63
80
  RedisSearch::Search.query(params[:q], :type => "Post")
64
81
  end
82
+
83
+ # GET /search_users?q=j
84
+ def search_users
85
+ RedisSearch::Search.complete(params[:q], :type => "Post")
86
+ end
65
87
  end
88
+
89
+ ## Benchmark test
90
+
91
+ * [https://gist.github.com/1150933](https://gist.github.com/1150933)
66
92
 
67
93
  ## Demo
68
94
 
@@ -4,6 +4,7 @@ module RedisSearch
4
4
  module ClassMethods
5
5
  def redis_search_index(options = {})
6
6
  title_field = options[:title_field] || :title
7
+ prefix_index_enable = options[:prefix_index_enable] || false
7
8
  ext_fields = options[:ext_fields] || []
8
9
  class_eval %(
9
10
  def redis_search_ext_fields(ext_fields)
@@ -18,8 +19,11 @@ module RedisSearch
18
19
  def redis_search_index_create
19
20
  s = Search.new(:title => self.#{title_field}, :id => self.id,
20
21
  :exts => self.redis_search_ext_fields(#{ext_fields}),
21
- :type => self.class.to_s)
22
+ :type => self.class.to_s,
23
+ :prefix_index_enable => #{prefix_index_enable})
22
24
  s.save
25
+ # release s
26
+ s = nil
23
27
  end
24
28
 
25
29
  before_destroy :redis_search_index_remove
@@ -4,11 +4,20 @@ module RedisSearch
4
4
  end
5
5
 
6
6
  class Config
7
- attr_accessor :redis, :debug
7
+ # Redis
8
+ attr_accessor :redis
9
+ # Debug toggle
10
+ attr_accessor :debug
11
+ # config for max length of content with RedisSearch:Search.complete method,default 100
12
+ # Please change this with your real data length, short is fast
13
+ # For example: You use complete search for your User model name field, and the "name" as max length in 15 chars, then you can set here to 15
14
+ # warring! The long content will can't be found, if the config length less than real content.
15
+ attr_accessor :complete_max_length
8
16
 
9
17
  def initialize
10
18
  self.debug = false
11
19
  self.redis = nil
20
+ self.complete_max_length = 100
12
21
  end
13
22
  end
14
23
  end
@@ -2,7 +2,7 @@
2
2
  require "rmmseg"
3
3
  module RedisSearch
4
4
  class Search
5
- attr_accessor :type, :title, :id, :exts
5
+ attr_accessor :type, :title, :id, :exts, :prefix_index_enable
6
6
  def initialize(options = {})
7
7
  self.exts = []
8
8
  options.keys.each do |k|
@@ -52,15 +52,19 @@ module RedisSearch
52
52
  return if words.blank?
53
53
  words.each do |word|
54
54
  next if not Search.word?(word)
55
- save_zindex(word)
56
55
  key = Search.mk_sets_key(self.type,word)
57
56
  RedisSearch.config.redis.sadd(key, self.id)
58
57
  end
58
+
59
+ # 建立前最索引
60
+ if prefix_index_enable
61
+ save_prefix_index(self.title)
62
+ end
59
63
  end
60
64
 
61
- def save_zindex(word)
62
- return if not Search.word?(word)
63
- word = word.downcase
65
+ def save_prefix_index(title)
66
+ return if not Search.word?(title)
67
+ word = title.downcase
64
68
  key = Search.mk_complete_key(self.type)
65
69
  (1..(word.length)).each do |l|
66
70
  prefix = word[0...l]
@@ -96,15 +100,17 @@ module RedisSearch
96
100
  limit = options[:limit] || 10
97
101
 
98
102
  prefix_matchs = []
99
- rangelen = 100 # This is not random, try to get replies < MTU size
103
+ # This is not random, try to get replies < MTU size
104
+ rangelen = RedisSearch.config.complete_max_length
100
105
  prefix = w.downcase
101
106
  key = Search.mk_complete_key(type)
107
+
102
108
  start = RedisSearch.config.redis.zrank(key,prefix)
103
-
104
109
  return [] if !start
105
110
  count = limit
111
+ max_range = start+(rangelen*limit)-1
112
+ range = RedisSearch.config.redis.zrange(key,start,max_range)
106
113
  while prefix_matchs.length <= count
107
- range = RedisSearch.config.redis.zrange(key,start,start+rangelen-1)
108
114
  start += rangelen
109
115
  break if !range or range.length == 0
110
116
  range.each {|entry|
@@ -117,12 +123,25 @@ module RedisSearch
117
123
  prefix_matchs << entry[0...-1]
118
124
  end
119
125
  }
126
+ range = range[start..max_range]
120
127
  end
121
128
  words = []
122
129
  words = prefix_matchs.uniq.collect { |w| Search.mk_sets_key(type,w) }
123
- ids = RedisSearch.config.redis.sunion(*words)
130
+ if words.length > 1
131
+ temp_store_key = "tmpsunionstore:#{words.join("+")}"
132
+ if !RedisSearch.config.redis.exists(temp_store_key)
133
+ # 将多个词语组合对比,得到并集,并存入临时区域
134
+ RedisSearch.config.redis.sunionstore(temp_store_key,*words)
135
+ # 将临时搜索设为1天后自动清除
136
+ RedisSearch.config.redis.expire(temp_store_key,86400)
137
+ end
138
+ # 根据需要的数量取出 ids
139
+ ids = RedisSearch.config.redis.sort(temp_store_key,:limit => [0,limit])
140
+ else
141
+ ids = RedisSearch.config.redis.sort(words.first,:limit => [0,limit])
142
+ end
124
143
  return [] if ids.blank?
125
- hmget(type,ids, :limit => limit)
144
+ hmget(type,ids)
126
145
  end
127
146
 
128
147
  # Search items, this will split words by Libmmseg
@@ -142,14 +161,26 @@ module RedisSearch
142
161
  sort_field = options[:sort_field] || "id"
143
162
  words = words.collect { |w| Search.mk_sets_key(type,w) }
144
163
  return result if words.blank?
145
- ids = RedisSearch.config.redis.sinter(*words)
146
- hmget(type,ids, :limit => limit, :sort_field => sort_field)
164
+ temp_store_key = "tmpinterstore:#{words.join("+")}"
165
+ if words.length > 1
166
+ if !RedisSearch.config.redis.exists(temp_store_key)
167
+ # 将多个词语组合对比,得到交集,并存入临时区域
168
+ RedisSearch.config.redis.sinterstore(temp_store_key,*words)
169
+ # 将临时搜索设为1天后自动清除
170
+ RedisSearch.config.redis.expire(temp_store_key,86400)
171
+ end
172
+ # 根据需要的数量取出 ids
173
+ ids = RedisSearch.config.redis.sort(temp_store_key,:limit => [0,limit])
174
+ else
175
+ # 根据需要的数量取出 ids
176
+ ids = RedisSearch.config.redis.sort(words.first,:limit => [0,limit])
177
+ end
178
+ hmget(type,ids, :sort_field => sort_field)
147
179
  end
148
180
 
149
181
  private
150
182
  def self.hmget(type, ids, options = {})
151
183
  result = []
152
- limit = options[:limit] || 10
153
184
  sort_field = options[:sort_field] || "id"
154
185
  return result if ids.blank?
155
186
  RedisSearch.config.redis.hmget(type,*ids).each do |r|
@@ -159,15 +190,7 @@ module RedisSearch
159
190
  Search.warn("Search.query failed: #{e}")
160
191
  end
161
192
  end
162
- items = sort_result(result, type, sort_field)
163
- items = items[0..limit-1] if items.length > limit
164
- items
165
- end
166
-
167
- def self.sort_result(items, type, sort_field)
168
- return items if items.blank?
169
- items = items.sort { |x,y| y[sort_field] <=> x[sort_field] }
170
- items
193
+ result
171
194
  end
172
195
  end
173
196
  end
metadata CHANGED
@@ -1,104 +1,74 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: redis-search
3
- version: !ruby/object:Gem::Version
4
- hash: 15
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- version: "0.2"
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Jason Lee
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-08-17 00:00:00 +08:00
18
- default_executable:
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-17 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: rmmseg-cpp-huacnlee
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2159797520 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
18
+ requirements:
26
19
  - - ~>
27
- - !ruby/object:Gem::Version
28
- hash: 7
29
- segments:
30
- - 0
31
- - 2
32
- - 8
20
+ - !ruby/object:Gem::Version
33
21
  version: 0.2.8
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: redis
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2159797520
25
+ - !ruby/object:Gem::Dependency
26
+ name: redis
27
+ requirement: &2159797040 !ruby/object:Gem::Requirement
40
28
  none: false
41
- requirements:
29
+ requirements:
42
30
  - - ~>
43
- - !ruby/object:Gem::Version
44
- hash: 9
45
- segments:
46
- - 2
47
- - 1
48
- - 1
31
+ - !ruby/object:Gem::Version
49
32
  version: 2.1.1
50
33
  type: :runtime
51
- version_requirements: *id002
52
- description: High performance real-time search (Support Chinese), index in Redis for Rails application.
53
- email:
34
+ prerelease: false
35
+ version_requirements: *2159797040
36
+ description: High performance real-time search (Support Chinese), index in Redis for
37
+ Rails application.
38
+ email:
54
39
  - huacnlee@gmail.com
55
40
  executables: []
56
-
57
41
  extensions: []
58
-
59
42
  extra_rdoc_files: []
60
-
61
- files:
43
+ files:
62
44
  - lib/redis_search/base.rb
63
45
  - lib/redis_search/config.rb
64
46
  - lib/redis_search/search.rb
65
47
  - lib/redis_search.rb
66
48
  - README.markdown
67
- has_rdoc: true
68
49
  homepage: http://github.com/huacnlee/redis-search
69
50
  licenses: []
70
-
71
51
  post_install_message:
72
52
  rdoc_options: []
73
-
74
- require_paths:
53
+ require_paths:
75
54
  - lib
76
- required_ruby_version: !ruby/object:Gem::Requirement
55
+ required_ruby_version: !ruby/object:Gem::Requirement
77
56
  none: false
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- hash: 3
82
- segments:
83
- - 0
84
- version: "0"
85
- required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
62
  none: false
87
- requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- hash: 23
91
- segments:
92
- - 1
93
- - 3
94
- - 6
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
95
66
  version: 1.3.6
96
67
  requirements: []
97
-
98
68
  rubyforge_project:
99
- rubygems_version: 1.6.2
69
+ rubygems_version: 1.8.6
100
70
  signing_key:
101
71
  specification_version: 3
102
- summary: High performance real-time search (Support Chinese), index in Redis for Rails application.
72
+ summary: High performance real-time search (Support Chinese), index in Redis for Rails
73
+ application.
103
74
  test_files: []
104
-