redisearch-rb 0.1.7 → 1.1.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
- SHA1:
3
- metadata.gz: 989e86efdc440fc616ee1741256c81b829ca69b0
4
- data.tar.gz: 2f2d59a65e355750b045f317dc7e6619d120bb0b
2
+ SHA256:
3
+ metadata.gz: 99d803efbd160ce83d3cf58a50b2e739540f143666dc87f18b04c3ff10766577
4
+ data.tar.gz: d83a8a81049e5b6266130f351d2c82381b12abaf93c1f616e7d602b2503d5abb
5
5
  SHA512:
6
- metadata.gz: 3b61fc7f81b2d86c33142c3c01a3b2b79647b5d40c0f5cfdca094ac9710104c439e153f8c8ab68a29503c97cbedc64a7f440824e02521051f7b0a304a59c25e4
7
- data.tar.gz: 9208b2b791c07ce5a0dc7b700e9cefc99030a255444500a5bc99103a053b6af167baa17a14120cd7111c8b55a6804bb65ad81e393406a6e642a4a28a6f2f0253
6
+ metadata.gz: abf6a993f32a3c93979763b3cf22e72a959fffd3d14c8ccf2fa44031b5f1c65a30ddad8715c1226ff7467f2b3ad4146be3edc806248073ec5e6926f75fc233fe
7
+ data.tar.gz: 524ed525611003f0ef515d2eee40ab4a4fcdef851083fa75b053011c1219187f1c2d1ff22c7d3fb6e456b7c53e6a0af42d12f9f6b32701bd91f0ea9a4d8be568
@@ -7,12 +7,14 @@ before_install:
7
7
  - gem install bundler -v 1.14.6
8
8
  - git clone --depth 10 https://github.com/antirez/redis.git
9
9
  - cd redis
10
- - git fetch origin 4.0.1
11
- - git checkout ae43e164d4b09d7753e27bb7cba6f7f4f85326b3
10
+ - git fetch && git fetch --tags
11
+ - git checkout 4.0.6
12
12
  - make
13
13
  - cd ..
14
14
  - git clone --depth 1 https://github.com/RedisLabsModules/RediSearch.git
15
- - cd RediSearch/src
15
+ - cd RediSearch
16
+ - git fetch && git fetch --tags
17
+ - git checkout v1.2.0
16
18
  - make all
17
- - ls -la *.so
18
19
  - cd ..
20
+
data/Gemfile CHANGED
@@ -1,11 +1,11 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ruby '2.4.1'
4
-
5
- gem 'redis', '~> 4.0.0.rc1'
3
+ ruby '>=2.4.1'
6
4
 
5
+ gem 'redis', '~> 4.0.1'
7
6
  group :development, :test do
8
7
  gem 'dotenv'
8
+ gem 'rake'
9
9
  gem 'subprocess'
10
10
  gem 'minitest', '~> 5.0'
11
11
  end
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- [![Gem Version](https://badge.fury.io/rb/redisearch-rb.svg)](https://badge.fury.io/rb/redisearch-rb) ![travis-ci](https://travis-ci.org/vruizext/redisearch-rb.svg?branch=master)
1
+ [![Gem Version](https://badge.fury.io/rb/redisearch-rb.svg)](https://badge.fury.io/rb/redisearch-rb)
2
+ [![travis-ci](https://travis-ci.org/vruizext/redisearch-rb.svg?branch=master)](https://travis-ci.org/vruizext/redisearch-rb)
2
3
 
3
4
  # redisearch-rb
4
5
 
@@ -10,8 +11,8 @@ http://redisearch.io/
10
11
 
11
12
  First of all, you need to install RediSearch, if you haven't yet:
12
13
 
13
- 1. Install Redis 4.0.1 https://github.com/antirez/redis/releases/tag/4.0.1
14
- 2. Install RediSearch http://redisearch.io/Quick_Start/
14
+ 1. Install Redis 4.0.1 or highger https://github.com/antirez/redis/releases/tag/4.0.1
15
+ 2. Install RediSearch 1.2.0 or higher http://redisearch.io/Quick_Start/
15
16
  3. Edit your `redis.conf` file and add a `loadmodule` directive to load the RediSearch module built in the step 2.
16
17
 
17
18
  To install this gem, add this line to your application's Gemfile:
@@ -43,7 +44,7 @@ In order to run the tests, it's necessary to set some env variables (see .env.ex
43
44
  ```ruby
44
45
  require 'redisearch-rb'
45
46
 
46
- redis = Redis.new(REDIS_URL)
47
+ redis = Redis.new(url: REDIS_URL)
47
48
  redisearch_client = RediSearch.new('test_idx', redis)
48
49
 
49
50
  schema = ['title', 'TEXT', 'WEIGHT', '2.0',
@@ -12,14 +12,26 @@ class RediSearch
12
12
  #
13
13
  # { verbatim: true, withscores: true, withsortkey: false }
14
14
 
15
- CREATE_OPTIONS_FLAGS = [:nooffsets, :nofreqs, :noscoreidx, :nofields]
16
- ADD_OPTIONS_FLAGS = [:nosave, :replace]
17
- SEARCH_OPTIONS_FLAGS = [:nocontent, :verbatim, :nostopwords, :withscores, :withsortkeys]
15
+ OPTIONS_FLAGS = {
16
+ add: [:nosave, :replace, :partial],
17
+ addhash: [:replace],
18
+ create: [:nooffsets, :nofreqs, :nohl, :nofields],
19
+ del: [:dd],
20
+ drop: [:keepdocs],
21
+ search: [:nocontent, :verbatim, :nostopwords, :withscores, :withsortkeys, :highlight, :summarize],
22
+ sugadd: [:incr],
23
+ sugget: [:fuzzy, :withscores],
24
+ }
18
25
 
19
26
  # Params options need an array with the values for the option
20
27
  # { limit: ['0', '50'], sortby: ['year', 'desc'], return: ['2', 'title', 'year'] }
21
- ADD_OPTIONS_PARAMS = [:language]
22
- SEARCH_OPTIONS_PARAMS = [:filter, :return, :infields, :inkeys, :slop, :scorer, :sortby, :limit]
28
+ OPTIONS_PARAMS = {
29
+ add: [:language, :payload],
30
+ addhash: [:language],
31
+ create: [:stopwords],
32
+ search: [:filter, :return, :infields, :inkeys, :slop, :scorer, :sortby, :limit, :payload],
33
+ sugget: [:max],
34
+ }
23
35
 
24
36
  # Create RediSearch client instance
25
37
  #
@@ -36,11 +48,11 @@ class RediSearch
36
48
 
37
49
  # Create new index with the given `schema`
38
50
  # @param [Array] schema
39
- #
51
+ # @param [Hash] opts options
40
52
  # Example:
41
53
  #
42
54
  # redisearch = RediSearch.new('my_idx')
43
- # redisearch.create_idx(['title', 'TEXT', 'WEIGHT', '2.0', 'director', 'TEXT', 'WEIGHT', '1.0'])
55
+ # redisearch.create_idx(['title', 'TEXT', 'WEIGHT', '2.0', 'director', 'TEXT', 'WEIGHT', '1.0', 'year', 'NUMERIC', 'SORTABLE'])
44
56
  #
45
57
  # See http://redisearch.io/Commands/#ftcreate
46
58
  #
@@ -53,14 +65,15 @@ class RediSearch
53
65
  #
54
66
  # See http://redisearch.io/Commands/#ftdrop
55
67
  # @return [String] "OK" on success
56
- def drop_index
57
- call(ft_drop)
68
+ def drop_index(opts = {})
69
+ call(ft_drop(opts))
58
70
  end
59
71
 
60
72
  # Add a single doc to the index, with the given `doc_id` and `fields`
61
73
  #
62
74
  # @param [String] doc_id id assigned to the document
63
75
  # @param [Array] fields name-value pairs to be indexed
76
+ # @param [Float] weight asigned by the user to the document
64
77
  # @param [Hash] opts optional parameters
65
78
  # Example:
66
79
  #
@@ -69,8 +82,8 @@ class RediSearch
69
82
  #
70
83
  # See http://redisearch.io/Commands/#ftadd
71
84
  # @return [String] "OK" on success
72
- def add_doc(doc_id, fields, opts = {})
73
- call(ft_add(doc_id, fields, opts))
85
+ def add_doc(doc_id, fields, opts = {}, weight = nil)
86
+ call(ft_add(doc_id, fields, opts, weight))
74
87
  end
75
88
 
76
89
  # Add a set of docs to the index. Uses redis `multi` to make a single bulk insert.
@@ -80,23 +93,58 @@ class RediSearch
80
93
  # Example:
81
94
  #
82
95
  # redisearch = RediSearch.new('my_idx')
83
- # docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola'],
84
- # ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland']]
96
+ # docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola'], 0.75],
97
+ # ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland'], 0.95]
85
98
  # redisearch.add_docs(docs)
86
99
  #
87
100
  # See http://redisearch.io/Commands/#ftadd
88
101
  # @return [String] "OK" on success
89
102
  def add_docs(docs, opts = {})
90
- multi { docs.each { |doc_id, fields| @redis.call(ft_add(doc_id, fields, opts)) } }
103
+ docs.map { |doc_id, fields, weight| call(ft_add(doc_id, fields, opts, weight))}
104
+ end
105
+
106
+ # Adds a document to the index from an existing HASH key `doc_id` in Redis.
107
+ #
108
+ # @param [String] doc_id HASH key holding the fields that need to be indexed
109
+ # @param [Hash] opts optional parameters
110
+ # @param [Float]weight asigned by the user to the document
111
+ # Example:
112
+ #
113
+ # redisearch = RediSearch.new('my_idx')
114
+ # redisearch.add_hash('id_1', { weight: true })
115
+ #
116
+ # See http://redisearch.io/Commands/#ftadd
117
+ # @return [String] "OK" on success
118
+ def add_hash(doc_id, opts = {}, weight = nil)
119
+ call(ft_addhash(doc_id, opts, weight))
91
120
  end
92
121
 
93
122
  # Search the index with the given `query`
94
123
  # @param [String] query text query, see syntax here http://redisearch.io/Query_Syntax/
95
124
  # @param [Hash] opts options for the query
96
125
  #
97
- #@return [Array] documents matching the query
126
+ # @return [Array] documents matching the query
98
127
  def search(query, opts = {})
99
- results_to_hash(call(ft_search(query, opts)), opts)
128
+ build_docs(call(ft_search(query, opts)), opts)
129
+ end
130
+
131
+ # Fetch the contents of multiple documents
132
+ #
133
+ # @param [Array] doc_ids ids assigned to the document
134
+ # @return [Array] documents found for the ids given
135
+ def get_by_ids(doc_ids)
136
+ call(ft_mget(doc_ids)).map.with_index do |doc, i|
137
+ { 'id' => doc_ids[i] }.merge(Hash[*doc]) unless doc.empty?
138
+ end.compact
139
+ end
140
+
141
+ # Fetch a document by id
142
+ #
143
+ # @param [String] doc_id id assigned to the document
144
+ # @return [Hash] Hash containing document
145
+ def get_by_id(doc_id)
146
+ Hash[*call(ft_get(doc_id))]
147
+ .tap { |doc| doc['id'] = doc_id unless doc.empty? } || {}
100
148
  end
101
149
 
102
150
  # Return information and statistics on the index.
@@ -106,14 +154,92 @@ class RediSearch
106
154
  Hash[*call(ft_info)]
107
155
  end
108
156
 
109
- private
157
+ # Deletes a document from the index
158
+ #
159
+ # See http://redisearch.io/Commands/#ftdel
160
+ #
161
+ # @param [String] doc_id id assigned to the document
162
+ # @return [int] 1 if the document was in the index, or 0 if not.
163
+ def delete_by_id(doc_id, opts = {})
164
+ call(ft_del(doc_id, opts))
165
+ end
110
166
 
111
- def multi
112
- @redis.with_reconnect { @redis.multi { yield } }
167
+ # Deletes all documents matching the query
168
+ #
169
+ # @param [String] query in the same format as used in `search`
170
+ # @param [Hash] opts options for the query, same as in `search`
171
+ # @return [int] count of documents deleted
172
+ def delete_by_query(query, opts = {})
173
+ call(ft_search(query, opts.merge(nocontent: true)))[1..-1].map do |doc_id|
174
+ call(ft_del(doc_id, opts))
175
+ end.sum
176
+ end
177
+
178
+ # Adds a string to an auto-complete suggestion dictionary.
179
+ #
180
+ # See https://oss.redislabs.com/redisearch/Commands/#ftsugadd
181
+ #
182
+ # @param [String] dict_name the key used to store the dictionary
183
+ # @param [String] content the string that is going to be indexed
184
+ # @param [Hash] opts optional parameters
185
+ # @return [int] current size of the dictionary
186
+ def autocomplete_add(dict_name, content, score = 1.0, opts = {})
187
+ call(ft_sugadd(dict_name, content, score, opts))
188
+ end
189
+
190
+ # Gets completion suggestions for a prefix.
191
+ #
192
+ # See https://oss.redislabs.com/redisearch/Commands/#ftsugadd
193
+ #
194
+ # @param [String] dict_name the key used to store the dictionary
195
+ # @param [String] prefix the prefix to search / complete
196
+ # @param [Hash] opts optional parameters
197
+ # @return [Array] a list of the top suggestions matching the prefix,
198
+ # optionally with score after each entry
199
+ def autocomplete_get(dict_name, prefix, opts = {})
200
+ call(ft_sugget(dict_name, prefix, opts))
201
+ end
202
+
203
+ # Deletes a string from an auto-complete suggestion dictionary.
204
+ #
205
+ # See https://oss.redislabs.com/redisearch/Commands/#ftsugdel
206
+ #
207
+ # @param [String] dict_name the key used to store the dictionary
208
+ # @param [String] content the string that is going to be deleted
209
+ # @param [Hash] opts optional parameters
210
+ # @return [int] 1 if the string was found and deleted, 0 otherwise
211
+ def autocomplete_del(dict_name, content)
212
+ call(ft_sugdel(dict_name, content))
213
+ end
214
+
215
+ # Gets the current size of an auto-complete suggestion dictionary.
216
+ #
217
+ # See https://oss.redislabs.com/redisearch/Commands/#ftsugdel
218
+ #
219
+ # @param [String] dict_name the key used to store the dictionary
220
+ # @return [int] current size of the dictionary
221
+ def autocomplete_len(dict_name)
222
+ call(ft_suglen(dict_name))
113
223
  end
114
224
 
225
+ # Execute arbitrary command in redisearch index
226
+ # Only RediSearch commands are allowed
227
+ #
228
+ # @param [Array] command
229
+ # @return [mixed] The output returned by redis
115
230
  def call(command)
116
- @redis.with_reconnect { @redis.call(command) }
231
+ raise ArgumentError.new("unknown/unsupported command '#{command.first}'") unless valid_command?(command.first)
232
+ @redis.with_reconnect { @redis.call(command.flatten) }
233
+ end
234
+
235
+ private
236
+
237
+ def with_reconnect
238
+ @redis.with_reconnect { yield }
239
+ end
240
+
241
+ def multi
242
+ @redis.with_reconnect { @redis.multi { yield } }
117
243
  end
118
244
 
119
245
  def add(doc_id, fields)
@@ -121,11 +247,11 @@ class RediSearch
121
247
  end
122
248
 
123
249
  def ft_create(schema, opts)
124
- ['FT.CREATE', @idx_name , *create_options(opts), 'SCHEMA', *schema]
250
+ ['FT.CREATE', @idx_name , *serialize_options(opts, :create), 'SCHEMA', *schema]
125
251
  end
126
252
 
127
- def ft_drop
128
- ['FT.DROP', @idx_name]
253
+ def ft_drop(opts)
254
+ ['FT.DROP', @idx_name, *serialize_options(opts, :drop)]
129
255
  end
130
256
 
131
257
  def ft_info
@@ -133,45 +259,87 @@ class RediSearch
133
259
  end
134
260
 
135
261
  def ft_add(doc_id, fields, opts = {}, weight = nil)
136
- ['FT.ADD', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *add_options(opts), 'FIELDS', *fields]
262
+ ['FT.ADD', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *serialize_options(opts, :add), 'FIELDS', *fields]
263
+ end
264
+
265
+ def ft_addhash(doc_id, opts = {}, weight = nil)
266
+ ['FT.ADDHASH', @idx_name , doc_id, weight || DEFAULT_WEIGHT, *serialize_options(opts, :add)]
137
267
  end
138
268
 
139
269
  def ft_search(query, opts)
140
- ['FT.SEARCH', @idx_name, *query, *search_options(opts)].flatten
270
+ ['FT.SEARCH', @idx_name, *query, *serialize_options(opts, :search)].flatten
141
271
  end
142
272
 
143
- def create_options(opts = {})
144
- build_options(opts, CREATE_OPTIONS_FLAGS, [])
273
+ def ft_get(doc_id)
274
+ ['FT.GET', @idx_name , doc_id]
145
275
  end
146
276
 
147
- def add_options(opts = {})
148
- build_options(opts, ADD_OPTIONS_FLAGS, ADD_OPTIONS_PARAMS)
277
+ def ft_mget(doc_ids)
278
+ ['FT.MGET', @idx_name , *doc_ids]
149
279
  end
150
280
 
151
- def search_options(opts = {})
152
- build_options(opts, SEARCH_OPTIONS_FLAGS, SEARCH_OPTIONS_PARAMS)
281
+ def ft_del(doc_id, opts)
282
+ ['FT.DEL', @idx_name , doc_id, *serialize_options(opts, :del)]
153
283
  end
154
284
 
155
- def build_options(opts, flags_keys, params_keys)
156
- flags_keys.map do |key|
285
+ def ft_tagvals(field_name)
286
+ ['FT.TAGVALS', @idx_name , field_name]
287
+ end
288
+
289
+ def ft_explain(query, opts)
290
+ ['FT.EXPLAIN', @idx_name, *query, *serialize_options(opts, :search)].flatten
291
+ end
292
+
293
+ def ft_sugadd(dict_name, content, score, opts)
294
+ ['FT.SUGADD', dict_name , content, score, *serialize_options(opts, :sugadd)]
295
+ end
296
+
297
+ def ft_sugdel(dict_name, content)
298
+ ['FT.SUGDEL', dict_name , content]
299
+ end
300
+
301
+ def ft_suglen(dict_name)
302
+ ['FT.SUGLEN', dict_name]
303
+ end
304
+
305
+ def ft_sugget(dict_name, prefix,opts)
306
+ ['FT.SUGGET', dict_name , prefix, *serialize_options(opts, :sugget)]
307
+ end
308
+
309
+ def serialize_options(opts, method)
310
+ [flags_for_method(opts, method), params_for_method(opts, method)].flatten.compact
311
+ end
312
+
313
+ def flags_for_method(opts, method)
314
+ OPTIONS_FLAGS[method].to_a.map do |key|
157
315
  key.to_s.upcase if opts[key]
158
- end.compact +
159
- params_keys.map do |key|
316
+ end.compact
317
+ end
318
+
319
+ def params_for_method(opts, method)
320
+ OPTIONS_PARAMS[method].to_a.map do |key|
160
321
  [key.to_s.upcase, *opts[key]] unless opts[key].nil?
161
322
  end.compact
162
323
  end
163
324
 
164
- def results_to_hash(results, opts = {})
165
- return {} if results.nil? || results[0] == 0
325
+ def build_docs(results, opts = {})
326
+ return [] if results.nil? || results[0] == 0
166
327
  results.shift
167
- offset = opts[:withscores] ? 1 : 0
168
- rows_per_doc = 2 + offset
169
- nr_of_docs = results.size / (2 + offset)
328
+ score_offset = opts[:withscores] ? 1 : 0
329
+ content_offset = opts[:nocontent] ? 0 : 1
330
+ rows_per_doc = 1 + content_offset + score_offset
331
+ nr_of_docs = results.size / rows_per_doc
170
332
  (0..nr_of_docs-1).map do |n|
171
- doc = Hash[*results[rows_per_doc * n + 1 + offset]]
172
- doc['score'] = results[rows_per_doc * n + offset] if offset > 0
333
+ doc = opts[:nocontent] ? {} : Hash[*results[rows_per_doc * n + content_offset + score_offset]]
334
+ doc['score'] = results[rows_per_doc * n + score_offset] if opts[:withscores]
173
335
  doc['id'] = results[rows_per_doc * n]
174
336
  doc
175
337
  end
176
338
  end
339
+
340
+ def valid_command?(command)
341
+ %w(FT.CREATE FT.ADD FT.ADDHASH FT.SEARCH FT.DEL FT.DROP FT.GET FT.MGET
342
+ FT.SUGADD FT.SUGGET FT.SUGDEL FT.SUGLEN FT.SYNADD FT.SYNUPDATE FT.SYNDUMP
343
+ FT.INFO FT.AGGREGATE FT.EXPLAIN FT.TAGVALS).include?(command)
344
+ end
177
345
  end
@@ -1,3 +1,3 @@
1
1
  class RediSearch
2
- VERSION = '0.1.7'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  RediSearch is a Redis powered search engine, developed by RedisLabs. http://redisearch.io/"
13
13
  spec.homepage = "https://github.com/vruizext/redisearch-rb"
14
14
  spec.license = "MIT"
15
-
15
+ spec.required_ruby_version = '> 2.4.0'
16
16
  spec.files = `git ls-files`.split("\n")
17
17
  spec.test_files = `git ls-files -- {test}/*`.split("\n")
18
18
  end
@@ -10,6 +10,7 @@ class RediSearchTest < Minitest::Test
10
10
  @redisearch_client = RediSearch.new('test_idx', @redis_client)
11
11
  @schema = ['title', 'TEXT', 'WEIGHT', '2.0',
12
12
  'director', 'TEXT', 'WEIGHT', '1.0',
13
+ 'genre', 'TAG',
13
14
  'year', 'NUMERIC', 'SORTABLE']
14
15
 
15
16
  end
@@ -36,8 +37,19 @@ class RediSearchTest < Minitest::Test
36
37
 
37
38
  def test_drop_idx
38
39
  assert(@redisearch_client.create_index(@schema))
39
- 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
40
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?)
41
53
  end
42
54
 
43
55
  def test_add_doc
@@ -72,6 +84,36 @@ class RediSearchTest < Minitest::Test
72
84
  assert_includes(search_result.to_s, 'Ex Machina')
73
85
  end
74
86
 
87
+ def test_add_hash
88
+ assert(@redisearch_client.create_index(@schema))
89
+ doc = ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']
90
+ @redis_client.hmset('id_1', *doc)
91
+ assert(@redisearch_client.add_hash('id_1'))
92
+ assert_includes(@redis_client.call(['FT.SEARCH', 'test_idx', '@title:lost']).to_s, 'Lost in translation')
93
+ end
94
+
95
+ def test_get_by_id
96
+ assert(@redisearch_client.create_index(@schema))
97
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
98
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
99
+ assert(@redisearch_client.add_docs(docs))
100
+ doc = @redisearch_client.get_by_id('id_1')
101
+ assert_equal('id_1', doc['id'])
102
+ assert_equal('Lost in translation', doc['title'])
103
+ end
104
+
105
+ def test_get_by_ids
106
+ assert(@redisearch_client.create_index(@schema))
107
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
108
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']]]
109
+ assert(@redisearch_client.add_docs(docs))
110
+ docs = @redisearch_client.get_by_ids(['id_1', 'id_2'])
111
+ assert_equal('id_1', docs[0]['id'])
112
+ assert_equal('Lost in translation', docs[0]['title'])
113
+ assert_equal('id_2', docs[1]['id'])
114
+ assert_equal('Ex Machina', docs[1]['title'])
115
+ end
116
+
75
117
  def test_search_simple_query
76
118
  assert(@redisearch_client.create_index(@schema))
77
119
  docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
@@ -82,6 +124,7 @@ class RediSearchTest < Minitest::Test
82
124
  assert matches.any? { |doc| 'Lost in translation' == doc['title'] }
83
125
  assert matches.any? { |doc| 'Ex Machina' == doc['title'] }
84
126
  matches.each { |doc| assert doc['score'].to_i > 0 }
127
+ assert_equal([], @redisearch_client.search('foobarwozzz'))
85
128
  end
86
129
 
87
130
  def test_search_field_selector
@@ -146,6 +189,40 @@ class RediSearchTest < Minitest::Test
146
189
  assert_equal 'id_2', matches[0]['id']
147
190
  end
148
191
 
192
+ def test_search_summarize
193
+ assert(@redisearch_client.create_index(@schema))
194
+ long_director = 'Steven ' * 80 + 'Sofia Coppola' + ' ' + 'Steven ' * 80
195
+ docs = [['id_1', ['title', 'Lost in translation', 'director', long_director, 'year', '2004']],
196
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']],
197
+ ['id_3', ['title', 'Terminator', 'director', 'James Cameron', 'year', '1984']],
198
+ ['id_4', ['title', 'Blade Runner', 'director', 'Ridley Scott', 'year', '1982']]]
199
+ assert(@redisearch_client.add_docs(docs))
200
+ matches = @redisearch_client.search('Sofia', summarize: false)
201
+ assert_equal 1, matches.count
202
+ assert_equal long_director.size, matches[0]['director'].size
203
+
204
+ matches = @redisearch_client.search('Sofia', summarize: true)
205
+ assert_equal 1, matches.count
206
+ assert_in_epsilon 122, matches[0]['director'].size, 20
207
+ end
208
+
209
+ def test_search_highlight
210
+ assert(@redisearch_client.create_index(@schema))
211
+ long_director = 'Steven ' * 80 + 'Sofia Coppola' + ' ' + 'Steven ' * 80
212
+ docs = [['id_1', ['title', 'Lost in translation', 'director', long_director, 'year', '2004']],
213
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']],
214
+ ['id_3', ['title', 'Terminator', 'director', 'James Cameron', 'year', '1984']],
215
+ ['id_4', ['title', 'Blade Runner', 'director', 'Ridley Scott', 'year', '1982']]]
216
+ assert(@redisearch_client.add_docs(docs))
217
+ matches = @redisearch_client.search('Sofia', highlight: false)
218
+ assert_equal 1, matches.count
219
+ assert_match(/^[^<]+$/, matches[0]['director'])
220
+
221
+ matches = @redisearch_client.search('Sofia', highlight: true)
222
+ assert_equal 1, matches.count
223
+ assert_match(/<b>/, matches[0]['director'])
224
+ end
225
+
149
226
  def test_index_info
150
227
  assert(@redisearch_client.create_index(@schema))
151
228
  doc = ['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']]
@@ -156,4 +233,36 @@ class RediSearchTest < Minitest::Test
156
233
  assert_equal 1, info['max_doc_id'].to_i
157
234
  assert_equal 5, info['num_terms'].to_i
158
235
  end
236
+
237
+ def test_delete_by_id
238
+ assert(@redisearch_client.create_index(@schema))
239
+ docs = [['id_1', ['title', 'Lost in translation', 'director', 'Sofia Coppola', 'year', '2004']],
240
+ ['id_2', ['title', 'Ex Machina', 'director', 'Alex Garland', 'year', '2014']],
241
+ ['id_3', ['title', 'Terminator', 'director', 'James Cameron', 'year', '1984']],
242
+ ['id_4', ['title', 'Blade Runner', 'director', 'Ridley Scott', 'year', '1982']]]
243
+ assert(@redisearch_client.add_docs(docs))
244
+ assert_equal(1, @redisearch_client.delete_by_id('id_1', { dd: false }))
245
+ assert_equal(1, @redisearch_client.delete_by_id('id_2', { dd: true }))
246
+ assert_empty(@redisearch_client.search('@title:lost'))
247
+ assert(@redisearch_client.get_by_id('id_1').any?)
248
+ assert(@redisearch_client.get_by_id('id_2').empty?)
249
+ assert(@redis_client.del('id_1'))
250
+ assert(@redisearch_client.get_by_id('id_1').empty?)
251
+ end
252
+
253
+ def test_auto_complete_suggestions
254
+ dict_name = 'test_suggestions'
255
+ @redisearch_client.autocomplete_add(dict_name, 'foobar', 2.0)
256
+ @redisearch_client.autocomplete_add(dict_name, 'foowoz', 1.0)
257
+ @redisearch_client.autocomplete_add(dict_name, 'foomeh', 0.85)
258
+ @redisearch_client.autocomplete_add(dict_name, 'woowoz', 1.0)
259
+ assert_equal(4, @redisearch_client.autocomplete_len(dict_name))
260
+ results = @redisearch_client.autocomplete_get(dict_name, 'foo')
261
+ assert_equal(3, results.count)
262
+ assert_equal('foobar', results.first)
263
+ assert_equal(4, @redisearch_client.autocomplete_get(dict_name, 'foo', { fuzzy: true }).count)
264
+ assert @redisearch_client.autocomplete_del(dict_name, 'foobar').to_i > 0
265
+ refute @redisearch_client.autocomplete_del(dict_name, 'foozzz').to_i > 0
266
+ assert_equal('foowoz', @redisearch_client.autocomplete_get(dict_name, 'foo').first)
267
+ end
159
268
  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.1.7
4
+ version: 1.1.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: 2017-08-08 00:00:00.000000000 Z
11
+ date: 2020-11-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  A simple Ruby client library for RediSearch.
@@ -43,17 +43,16 @@ require_paths:
43
43
  - lib
44
44
  required_ruby_version: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - ">="
46
+ - - ">"
47
47
  - !ruby/object:Gem::Version
48
- version: '0'
48
+ version: 2.4.0
49
49
  required_rubygems_version: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  requirements: []
55
- rubyforge_project:
56
- rubygems_version: 2.6.8
55
+ rubygems_version: 3.0.3
57
56
  signing_key:
58
57
  specification_version: 4
59
58
  summary: A simple Ruby client library for RediSearch