redis-search 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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
-