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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1184cc3821b5bee90dcb18d41ad3b6ea9a93b750
4
- data.tar.gz: 11ac50692f0cfc629e4283baa588e20cba18d854
3
+ metadata.gz: ea52a309430c3f6fbed50c64e43e8a6158606584
4
+ data.tar.gz: 0bcfe47c805b649f14cefed4772526f7004f2173
5
5
  SHA512:
6
- metadata.gz: c0e51193c7b3d7989f973472e5cdc4335f3ed428fa73c56ddbefa271023c053c85107639fc6f05bf0dee00820e4eb729cea3782ab7f0d62cb197d3587e7da624
7
- data.tar.gz: ab05d37fe0469216d05dff6fa0223dd7530832b0a945226352a67a270a2e5239376111418793b7ccff8c2245b68916d46cc71fcba486c8a3f9b48c96d9b30904
6
+ metadata.gz: c3d38b1b71012fa9acd1bb7d14a7e71db16c7d840a3ec934f93f1167b5fb4e86440fc6be916ab17bfe77757b1f4617d2f25d92b490ef3eeed734177edf18605a
7
+ data.tar.gz: 2f80306e5c404618a47ed90aee72c42e77ee343559f2ce54539ef4f74e61d575f3a851761c22ef6520994bde9a04d9ff84269da2da0560efa4d98f0f23d26666
@@ -14,7 +14,7 @@ before_install:
14
14
  - git clone --depth 1 https://github.com/RedisLabsModules/RediSearch.git
15
15
  - cd RediSearch
16
16
  - git fetch && git fetch --tags
17
- - git checkout v1.0.4
17
+ - git checkout v1.2.0
18
18
  - make all
19
19
  - cd ..
20
20
 
@@ -12,15 +12,24 @@ class RediSearch
12
12
  #
13
13
  # { verbatim: true, withscores: true, withsortkey: false }
14
14
 
15
- CREATE_OPTIONS_FLAGS = [:nooffsets, :nofreqs, :nohl, :nofields]
16
- ADD_OPTIONS_FLAGS = [:nosave, :replace, :partial]
17
- SEARCH_OPTIONS_FLAGS = [:nocontent, :verbatim, :nostopwords, :withscores, :withsortkeys]
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
- CREATE_OPTIONS_PARAMS = [:stopwords]
22
- ADD_OPTIONS_PARAMS = [:language]
23
- SEARCH_OPTIONS_PARAMS = [:filter, :return, :infields, :inkeys, :slop, :scorer, :sortby, :limit]
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 returned by the query
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
- # Execute arbitrary command. Only RediSearch commands are allowed
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&.first} is not a RediSearch command") unless valid_command?(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 , *create_options(opts), 'SCHEMA', *schema]
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, *add_options(opts), 'FIELDS', *fields]
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, *add_options(opts)]
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, *search_options(opts)].flatten
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 create_options(opts = {})
200
- build_options(opts, CREATE_OPTIONS_FLAGS, [])
268
+ def ft_sugdel(dict_name, content)
269
+ ['FT.SUGDEL', dict_name , content]
201
270
  end
202
271
 
203
- def add_options(opts = {})
204
- build_options(opts, ADD_OPTIONS_FLAGS, ADD_OPTIONS_PARAMS)
272
+ def ft_suglen(dict_name)
273
+ ['FT.SUGLEN', dict_name]
205
274
  end
206
275
 
207
- def search_options(opts = {})
208
- build_options(opts, SEARCH_OPTIONS_FLAGS, SEARCH_OPTIONS_PARAMS)
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 build_options(opts, flags_keys, params_keys)
212
- flags_keys.map do |key|
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
- params_keys.map do |key|
216
- [key.to_s.upcase, *opts[key]] unless opts[key].nil?
217
- end.compact
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 = score_offset + 1
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
- command[0] =~ /^ft\./i
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
@@ -1,3 +1,3 @@
1
1
  class RediSearch
2
- VERSION = '0.2.0'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -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
- assert(@redisearch_client.drop_index)
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.2.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-01-15 00:00:00.000000000 Z
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.