redisearch-rb 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/lib/redisearch.rb +116 -38
- data/lib/redisearch/version.rb +1 -1
- data/test/redisearch_test.rb +31 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea52a309430c3f6fbed50c64e43e8a6158606584
|
4
|
+
data.tar.gz: 0bcfe47c805b649f14cefed4772526f7004f2173
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3d38b1b71012fa9acd1bb7d14a7e71db16c7d840a3ec934f93f1167b5fb4e86440fc6be916ab17bfe77757b1f4617d2f25d92b490ef3eeed734177edf18605a
|
7
|
+
data.tar.gz: 2f80306e5c404618a47ed90aee72c42e77ee343559f2ce54539ef4f74e61d575f3a851761c22ef6520994bde9a04d9ff84269da2da0560efa4d98f0f23d26666
|
data/.travis.yml
CHANGED
data/lib/redisearch.rb
CHANGED
@@ -12,15 +12,24 @@ class RediSearch
|
|
12
12
|
#
|
13
13
|
# { verbatim: true, withscores: true, withsortkey: false }
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
OPTIONS_FLAGS = {
|
16
|
+
add: [:nosave, :replace, :partial],
|
17
|
+
create: [:nooffsets, :nofreqs, :nohl, :nofields],
|
18
|
+
del: [:dd],
|
19
|
+
drop: [:keepdocs],
|
20
|
+
search: [:nocontent, :verbatim, :nostopwords, :withscores, :withsortkeys],
|
21
|
+
sugadd: [:incr],
|
22
|
+
sugget: [:fuzzy, :withscores],
|
23
|
+
}
|
18
24
|
|
19
25
|
# Params options need an array with the values for the option
|
20
26
|
# { limit: ['0', '50'], sortby: ['year', 'desc'], return: ['2', 'title', 'year'] }
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
OPTIONS_PARAMS = {
|
28
|
+
add: [:language, :payload],
|
29
|
+
create: [:stopwords],
|
30
|
+
search: [:filter, :return, :infields, :inkeys, :slop, :scorer, :sortby, :limit, :payload],
|
31
|
+
sugget: [:max],
|
32
|
+
}
|
24
33
|
|
25
34
|
# Create RediSearch client instance
|
26
35
|
#
|
@@ -54,8 +63,8 @@ class RediSearch
|
|
54
63
|
#
|
55
64
|
# See http://redisearch.io/Commands/#ftdrop
|
56
65
|
# @return [String] "OK" on success
|
57
|
-
def drop_index
|
58
|
-
call(ft_drop)
|
66
|
+
def drop_index(opts = {})
|
67
|
+
call(ft_drop(opts))
|
59
68
|
end
|
60
69
|
|
61
70
|
# Add a single doc to the index, with the given `doc_id` and `fields`
|
@@ -122,27 +131,75 @@ class RediSearch
|
|
122
131
|
#
|
123
132
|
# @param [String] doc_id id assigned to the document
|
124
133
|
# @return [int] 1 if the document was in the index, or 0 if not.
|
125
|
-
def delete_by_id(doc_id)
|
126
|
-
call(ft_del(doc_id))
|
134
|
+
def delete_by_id(doc_id, opts = {})
|
135
|
+
call(ft_del(doc_id, opts))
|
127
136
|
end
|
128
137
|
|
129
|
-
# Deletes all documents
|
138
|
+
# Deletes all documents matching the query
|
130
139
|
#
|
131
140
|
# @param [String] query in the same format as used in `search`
|
132
141
|
# @param [Hash] opts options for the query, same as in `search`
|
133
142
|
# @return [int] count of documents deleted
|
134
143
|
def delete_by_query(query, opts = {})
|
135
144
|
call(ft_search(query, opts.merge(nocontent: true)))[1..-1].map do |doc_id|
|
136
|
-
call(ft_del(doc_id))
|
145
|
+
call(ft_del(doc_id, opts))
|
137
146
|
end.sum
|
138
147
|
end
|
139
148
|
|
140
|
-
#
|
149
|
+
# Adds a string to an auto-complete suggestion dictionary.
|
150
|
+
#
|
151
|
+
# See https://oss.redislabs.com/redisearch/Commands/#ftsugadd
|
152
|
+
#
|
153
|
+
# @param [String] dict_name the key used to store the dictionary
|
154
|
+
# @param [String] content the string that is going to be indexed
|
155
|
+
# @param [Hash] opts optional parameters
|
156
|
+
# @return [int] current size of the dictionary
|
157
|
+
def autocomplete_add(dict_name, content, score = 1.0, opts = {})
|
158
|
+
call(ft_sugadd(dict_name, content, score, opts))
|
159
|
+
end
|
160
|
+
|
161
|
+
# Gets completion suggestions for a prefix.
|
162
|
+
#
|
163
|
+
# See https://oss.redislabs.com/redisearch/Commands/#ftsugadd
|
164
|
+
#
|
165
|
+
# @param [String] dict_name the key used to store the dictionary
|
166
|
+
# @param [String] prefix the prefix to search / complete
|
167
|
+
# @param [Hash] opts optional parameters
|
168
|
+
# @return [Array] a list of the top suggestions matching the prefix,
|
169
|
+
# optionally with score after each entry
|
170
|
+
def autocomplete_get(dict_name, prefix, opts = {})
|
171
|
+
call(ft_sugget(dict_name, prefix, opts))
|
172
|
+
end
|
173
|
+
|
174
|
+
# Deletes a string from an auto-complete suggestion dictionary.
|
175
|
+
#
|
176
|
+
# See https://oss.redislabs.com/redisearch/Commands/#ftsugdel
|
177
|
+
#
|
178
|
+
# @param [String] dict_name the key used to store the dictionary
|
179
|
+
# @param [String] content the string that is going to be deleted
|
180
|
+
# @param [Hash] opts optional parameters
|
181
|
+
# @return [int] 1 if the string was found and deleted, 0 otherwise
|
182
|
+
def autocomplete_del(dict_name, content)
|
183
|
+
call(ft_sugdel(dict_name, content))
|
184
|
+
end
|
185
|
+
|
186
|
+
# Gets the current size of an auto-complete suggestion dictionary.
|
187
|
+
#
|
188
|
+
# See https://oss.redislabs.com/redisearch/Commands/#ftsugdel
|
189
|
+
#
|
190
|
+
# @param [String] dict_name the key used to store the dictionary
|
191
|
+
# @return [int] current size of the dictionary
|
192
|
+
def autocomplete_len(dict_name)
|
193
|
+
call(ft_suglen(dict_name))
|
194
|
+
end
|
195
|
+
|
196
|
+
# Execute arbitrary command in redisearch index
|
197
|
+
# Only RediSearch commands are allowed
|
141
198
|
#
|
142
199
|
# @param [Array] command
|
143
200
|
# @return [mixed] The output returned by redis
|
144
201
|
def call(command)
|
145
|
-
raise ArgumentError.new("#{command
|
202
|
+
raise ArgumentError.new("unknown/unsupported command '#{command.first}'") unless valid_command?(command.first)
|
146
203
|
@redis.with_reconnect { @redis.call(command.flatten) }
|
147
204
|
end
|
148
205
|
|
@@ -161,11 +218,11 @@ class RediSearch
|
|
161
218
|
end
|
162
219
|
|
163
220
|
def ft_create(schema, opts)
|
164
|
-
['FT.CREATE', @idx_name , *
|
221
|
+
['FT.CREATE', @idx_name , *serialize_options(opts, :create), 'SCHEMA', *schema]
|
165
222
|
end
|
166
223
|
|
167
|
-
def ft_drop
|
168
|
-
['FT.DROP', @idx_name]
|
224
|
+
def ft_drop(opts)
|
225
|
+
['FT.DROP', @idx_name, *serialize_options(opts, :drop)]
|
169
226
|
end
|
170
227
|
|
171
228
|
def ft_info
|
@@ -173,15 +230,15 @@ class RediSearch
|
|
173
230
|
end
|
174
231
|
|
175
232
|
def ft_add(doc_id, fields, opts = {}, weight = nil)
|
176
|
-
['FT.ADD', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *
|
233
|
+
['FT.ADD', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *serialize_options(opts, :add), 'FIELDS', *fields]
|
177
234
|
end
|
178
235
|
|
179
236
|
def ft_add_hash(doc_id, opts = {}, weight = nil)
|
180
|
-
['FT.ADDHASH', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *
|
237
|
+
['FT.ADDHASH', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *serialize_options(opts, :add)]
|
181
238
|
end
|
182
239
|
|
183
240
|
def ft_search(query, opts)
|
184
|
-
['FT.SEARCH', @idx_name, *query, *
|
241
|
+
['FT.SEARCH', @idx_name, *query, *serialize_options(opts, :search)].flatten
|
185
242
|
end
|
186
243
|
|
187
244
|
def ft_get(doc_id)
|
@@ -192,40 +249,59 @@ class RediSearch
|
|
192
249
|
['FT.MGET', @idx_name , *doc_ids]
|
193
250
|
end
|
194
251
|
|
195
|
-
def ft_del(doc_id)
|
196
|
-
['FT.DEL', @idx_name , doc_id]
|
252
|
+
def ft_del(doc_id, opts)
|
253
|
+
['FT.DEL', @idx_name , doc_id, *serialize_options(opts, :del)]
|
254
|
+
end
|
255
|
+
|
256
|
+
def ft_tagvals(field_name)
|
257
|
+
['FT.TAGVALS', @idx_name , field_name]
|
258
|
+
end
|
259
|
+
|
260
|
+
def ft_explain(query, opts)
|
261
|
+
['FT.EXPLAIN', @idx_name, *query, *serialize_options(opts, :search)].flatten
|
262
|
+
end
|
263
|
+
|
264
|
+
def ft_sugadd(dict_name, content, score, opts)
|
265
|
+
['FT.SUGADD', dict_name , content, score, *serialize_options(opts, :sugadd)]
|
197
266
|
end
|
198
267
|
|
199
|
-
def
|
200
|
-
|
268
|
+
def ft_sugdel(dict_name, content)
|
269
|
+
['FT.SUGDEL', dict_name , content]
|
201
270
|
end
|
202
271
|
|
203
|
-
def
|
204
|
-
|
272
|
+
def ft_suglen(dict_name)
|
273
|
+
['FT.SUGLEN', dict_name]
|
205
274
|
end
|
206
275
|
|
207
|
-
def
|
208
|
-
|
276
|
+
def ft_sugget(dict_name, prefix,opts)
|
277
|
+
['FT.SUGGET', dict_name , prefix, *serialize_options(opts, :sugget)]
|
209
278
|
end
|
210
279
|
|
211
|
-
def
|
212
|
-
|
280
|
+
def serialize_options(opts, method)
|
281
|
+
[flags_for_method(opts, method), params_for_method(opts, method)].flatten.compact
|
282
|
+
end
|
283
|
+
|
284
|
+
def flags_for_method(opts, method)
|
285
|
+
OPTIONS_FLAGS[method].to_a.map do |key|
|
213
286
|
key.to_s.upcase if opts[key]
|
214
|
-
end.compact
|
215
|
-
|
216
|
-
|
217
|
-
|
287
|
+
end.compact
|
288
|
+
end
|
289
|
+
|
290
|
+
def params_for_method(opts, method)
|
291
|
+
OPTIONS_PARAMS[method].to_a.map do |key|
|
292
|
+
[key.to_s.upcase, *opts[key]] unless opts[key].nil?
|
293
|
+
end.compact
|
218
294
|
end
|
219
295
|
|
220
296
|
def build_docs(results, opts = {})
|
221
297
|
return {} if results.nil? || results[0] == 0
|
222
298
|
results.shift
|
223
299
|
score_offset = opts[:withscores] ? 1 : 0
|
224
|
-
content_offset =
|
225
|
-
rows_per_doc = 1 + content_offset
|
300
|
+
content_offset = opts[:nocontent] ? 0 : 1
|
301
|
+
rows_per_doc = 1 + content_offset + score_offset
|
226
302
|
nr_of_docs = results.size / rows_per_doc
|
227
303
|
(0..nr_of_docs-1).map do |n|
|
228
|
-
doc = opts[:nocontent] ? {} : Hash[*results[rows_per_doc * n + content_offset]]
|
304
|
+
doc = opts[:nocontent] ? {} : Hash[*results[rows_per_doc * n + content_offset + score_offset]]
|
229
305
|
doc['score'] = results[rows_per_doc * n + score_offset] if opts[:withscores]
|
230
306
|
doc['id'] = results[rows_per_doc * n]
|
231
307
|
doc
|
@@ -233,6 +309,8 @@ class RediSearch
|
|
233
309
|
end
|
234
310
|
|
235
311
|
def valid_command?(command)
|
236
|
-
|
312
|
+
%w(FT.CREATE FT.ADD FT.ADDHASH FT.SEARCH FT.DEL FT.DROP FT.GET FT.MGET
|
313
|
+
FT.SUGADD FT.SUGGET FT.SUGDEL FT.SUGLEN FT.SYNADD FT.SYNUPDATE FT.SYNDUMP
|
314
|
+
FT.INFO FT.AGGREGATE FT.EXPLAIN FT.TAGVALS).include?(command)
|
237
315
|
end
|
238
316
|
end
|
data/lib/redisearch/version.rb
CHANGED
data/test/redisearch_test.rb
CHANGED
@@ -37,8 +37,19 @@ class RediSearchTest < Minitest::Test
|
|
37
37
|
|
38
38
|
def test_drop_idx
|
39
39
|
assert(@redisearch_client.create_index(@schema))
|
40
|
-
|
40
|
+
doc = ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']
|
41
|
+
@redisearch_client.add_doc('id_1', doc)
|
42
|
+
@redisearch_client.drop_index
|
41
43
|
assert_raises(Redis::CommandError) { @redisearch_client.info }
|
44
|
+
assert(@redis_client.hgetall('id_1').empty?)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_drop_idx_keep_documents
|
48
|
+
@redisearch_client.create_index(@schema)
|
49
|
+
doc = ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']
|
50
|
+
@redisearch_client.add_doc('id_1', doc)
|
51
|
+
@redisearch_client.drop_index({ keepdocs: true })
|
52
|
+
assert(@redis_client.hgetall('id_1').any?)
|
42
53
|
end
|
43
54
|
|
44
55
|
def test_add_doc
|
@@ -175,10 +186,28 @@ class RediSearchTest < Minitest::Test
|
|
175
186
|
['id_3', ['title', 'Terminator', 'director', 'James Cameron', 'year', '1984']],
|
176
187
|
['id_4', ['title', 'Blade Runner', 'director', 'Ridley Scott', 'year', '1982']]]
|
177
188
|
assert(@redisearch_client.add_docs(docs))
|
178
|
-
assert_equal(1, @redisearch_client.delete_by_id('id_1'))
|
189
|
+
assert_equal(1, @redisearch_client.delete_by_id('id_1', { dd: false }))
|
190
|
+
assert_equal(1, @redisearch_client.delete_by_id('id_2', { dd: true }))
|
179
191
|
assert_empty(@redisearch_client.search('@title:lost'))
|
180
192
|
assert(@redisearch_client.get_by_id('id_1').any?)
|
193
|
+
assert(@redisearch_client.get_by_id('id_2').empty?)
|
181
194
|
assert(@redis_client.del('id_1'))
|
182
195
|
assert(@redisearch_client.get_by_id('id_1').empty?)
|
183
196
|
end
|
197
|
+
|
198
|
+
def test_auto_complete_suggestions
|
199
|
+
dict_name = 'test_suggestions'
|
200
|
+
@redisearch_client.autocomplete_add(dict_name, 'foobar', 2.0)
|
201
|
+
@redisearch_client.autocomplete_add(dict_name, 'foowoz', 1.0)
|
202
|
+
@redisearch_client.autocomplete_add(dict_name, 'foomeh', 0.85)
|
203
|
+
@redisearch_client.autocomplete_add(dict_name, 'woowoz', 1.0)
|
204
|
+
assert_equal(4, @redisearch_client.autocomplete_len(dict_name))
|
205
|
+
results = @redisearch_client.autocomplete_get(dict_name, 'foo')
|
206
|
+
assert_equal(3, results.count)
|
207
|
+
assert_equal('foobar', results.first)
|
208
|
+
assert_equal(4, @redisearch_client.autocomplete_get(dict_name, 'foo', { fuzzy: true }).count)
|
209
|
+
assert @redisearch_client.autocomplete_del(dict_name, 'foobar').to_i > 0
|
210
|
+
refute @redisearch_client.autocomplete_del(dict_name, 'foozzz').to_i > 0
|
211
|
+
assert_equal('foowoz', @redisearch_client.autocomplete_get(dict_name, 'foo').first)
|
212
|
+
end
|
184
213
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redisearch-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Ruiz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
A simple Ruby client library for RediSearch.
|