redi_search_rails 0.1.2 → 0.1.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -0
- data/README.md +10 -2
- data/lib/redi_search_rails.rb +148 -14
- data/lib/redi_search_rails/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8bac86f5f0acbceaf4952a150531ddedc0c59e72
|
4
|
+
data.tar.gz: 1dbe06379bff95b1ef9d81e328871e808f693a47
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe54ebbd8202130643d8cedc40158faf6edd830f7ad524c4fdbf7864c734ca4f8738559cfee67a65580387fd4501052396337e654195f9722c1621f3f721ea19
|
7
|
+
data.tar.gz: a0c895f524468aa0f5777eff292f82512f148cbe7db3488fd951dd639010d559b28e5e3814e5e9d83d1856048ed4a149b4e15387b29237e8646b69f693aeb9fa
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
### Changed
|
12
|
+
|
13
|
+
### Removed
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
## 0.1.3 - 2017-05-19
|
18
|
+
|
19
|
+
### Added
|
20
|
+
- ft_search_format and ft_search_count methods
|
21
|
+
- ft_search_format returns array of objects
|
22
|
+
- ft_add_hash and ft_del_all methods
|
23
|
+
- ft_sug* methods
|
24
|
+
|
25
|
+
### Changed
|
26
|
+
- ft_search returns raw FT.SEARCH results
|
27
|
+
- switched to keyword arguments
|
28
|
+
- passing offset and num to FT.SEARCH
|
29
|
+
|
30
|
+
## 0.1.2 - 2017-04-23
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
- variable scope with redi_search_schema
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
- changed ft_search output to be an array of hashes
|
37
|
+
- changed dependencies to > Rails 4.2.8
|
data/README.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# RediSearchRails
|
2
2
|
|
3
|
+
[](https://codeclimate.com/github/dmitrypol/redi_search_rails)
|
4
|
+
[](http://inch-ci.org/github/dmitrypol/redi_search_rails)
|
5
|
+
[](http://badge.fury.io/rb/redi_search_rails)
|
6
|
+
|
3
7
|
This gems simplifies integration with RediSearch module (http://redisearch.io/). This software is of Alpha quality and is provided with no warranties whatsoever. Additionally RediSearch is still not officially released so major features may change.
|
4
8
|
|
5
9
|
## Installation
|
@@ -42,7 +46,11 @@ User.ft_add_all
|
|
42
46
|
# => or you can do it for specific record
|
43
47
|
User.ft_add(User.where(id: 1))
|
44
48
|
# => search
|
45
|
-
User.ft_search('keyword here')
|
49
|
+
User.ft_search(keyword: 'keyword here', offset: 0, num: 10)
|
50
|
+
# => output
|
51
|
+
[1, "gid://application_name/User/unique_id", ["name", "Bob", "age", "100"]]
|
52
|
+
# => format results as array of objects
|
53
|
+
User.ft_search_format(keyword: 'keyword here', offset: 0, num: 10)
|
46
54
|
# => output
|
47
55
|
[{"id": "gid://application_name/User/unique_id", "name": "Bob", "age": "100"}, {...}]
|
48
56
|
```
|
@@ -63,9 +71,9 @@ Testing this gem requires having local Redis with RediSearch module. This makes
|
|
63
71
|
|
64
72
|
* ActiveModel callbacks to index records on saving and remove from Redis on delete
|
65
73
|
* Rake tasks to manage indexes
|
66
|
-
* Support additional RediSearch API calls (SUGGADD, SUGGET, ...)
|
67
74
|
* Support configuring SCORE, WEIGHT and other options
|
68
75
|
* Support indexing fields from related models (index group name if user belongs to a group)
|
76
|
+
* Support GEO filters
|
69
77
|
* Stopwords configuration
|
70
78
|
* Minimum keyword length to index
|
71
79
|
* Configurable method for doc_id, not just default to_global_id
|
data/lib/redi_search_rails.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "redi_search_rails/version"
|
2
2
|
require "active_support/concern"
|
3
|
+
require "ostruct"
|
3
4
|
|
4
5
|
module RediSearchRails
|
5
6
|
extend ActiveSupport::Concern
|
@@ -24,15 +25,37 @@ module RediSearchRails
|
|
24
25
|
# search the index for specific keyword(s)
|
25
26
|
#
|
26
27
|
# @param keyword [String] 'some keyword'
|
27
|
-
# @
|
28
|
+
# @param offset [Integer] default 0
|
29
|
+
# @param keyword [Integer] default 10
|
30
|
+
# @return [Array] [1, "gid://application_name/User/unique_id", ["name", "Bob", "age", "100"]]
|
28
31
|
# @raise [RuntimeError]
|
29
|
-
def ft_search keyword
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
def ft_search keyword:, offset: 0, num: 10, filter: {}
|
33
|
+
if filter[:numeric_field].blank?
|
34
|
+
results = REDI_SEARCH.call('FT.SEARCH', @index_name, keyword.strip,
|
35
|
+
'LIMIT', offset, num)
|
36
|
+
else
|
37
|
+
results = REDI_SEARCH.call('FT.SEARCH', @index_name, keyword.strip,
|
38
|
+
'LIMIT', offset, num,
|
39
|
+
'FILTER', filter[:numeric_field], filter[:min], filter[:max]
|
40
|
+
)
|
41
|
+
end
|
42
|
+
#'NOCONTENT', 'VERBATIM', 'WITHSCORES', 'NOSTOPWORDS', 'WITHPAYLOADS',
|
43
|
+
#'INKEYS', 'INFIELDS', 'SLOP', 'LANGUAGE', 'EXPANDER', 'SCORER', 'PAYLOAD', 'SORTBY'
|
44
|
+
return results
|
45
|
+
rescue Exception => e
|
46
|
+
Rails.logger.error e if defined? Rails
|
47
|
+
return e.message
|
48
|
+
end
|
49
|
+
|
50
|
+
# search the index for specific keyword(s) and return output as array of objects
|
51
|
+
#
|
52
|
+
# @param keyword [String] 'some keyword'
|
53
|
+
# @param offset [Integer] default 0
|
54
|
+
# @param keyword [Integer] default 10
|
55
|
+
# @return [Array] [{"id": "gid://application_name/User/unique_id", "name": "Bob", "age": "100"}, ...]
|
56
|
+
def ft_search_format(args)
|
57
|
+
results = ft_search(args)
|
58
|
+
# => transform into array of objects
|
36
59
|
output = []
|
37
60
|
results.shift # => remove count
|
38
61
|
results.each_slice(2) do |result|
|
@@ -40,7 +63,8 @@ module RediSearchRails
|
|
40
63
|
result[1].each_slice(2) do |attribute|
|
41
64
|
attributes[attribute[0]] = attribute[1]
|
42
65
|
end
|
43
|
-
|
66
|
+
hash = {id: result[0]}.merge(attributes)
|
67
|
+
output << OpenStruct.new(hash)
|
44
68
|
end
|
45
69
|
return output
|
46
70
|
rescue Exception => e
|
@@ -48,13 +72,24 @@ module RediSearchRails
|
|
48
72
|
return e.message
|
49
73
|
end
|
50
74
|
|
75
|
+
# number of records found for keywords
|
76
|
+
#
|
77
|
+
# @param keyword [Hash] keyword that gets passed to ft_search
|
78
|
+
# @return [Integer] number of results matching the search
|
79
|
+
def ft_search_count(args)
|
80
|
+
ft_search(args).first
|
81
|
+
rescue Exception => e
|
82
|
+
Rails.logger.error e if defined? Rails
|
83
|
+
return e.message
|
84
|
+
end
|
85
|
+
|
51
86
|
# create index for specific model
|
52
87
|
#
|
53
88
|
# @return [String]
|
54
89
|
def ft_create
|
55
90
|
REDI_SEARCH.call('FT.CREATE', @index_name,
|
56
|
-
#'NOFIELDS', 'NOSCOREIDX', 'NOOFFSETS',
|
57
91
|
'SCHEMA', @schema
|
92
|
+
#'NOFIELDS', 'NOSCOREIDX', 'NOOFFSETS',
|
58
93
|
)
|
59
94
|
ft_optimize
|
60
95
|
rescue Exception => e
|
@@ -66,7 +101,8 @@ module RediSearchRails
|
|
66
101
|
#
|
67
102
|
# @return [String]
|
68
103
|
def ft_add_all
|
69
|
-
@model.all.each {|record| ft_add(record) }
|
104
|
+
@model.all.each {|record| ft_add(record: record) }
|
105
|
+
ft_optimize
|
70
106
|
rescue Exception => e
|
71
107
|
Rails.logger.error e if defined? Rails
|
72
108
|
return e.message
|
@@ -76,24 +112,46 @@ module RediSearchRails
|
|
76
112
|
#
|
77
113
|
# @param record [Object] Object to index
|
78
114
|
# @return [String]
|
79
|
-
def ft_add record
|
115
|
+
def ft_add record:
|
80
116
|
fields = []
|
81
117
|
@fields.each { |field| fields.push(field, record.send(field)) }
|
82
118
|
REDI_SEARCH.call('FT.ADD', @index_name, record.to_global_id.to_s, @score,
|
83
119
|
'REPLACE',
|
84
|
-
#'NOSAVE', 'PAYLOAD', record.name,
|
85
120
|
'FIELDS', fields
|
121
|
+
#'NOSAVE', 'PAYLOAD', 'LANGUAGE'
|
86
122
|
)
|
87
123
|
rescue Exception => e
|
88
124
|
Rails.logger.error e if defined? Rails
|
89
125
|
return e.message
|
90
126
|
end
|
91
127
|
|
128
|
+
# index existing Hash
|
129
|
+
#
|
130
|
+
# @param record [string] key of existing HASH key in Redis that will hold the fields the index needs.
|
131
|
+
# @return [String]
|
132
|
+
def ft_addhash redis_key:
|
133
|
+
REDI_SEARCH.call('FT.ADDHASH', @index_name, redis_key, @score, 'REPLACE')
|
134
|
+
rescue Exception => e
|
135
|
+
Rails.logger.error e if defined? Rails
|
136
|
+
return e.message
|
137
|
+
end
|
138
|
+
|
139
|
+
# delete all records in specific model
|
140
|
+
#
|
141
|
+
# @return [String]
|
142
|
+
def ft_del_all
|
143
|
+
@model.all.each {|record| ft_del(record: record) }
|
144
|
+
ft_optimize
|
145
|
+
rescue Exception => e
|
146
|
+
Rails.logger.error e if defined? Rails
|
147
|
+
return e.message
|
148
|
+
end
|
149
|
+
|
92
150
|
# delete specific document from index
|
93
151
|
#
|
94
152
|
# @param record [Object] Object to delete
|
95
153
|
# @return [String]
|
96
|
-
def ft_del record
|
154
|
+
def ft_del record:
|
97
155
|
doc_id = record.to_global_id
|
98
156
|
REDI_SEARCH.call('FT.DEL', @index_name, doc_id)
|
99
157
|
rescue Exception => e
|
@@ -131,6 +189,82 @@ module RediSearchRails
|
|
131
189
|
return e.message
|
132
190
|
end
|
133
191
|
|
192
|
+
# add all values for a model attribute to autocomplete
|
193
|
+
#
|
194
|
+
# @param attribute [String] - name, email, etc
|
195
|
+
def ft_sugadd_all (attribute:)
|
196
|
+
@model.all.each {|record| ft_sugadd(record: record, attribute: attribute) }
|
197
|
+
rescue Exception => e
|
198
|
+
Rails.logger.error e if defined? Rails
|
199
|
+
return e.message
|
200
|
+
end
|
201
|
+
|
202
|
+
# add string to autocomplete dictionary
|
203
|
+
#
|
204
|
+
# @param record [Object] object
|
205
|
+
# @param attribute [String] - name, email, etc
|
206
|
+
# @param score [Integer] - score
|
207
|
+
# @return [Integer] - current size of the dictionary
|
208
|
+
def ft_sugadd (record:, attribute:, score: 1)
|
209
|
+
# => combine model with attribute to create unique key like user_name
|
210
|
+
key = "#{@model.to_s}:#{attribute}"
|
211
|
+
string = record.send(attribute)
|
212
|
+
REDI_SEARCH.call('FT.SUGADD', key, string, score)
|
213
|
+
# => INCR
|
214
|
+
rescue Exception => e
|
215
|
+
Rails.logger.error e if defined? Rails
|
216
|
+
return e.message
|
217
|
+
end
|
218
|
+
|
219
|
+
# query dictionary for suggestion
|
220
|
+
#
|
221
|
+
# @param attribute [String] - name, email, etc
|
222
|
+
# @param prefix [String] - prefix to query dictionary
|
223
|
+
# @return [Array] - suggestions for prefix
|
224
|
+
def ft_sugget (attribute:, prefix:)
|
225
|
+
key = "#{@model}:#{attribute}"
|
226
|
+
REDI_SEARCH.call('FT.SUGGET', key, prefix)
|
227
|
+
rescue Exception => e
|
228
|
+
Rails.logger.error e if defined? Rails
|
229
|
+
return e.message
|
230
|
+
end
|
231
|
+
|
232
|
+
# delete all values for a model attribute to autocomplete
|
233
|
+
#
|
234
|
+
# @param attribute [String] - name, email, etc
|
235
|
+
def ft_sugdel_all (attribute:)
|
236
|
+
@model.all.each {|record| ft_sugdel(record: record, attribute: attribute) }
|
237
|
+
rescue Exception => e
|
238
|
+
Rails.logger.error e if defined? Rails
|
239
|
+
return e.message
|
240
|
+
end
|
241
|
+
|
242
|
+
# delete a string from a suggestion index.
|
243
|
+
#
|
244
|
+
# @param attribute [String]
|
245
|
+
# @param value [String] - string to delete
|
246
|
+
# @return [Integer] - 1 if found, 0 if not
|
247
|
+
def ft_sugdel (record:, attribute:)
|
248
|
+
key = "#{@model}:#{attribute}"
|
249
|
+
string = record.send(attribute)
|
250
|
+
REDI_SEARCH.call('FT.SUGDEL', key, string)
|
251
|
+
rescue Exception => e
|
252
|
+
Rails.logger.error e if defined? Rails
|
253
|
+
return e.message
|
254
|
+
end
|
255
|
+
|
256
|
+
# size of dictionary
|
257
|
+
#
|
258
|
+
# @param attribute [String]
|
259
|
+
# @return [Integer] - number of possible suggestions
|
260
|
+
def ft_suglen (attribute:)
|
261
|
+
key = "#{@model}:#{attribute}"
|
262
|
+
REDI_SEARCH.call('FT.SUGLEN', key)
|
263
|
+
rescue Exception => e
|
264
|
+
Rails.logger.error e if defined? Rails
|
265
|
+
return e.message
|
266
|
+
end
|
267
|
+
|
134
268
|
end
|
135
269
|
|
136
270
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redi_search_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Polyakovsky
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- ".gitignore"
|
119
119
|
- ".rspec"
|
120
120
|
- ".travis.yml"
|
121
|
+
- CHANGELOG.md
|
121
122
|
- CODE_OF_CONDUCT.md
|
122
123
|
- Gemfile
|
123
124
|
- LICENSE.txt
|
@@ -149,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
149
150
|
version: '0'
|
150
151
|
requirements: []
|
151
152
|
rubyforge_project:
|
152
|
-
rubygems_version: 2.
|
153
|
+
rubygems_version: 2.6.11
|
153
154
|
signing_key:
|
154
155
|
specification_version: 4
|
155
156
|
summary: Simplifies integration with http://redisearch.io/.
|