redi_search_rails 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Code Climate](https://codeclimate.com/github/dmitrypol/redi_search_rails.svg)](https://codeclimate.com/github/dmitrypol/redi_search_rails)
|
4
|
+
[![Inline docs](http://inch-ci.org/github/dmitrypol/redi_search_rails.svg?branch=master)](http://inch-ci.org/github/dmitrypol/redi_search_rails)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/redi_search_rails.svg)](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/.
|