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 +35 -9
- data/lib/redis_search/base.rb +5 -1
- data/lib/redis_search/config.rb +10 -1
- data/lib/redis_search/search.rb +45 -22
- metadata +35 -65
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
|
-
*
|
13
|
-
*
|
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
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
|
data/lib/redis_search/base.rb
CHANGED
@@ -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
|
data/lib/redis_search/config.rb
CHANGED
@@ -4,11 +4,20 @@ module RedisSearch
|
|
4
4
|
end
|
5
5
|
|
6
6
|
class Config
|
7
|
-
|
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
|
data/lib/redis_search/search.rb
CHANGED
@@ -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
|
62
|
-
return if not Search.word?(
|
63
|
-
word =
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
146
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
82
|
-
|
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
|
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
|
72
|
+
summary: High performance real-time search (Support Chinese), index in Redis for Rails
|
73
|
+
application.
|
103
74
|
test_files: []
|
104
|
-
|