algolia 2.0.0.pre.alpha.2

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.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +146 -0
  3. data/.github/ISSUE_TEMPLATE.md +20 -0
  4. data/.github/PULL_REQUEST_TEMPLATE.md +22 -0
  5. data/.gitignore +38 -0
  6. data/.rubocop.yml +186 -0
  7. data/.rubocop_todo.yml +14 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +18 -0
  10. data/LICENSE +21 -0
  11. data/README.md +56 -0
  12. data/Rakefile +45 -0
  13. data/Steepfile +6 -0
  14. data/algolia.gemspec +41 -0
  15. data/bin/console +21 -0
  16. data/bin/setup +8 -0
  17. data/lib/algolia.rb +42 -0
  18. data/lib/algolia/account_client.rb +65 -0
  19. data/lib/algolia/analytics_client.rb +105 -0
  20. data/lib/algolia/config/algolia_config.rb +40 -0
  21. data/lib/algolia/config/analytics_config.rb +20 -0
  22. data/lib/algolia/config/insights_config.rb +20 -0
  23. data/lib/algolia/config/recommendation_config.rb +20 -0
  24. data/lib/algolia/config/search_config.rb +40 -0
  25. data/lib/algolia/defaults.rb +35 -0
  26. data/lib/algolia/enums/call_type.rb +4 -0
  27. data/lib/algolia/enums/retry_outcome_type.rb +5 -0
  28. data/lib/algolia/error.rb +29 -0
  29. data/lib/algolia/helpers.rb +83 -0
  30. data/lib/algolia/http/http_requester.rb +84 -0
  31. data/lib/algolia/http/response.rb +23 -0
  32. data/lib/algolia/insights_client.rb +238 -0
  33. data/lib/algolia/iterators/base_iterator.rb +19 -0
  34. data/lib/algolia/iterators/object_iterator.rb +27 -0
  35. data/lib/algolia/iterators/paginator_iterator.rb +44 -0
  36. data/lib/algolia/iterators/rule_iterator.rb +9 -0
  37. data/lib/algolia/iterators/synonym_iterator.rb +9 -0
  38. data/lib/algolia/logger_helper.rb +14 -0
  39. data/lib/algolia/recommendation_client.rb +60 -0
  40. data/lib/algolia/responses/add_api_key_response.rb +38 -0
  41. data/lib/algolia/responses/base_response.rb +9 -0
  42. data/lib/algolia/responses/delete_api_key_response.rb +40 -0
  43. data/lib/algolia/responses/indexing_response.rb +28 -0
  44. data/lib/algolia/responses/multiple_batch_indexing_response.rb +29 -0
  45. data/lib/algolia/responses/multiple_response.rb +45 -0
  46. data/lib/algolia/responses/restore_api_key_response.rb +36 -0
  47. data/lib/algolia/responses/update_api_key_response.rb +39 -0
  48. data/lib/algolia/search_client.rb +614 -0
  49. data/lib/algolia/search_index.rb +1094 -0
  50. data/lib/algolia/transport/request_options.rb +94 -0
  51. data/lib/algolia/transport/retry_strategy.rb +117 -0
  52. data/lib/algolia/transport/stateful_host.rb +26 -0
  53. data/lib/algolia/transport/transport.rb +161 -0
  54. data/lib/algolia/user_agent.rb +25 -0
  55. data/lib/algolia/version.rb +3 -0
  56. data/sig/config/algolia_config.rbs +24 -0
  57. data/sig/config/analytics_config.rbs +11 -0
  58. data/sig/config/insights_config.rbs +11 -0
  59. data/sig/config/recommendation_config.rbs +11 -0
  60. data/sig/config/search_config.rbs +11 -0
  61. data/sig/enums/call_type.rbs +5 -0
  62. data/sig/helpers.rbs +12 -0
  63. data/sig/http/http_requester.rbs +17 -0
  64. data/sig/http/response.rbs +14 -0
  65. data/sig/interfaces/_connection.rbs +16 -0
  66. data/sig/iterators/base_iterator.rbs +15 -0
  67. data/sig/iterators/object_iterator.rbs +6 -0
  68. data/sig/iterators/paginator_iterator.rbs +8 -0
  69. data/sig/iterators/rule_iterator.rbs +5 -0
  70. data/sig/iterators/synonym_iterator.rbs +5 -0
  71. data/sig/transport/request_options.rbs +33 -0
  72. data/sig/transport/stateful_host.rbs +21 -0
  73. data/test/algolia/integration/account_client_test.rb +47 -0
  74. data/test/algolia/integration/analytics_client_test.rb +113 -0
  75. data/test/algolia/integration/base_test.rb +9 -0
  76. data/test/algolia/integration/insights_client_test.rb +80 -0
  77. data/test/algolia/integration/mocks/mock_requester.rb +45 -0
  78. data/test/algolia/integration/recommendation_client_test.rb +30 -0
  79. data/test/algolia/integration/search_client_test.rb +361 -0
  80. data/test/algolia/integration/search_index_test.rb +698 -0
  81. data/test/algolia/unit/helpers_test.rb +69 -0
  82. data/test/algolia/unit/retry_strategy_test.rb +139 -0
  83. data/test/algolia/unit/user_agent_test.rb +16 -0
  84. data/test/test_helper.rb +89 -0
  85. data/upgrade_guide.md +595 -0
  86. metadata +307 -0
@@ -0,0 +1,1094 @@
1
+ module Algolia
2
+ module Search
3
+ # Class Index
4
+ class Index
5
+ include CallType
6
+ include Helpers
7
+
8
+ attr_reader :index_name, :transporter, :config
9
+
10
+ # Initialize an index
11
+ #
12
+ # @param index_name [String] name of the index
13
+ # @param transporter [Object] transport object used for the connection
14
+ # @param config [Config] a Config object which contains your APP_ID and API_KEY
15
+ #
16
+ def initialize(index_name, transporter, config)
17
+ @index_name = index_name
18
+ @transporter = transporter
19
+ @config = config
20
+ end
21
+
22
+ # # # # # # # # # # # # # # # # # # # # #
23
+ # MISC
24
+ # # # # # # # # # # # # # # # # # # # # #
25
+
26
+ # Wait the publication of a task on the server.
27
+ # All server task are asynchronous and you can check with this method that the task is published.
28
+ #
29
+ # @param task_id the id of the task returned by server
30
+ # @param time_before_retry the time in milliseconds before retry (default = 100ms)
31
+ # @param opts [Hash] contains extra parameters to send with your query
32
+ #
33
+ def wait_task(task_id, time_before_retry = Defaults::WAIT_TASK_DEFAULT_TIME_BEFORE_RETRY, opts = {})
34
+ loop do
35
+ status = get_task_status(task_id, opts)
36
+ if status == 'published'
37
+ return
38
+ end
39
+ sleep(time_before_retry / 1000)
40
+ end
41
+ end
42
+
43
+ # Check the status of a task on the server.
44
+ # All server task are asynchronous and you can check the status of a task with this method.
45
+ #
46
+ # @param task_id [Integer] the id of the task returned by server
47
+ # @param opts [Hash] contains extra parameters to send with your query
48
+ #
49
+ def get_task_status(task_id, opts = {})
50
+ res = @transporter.read(:GET, path_encode('/1/indexes/%s/task/%s', @index_name, task_id), {}, opts)
51
+ get_option(res, 'status')
52
+ end
53
+
54
+ # Delete the index content
55
+ #
56
+ # @param opts [Hash] contains extra parameters to send with your query
57
+ #
58
+ def clear_objects(opts = {})
59
+ response = @transporter.write(:POST, path_encode('/1/indexes/%s/clear', @index_name), {}, opts)
60
+
61
+ IndexingResponse.new(self, response)
62
+ end
63
+
64
+ # Delete the index content and wait for operation to complete
65
+ #
66
+ # @param opts [Hash] contains extra parameters to send with your query
67
+ #
68
+ def clear_objects!(opts = {})
69
+ response = clear_objects(opts)
70
+ response.wait(opts)
71
+ end
72
+
73
+ # Delete an existing index
74
+ #
75
+ # @param opts [Hash] contains extra parameters to send with your query
76
+ #
77
+ def delete(opts = {})
78
+ response = @transporter.write(:DELETE, path_encode('/1/indexes/%s', @index_name), opts)
79
+
80
+ IndexingResponse.new(self, response)
81
+ end
82
+
83
+ # Delete an existing index and wait for operation to complete
84
+ #
85
+ # @param opts [Hash] contains extra parameters to send with your query
86
+ #
87
+ def delete!(opts = {})
88
+ response = delete(opts)
89
+ response.wait(opts)
90
+ end
91
+
92
+ # Find object by the given condition.
93
+ #
94
+ # Options can be passed in request_options body:
95
+ # - query (string): pass a query
96
+ # - paginate (bool): choose if you want to iterate through all the
97
+ # documents (true) or only the first page (false). Default is true.
98
+ # The function takes a block to filter the results from search query
99
+ # Usage example:
100
+ # index.find_object({'query' => '', 'paginate' => true}) {|obj| obj.key?('company') and obj['company'] == 'Apple'}
101
+ #
102
+ # @param callback [Lambda] contains extra parameters to send with your query
103
+ # @param opts [Hash] contains extra parameters to send with your query
104
+ #
105
+ # @return [Hash|AlgoliaHttpError] the matching object and its position in the result set
106
+ #
107
+ def find_object(callback, opts = {})
108
+ request_options = symbolize_hash(opts)
109
+ paginate = true
110
+ page = 0
111
+
112
+ query = request_options.delete(:query) || ''
113
+ paginate = request_options.delete(:paginate) if request_options.has_key?(:paginate)
114
+
115
+ has_next_page = true
116
+ while has_next_page
117
+ request_options[:page] = page
118
+ res = search(query, request_options)
119
+
120
+ res[:hits].each_with_index do |hit, i|
121
+ if callback.call(hit)
122
+ return {
123
+ object: hit,
124
+ position: i,
125
+ page: page
126
+ }
127
+ end
128
+ end
129
+
130
+ has_next_page = page + 1 < res[:nbPages]
131
+ raise AlgoliaHttpError.new(404, 'Object not found') unless paginate && has_next_page
132
+
133
+ page += 1
134
+ end
135
+ end
136
+
137
+ # Retrieve the given object position in a set of results.
138
+ #
139
+ # @param [Array] objects the result set to browse
140
+ # @param [String] object_id the object to look for
141
+ #
142
+ # @return [Integer] position of the object, or -1 if it's not in the array
143
+ #
144
+ def self.get_object_position(objects, object_id)
145
+ hits = get_option(objects, 'hits')
146
+ hits.find_index { |hit| get_option(hit, 'objectID') == object_id } || -1
147
+ end
148
+
149
+ # Copy the current index to the given destination name
150
+ #
151
+ # @param name [String] destination index name
152
+ # @param opts [Hash] contains extra parameters to send with your query
153
+ #
154
+ # @return [IndexingResponse]
155
+ #
156
+ def copy_to(name, opts = {})
157
+ response = @transporter.write(:POST, path_encode('/1/indexes/%s/operation', @index_name), { operation: 'copy', destination: name }, opts)
158
+
159
+ IndexingResponse.new(self, response)
160
+ end
161
+
162
+ # Move the current index to the given destination name
163
+ #
164
+ # @param name [String] destination index name
165
+ # @param opts [Hash] contains extra parameters to send with your query
166
+ #
167
+ # @return [IndexingResponse]
168
+ #
169
+ def move_to(name, opts = {})
170
+ response = @transporter.write(:POST, path_encode('/1/indexes/%s/operation', @index_name), { operation: 'move', destination: name }, opts)
171
+
172
+ IndexingResponse.new(self, response)
173
+ end
174
+
175
+ # # # # # # # # # # # # # # # # # # # # #
176
+ # INDEXING
177
+ # # # # # # # # # # # # # # # # # # # # #
178
+
179
+ # Retrieve one object from the index
180
+ #
181
+ # @param object_id [String]
182
+ # @param opts [Hash] contains extra parameters to send with your query
183
+ #
184
+ # @return [Hash]
185
+ #
186
+ def get_object(object_id, opts = {})
187
+ @transporter.read(:GET, path_encode('/1/indexes/%s/%s', @index_name, object_id), {}, opts)
188
+ end
189
+
190
+ # Retrieve one or more objects in a single API call
191
+ #
192
+ # @param object_ids [Array]
193
+ # @param opts [Hash] contains extra parameters to send with your query
194
+ #
195
+ # @return [Hash]
196
+ #
197
+ def get_objects(object_ids, opts = {})
198
+ request_options = symbolize_hash(opts)
199
+ attributes_to_retrieve = get_option(request_options, 'attributesToRetrieve')
200
+ request_options.delete(:attributesToRetrieve)
201
+
202
+ requests = []
203
+ object_ids.each do |object_id|
204
+ request = { indexName: @index_name, objectID: object_id.to_s }
205
+
206
+ if attributes_to_retrieve
207
+ request[:attributesToRetrieve] = attributes_to_retrieve
208
+ end
209
+
210
+ requests.push(request)
211
+ end
212
+
213
+ @transporter.read(:POST, '/1/indexes/*/objects', { 'requests': requests }, opts)
214
+ end
215
+
216
+ # Add an object to the index
217
+ #
218
+ # @param object [Hash] the object to save
219
+ # @param opts [Hash] contains extra parameters to send with your query
220
+ #
221
+ # @return [IndexingResponse]
222
+ #
223
+ def save_object(object, opts = {})
224
+ save_objects([object], opts)
225
+ end
226
+
227
+ # Add an object to the index and wait for operation to complete
228
+ #
229
+ # @param object [Hash] the object to save
230
+ # @param opts [Hash] contains extra parameters to send with your query
231
+ #
232
+ # @return [IndexingResponse]
233
+ #
234
+ def save_object!(object, opts = {})
235
+ response = save_objects([object], opts)
236
+ response.wait(opts)
237
+ end
238
+
239
+ # Add several objects to the index
240
+ #
241
+ # @param objects [Array] the objects to save
242
+ # @param opts [Hash] contains extra parameters to send with your query
243
+ #
244
+ # @return [IndexingResponse]
245
+ #
246
+ def save_objects(objects, opts = {})
247
+ request_options = symbolize_hash(opts)
248
+ generate_object_id = request_options[:auto_generate_object_id_if_not_exist] || false
249
+ request_options.delete(:auto_generate_object_id_if_not_exist)
250
+ if generate_object_id
251
+ IndexingResponse.new(self, raw_batch(chunk('addObject', objects), request_options))
252
+ else
253
+ IndexingResponse.new(self, raw_batch(chunk('updateObject', objects, true), request_options))
254
+ end
255
+ end
256
+
257
+ # Add several objects to the index and wait for operation to complete
258
+ #
259
+ # @param objects [Array] the objects to save
260
+ # @param opts [Hash] contains extra parameters to send with your query
261
+ #
262
+ # @return [IndexingResponse]
263
+ #
264
+ def save_objects!(objects, opts = {})
265
+ response = save_objects(objects, opts)
266
+ response.wait(opts)
267
+ end
268
+
269
+ # Partially update an object
270
+ #
271
+ # @param object [String] object ID to partially update
272
+ # @param opts [Hash] contains extra parameters to send with your query
273
+ #
274
+ # @return [IndexingResponse]
275
+ #
276
+ def partial_update_object(object, opts = {})
277
+ partial_update_objects([object], opts)
278
+ end
279
+
280
+ # Partially update an object and wait for operation to complete
281
+ #
282
+ # @param object [String] object ID to partially update
283
+ # @param opts [Hash] contains extra parameters to send with your query
284
+ #
285
+ # @return [IndexingResponse]
286
+ #
287
+ def partial_update_object!(object, opts = {})
288
+ response = partial_update_objects([object], opts)
289
+ response.wait(opts)
290
+ end
291
+
292
+ # Partially update several objects
293
+ #
294
+ # @param objects [Array] array of objectIDs to partially update
295
+ # @param opts [Hash] contains extra parameters to send with your query
296
+ #
297
+ # @return [IndexingResponse]
298
+ #
299
+ def partial_update_objects(objects, opts = {})
300
+ generate_object_id = false
301
+ request_options = symbolize_hash(opts)
302
+ if get_option(request_options, 'createIfNotExists')
303
+ generate_object_id = true
304
+ request_options.delete(:createIfNotExists)
305
+ end
306
+
307
+ if generate_object_id
308
+ IndexingResponse.new(self, raw_batch(chunk('partialUpdateObject', objects), request_options))
309
+ else
310
+ IndexingResponse.new(self, raw_batch(chunk('partialUpdateObjectNoCreate', objects), request_options))
311
+ end
312
+ end
313
+
314
+ # Partially update several objects and wait for operation to complete
315
+ #
316
+ # @param objects [Array] array of objectIDs to partially update
317
+ # @param opts [Hash] contains extra parameters to send with your query
318
+ #
319
+ # @return [IndexingResponse]
320
+ #
321
+ def partial_update_objects!(objects, opts = {})
322
+ response = partial_update_objects(objects, opts)
323
+ response.wait(opts)
324
+ end
325
+
326
+ # Delete an existing object from an index
327
+ #
328
+ # @param object_id [String]
329
+ # @param opts [Hash] contains extra parameters to send with your query
330
+ #
331
+ # @return [IndexingResponse]
332
+ #
333
+ def delete_object(object_id, opts = {})
334
+ delete_objects([object_id], opts)
335
+ end
336
+
337
+ # Delete an existing object from an index and wait for operation to complete
338
+ #
339
+ # @param object_id [String]
340
+ # @param opts [Hash] contains extra parameters to send with your query
341
+ #
342
+ # @return [IndexingResponse]
343
+ #
344
+ def delete_object!(object_id, opts = {})
345
+ response = delete_objects([object_id], opts)
346
+ response.wait(opts)
347
+ end
348
+
349
+ # Delete several existing objects from an index
350
+ #
351
+ # @param object_ids [Array]
352
+ # @param opts [Hash] contains extra parameters to send with your query
353
+ #
354
+ # @return [IndexingResponse]
355
+ #
356
+ def delete_objects(object_ids, opts = {})
357
+ objects = object_ids.map do |object_id|
358
+ { objectID: object_id }
359
+ end
360
+
361
+ IndexingResponse.new(self, raw_batch(chunk('deleteObject', objects), opts))
362
+ end
363
+
364
+ # Delete several existing objects from an index and wait for operation to complete
365
+ #
366
+ # @param object_ids [Array]
367
+ # @param opts [Hash] contains extra parameters to send with your query
368
+ #
369
+ # @return [IndexingResponse]
370
+ #
371
+ def delete_objects!(object_ids, opts = {})
372
+ response = delete_objects(object_ids, opts)
373
+ response.wait(opts)
374
+ end
375
+
376
+ # Delete all records matching the query
377
+ #
378
+ # @param filters [Hash]
379
+ # @param opts [Hash] contains extra parameters to send with your query
380
+ #
381
+ # @return [IndexingResponse]
382
+ #
383
+ def delete_by(filters, opts = {})
384
+ response = @transporter.write(:POST, path_encode('/1/indexes/%s/deleteByQuery', @index_name), filters, opts)
385
+
386
+ IndexingResponse.new(self, response)
387
+ end
388
+
389
+ # Delete all records matching the query and wait for operation to complete
390
+ #
391
+ # @param filters [Hash]
392
+ # @param opts [Hash] contains extra parameters to send with your query
393
+ #
394
+ # @return [IndexingResponse]
395
+ #
396
+ def delete_by!(filters, opts = {})
397
+ response = delete_by(filters, opts)
398
+
399
+ response.wait(opts)
400
+ end
401
+
402
+ # Send a batch request
403
+ #
404
+ # @param requests [Hash] hash containing the requests to batch
405
+ # @param opts [Hash] contains extra parameters to send with your query
406
+ #
407
+ # @return [IndexingResponse]
408
+ #
409
+ def batch(requests, opts = {})
410
+ response = raw_batch(requests, opts)
411
+
412
+ IndexingResponse.new(self, response)
413
+ end
414
+
415
+ # Send a batch request and wait for operation to complete
416
+ #
417
+ # @param requests [Hash] hash containing the requests to batch
418
+ # @param opts [Hash] contains extra parameters to send with your query
419
+ #
420
+ # @return [IndexingResponse]
421
+ #
422
+ def batch!(requests, opts = {})
423
+ response = batch(requests, opts)
424
+ response.wait(opts)
425
+ end
426
+
427
+ # # # # # # # # # # # # # # # # # # # # #
428
+ # QUERY RULES
429
+ # # # # # # # # # # # # # # # # # # # # #
430
+
431
+ # Retrieve the Rule with the specified objectID
432
+ #
433
+ # @param object_id [String]
434
+ # @param opts [Hash] contains extra parameters to send with your query
435
+ #
436
+ # @return [Hash]
437
+ #
438
+ def get_rule(object_id, opts = {})
439
+ @transporter.read(:GET, path_encode('/1/indexes/%s/rules/%s', @index_name, object_id), {}, opts)
440
+ end
441
+
442
+ # Create or update a rule
443
+ #
444
+ # @param rule [Hash] a hash containing a rule objectID and different conditions/consequences
445
+ # @param opts [Hash] contains extra parameters to send with your query
446
+ #
447
+ # @return [Array, IndexingResponse]
448
+ #
449
+ def save_rule(rule, opts = {})
450
+ save_rules([rule], opts)
451
+ end
452
+
453
+ # Create or update a rule and wait for operation to complete
454
+ #
455
+ # @param rule [Hash] a hash containing a rule objectID and different conditions/consequences
456
+ # @param opts [Hash] contains extra parameters to send with your query
457
+ #
458
+ # @return [Array, IndexingResponse]
459
+ #
460
+ def save_rule!(rule, opts = {})
461
+ response = save_rules([rule], opts)
462
+ response.wait(opts)
463
+ end
464
+
465
+ # Create or update rules
466
+ #
467
+ # @param rules [Array] an array of hashes containing a rule objectID and different conditions/consequences
468
+ # @param opts [Hash] contains extra parameters to send with your query
469
+ #
470
+ # @return [Array, IndexingResponse]
471
+ #
472
+ def save_rules(rules, opts = {})
473
+ if rules.is_a?(RuleIterator)
474
+ iterated = []
475
+ rules.each do |rule|
476
+ iterated.push(rule)
477
+ end
478
+ rules = iterated
479
+ end
480
+
481
+ if rules.empty?
482
+ return []
483
+ end
484
+
485
+ forward_to_replicas = false
486
+ clear_existing_rules = false
487
+ request_options = symbolize_hash(opts)
488
+
489
+ if request_options[:forwardToReplicas]
490
+ forward_to_replicas = true
491
+ request_options.delete(:forwardToReplicas)
492
+ end
493
+
494
+ if request_options[:clearExistingRules]
495
+ clear_existing_rules = true
496
+ request_options.delete(:clearExistingRules)
497
+ end
498
+
499
+ rules.each do |rule|
500
+ get_object_id(rule)
501
+ end
502
+
503
+ response = @transporter.write(:POST, path_encode('/1/indexes/%s/rules/batch', @index_name) + handle_params({ forwardToReplicas: forward_to_replicas, clearExistingRules: clear_existing_rules }), rules, request_options)
504
+
505
+ IndexingResponse.new(self, response)
506
+ end
507
+
508
+ # Create or update rules and wait for operation to complete
509
+ #
510
+ # @param rules [Array] an array of hashes containing a rule objectID and different conditions/consequences
511
+ # @param opts [Hash] contains extra parameters to send with your query
512
+ #
513
+ # @return [Array, IndexingResponse]
514
+ #
515
+ def save_rules!(rules, opts = {})
516
+ response = save_rules(rules, opts)
517
+ response.wait(opts)
518
+ end
519
+
520
+ # Delete all Rules in the index
521
+ #
522
+ # @param opts [Hash] contains extra parameters to send with your query
523
+ #
524
+ # @return [IndexingResponse]
525
+ #
526
+ def clear_rules(opts = {})
527
+ forward_to_replicas = false
528
+ request_options = symbolize_hash(opts)
529
+
530
+ if request_options[:forwardToReplicas]
531
+ forward_to_replicas = true
532
+ request_options.delete(:forwardToReplicas)
533
+ end
534
+
535
+ response = @transporter.write(:POST, path_encode('1/indexes/%s/rules/clear', @index_name) + handle_params({ forwardToReplicas: forward_to_replicas }), '', request_options)
536
+
537
+ IndexingResponse.new(self, response)
538
+ end
539
+
540
+ # Delete all Rules in the index and wait for operation to complete
541
+ #
542
+ # @param opts [Hash] contains extra parameters to send with your query
543
+ #
544
+ # @return [IndexingResponse]
545
+ #
546
+ def clear_rules!(opts = {})
547
+ response = clear_rules(opts)
548
+ response.wait(opts)
549
+ end
550
+
551
+ # Delete the Rule with the specified objectID
552
+ #
553
+ # @param object_id [String]
554
+ # @param opts [Hash] contains extra parameters to send with your query
555
+ #
556
+ # @return [IndexingResponse]
557
+ #
558
+ def delete_rule(object_id, opts = {})
559
+ forward_to_replicas = false
560
+ request_options = symbolize_hash(opts)
561
+
562
+ if request_options[:forwardToReplicas]
563
+ forward_to_replicas = true
564
+ request_options.delete(:forwardToReplicas)
565
+ end
566
+
567
+ response = @transporter.write(
568
+ :DELETE,
569
+ path_encode('1/indexes/%s/rules/%s', @index_name, object_id) + handle_params({ forwardToReplicas: forward_to_replicas }),
570
+ '',
571
+ request_options
572
+ )
573
+
574
+ IndexingResponse.new(self, response)
575
+ end
576
+
577
+ # Delete the Rule with the specified objectID and wait for operation to complete
578
+ #
579
+ # @param object_id [String]
580
+ # @param opts [Hash] contains extra parameters to send with your query
581
+ #
582
+ # @return [IndexingResponse]
583
+ #
584
+ def delete_rule!(object_id, opts = {})
585
+ response = delete_rule(object_id, opts)
586
+ response.wait(opts)
587
+ end
588
+
589
+ # # # # # # # # # # # # # # # # # # # # #
590
+ # SYNONYMS
591
+ # # # # # # # # # # # # # # # # # # # # #
592
+
593
+ # Fetch a synonym object identified by its objectID
594
+ #
595
+ # @param object_id [String]
596
+ # @param opts [Hash] contains extra parameters to send with your query
597
+ #
598
+ # @return [Hash]
599
+ #
600
+ def get_synonym(object_id, opts = {})
601
+ @transporter.read(:GET, path_encode('/1/indexes/%s/synonyms/%s', @index_name, object_id), {}, opts)
602
+ end
603
+
604
+ # Create a new synonym object or update the existing synonym object with the given object ID
605
+ #
606
+ # @param synonym [Hash] Synonym object
607
+ # @param opts [Hash] contains extra parameters to send with your query
608
+ #
609
+ # @return [Array, IndexingResponse]
610
+ #
611
+ def save_synonym(synonym, opts = {})
612
+ save_synonyms([synonym], opts)
613
+ end
614
+
615
+ # Create a new synonym object or update the existing synonym object with the given object ID
616
+ # and wait for operation to finish
617
+ #
618
+ # @param synonym [Hash] Synonym object
619
+ # @param opts [Hash] contains extra parameters to send with your query
620
+ #
621
+ # @return [Array, IndexingResponse]
622
+ #
623
+ def save_synonym!(synonym, opts = {})
624
+ response = save_synonyms([synonym], opts)
625
+ response.wait(opts)
626
+ end
627
+
628
+ # Create/update multiple synonym objects at once, potentially replacing the entire list of synonyms if
629
+ # replaceExistingSynonyms is true
630
+ #
631
+ # @param synonyms [Array] Array of Synonym objects
632
+ # @param opts [Hash] contains extra parameters to send with your query
633
+ #
634
+ # @return [Array, IndexingResponse]
635
+ #
636
+ def save_synonyms(synonyms, opts = {})
637
+ if synonyms.is_a?(SynonymIterator)
638
+ iterated = []
639
+ synonyms.each do |synonym|
640
+ iterated.push(synonym)
641
+ end
642
+ synonyms = iterated
643
+ end
644
+
645
+ if synonyms.empty?
646
+ return []
647
+ end
648
+
649
+ synonyms.each do |synonym|
650
+ get_object_id(synonym)
651
+ end
652
+
653
+ forward_to_replicas = false
654
+ replace_existing_synonyms = false
655
+
656
+ request_options = symbolize_hash(opts)
657
+
658
+ if request_options[:forwardToReplicas]
659
+ forward_to_replicas = true
660
+ request_options.delete(:forwardToReplicas)
661
+ end
662
+
663
+ if request_options[:replaceExistingSynonyms]
664
+ replace_existing_synonyms = true
665
+ request_options.delete(:replaceExistingSynonyms)
666
+ end
667
+ response = @transporter.write(
668
+ :POST,
669
+ path_encode('/1/indexes/%s/synonyms/batch', @index_name) + handle_params({ forwardToReplicas: forward_to_replicas, replaceExistingSynonyms: replace_existing_synonyms }),
670
+ synonyms,
671
+ request_options
672
+ )
673
+
674
+ IndexingResponse.new(self, response)
675
+ end
676
+
677
+ # Create/update multiple synonym objects at once, potentially replacing the entire list of synonyms if
678
+ # replaceExistingSynonyms is true and wait for operation to complete
679
+ #
680
+ # @param synonyms [Array] Array of Synonym objects
681
+ # @param opts [Hash] contains extra parameters to send with your query
682
+ #
683
+ # @return [Array, IndexingResponse]
684
+ #
685
+ def save_synonyms!(synonyms, opts = {})
686
+ response = save_synonyms(synonyms, opts)
687
+ response.wait(opts)
688
+ end
689
+
690
+ # Delete all synonyms from the index
691
+ #
692
+ # @param opts [Hash] contains extra parameters to send with your query
693
+ #
694
+ # @return [IndexingResponse]
695
+ #
696
+ def clear_synonyms(opts = {})
697
+ forward_to_replicas = false
698
+ request_options = symbolize_hash(opts)
699
+
700
+ if request_options[:forwardToReplicas]
701
+ forward_to_replicas = true
702
+ request_options.delete(:forwardToReplicas)
703
+ end
704
+ response = @transporter.write(
705
+ :POST,
706
+ path_encode('1/indexes/%s/synonyms/clear', @index_name) + handle_params({ forwardToReplicas: forward_to_replicas }),
707
+ '',
708
+ request_options
709
+ )
710
+
711
+ IndexingResponse.new(self, response)
712
+ end
713
+
714
+ # Delete all synonyms from the index and wait for operation to complete
715
+ #
716
+ # @param opts [Hash] contains extra parameters to send with your query
717
+ #
718
+ # @return [IndexingResponse]
719
+ #
720
+ def clear_synonyms!(opts = {})
721
+ response = clear_synonyms(opts)
722
+ response.wait(opts)
723
+ end
724
+
725
+ # Delete a single synonyms set, identified by the given objectID
726
+ #
727
+ # @param opts [Hash] contains extra parameters to send with your query
728
+ #
729
+ # @return [IndexingResponse]
730
+ #
731
+ def delete_synonym(object_id, opts = {})
732
+ forward_to_replicas = false
733
+ request_options = symbolize_hash(opts)
734
+
735
+ if request_options[:forwardToReplicas]
736
+ forward_to_replicas = true
737
+ request_options.delete(:forwardToReplicas)
738
+ end
739
+ response = @transporter.write(
740
+ :DELETE,
741
+ path_encode('1/indexes/%s/synonyms/%s', @index_name, object_id) + handle_params({ forwardToReplicas: forward_to_replicas }),
742
+ '',
743
+ request_options
744
+ )
745
+
746
+ IndexingResponse.new(self, response)
747
+ end
748
+
749
+ # Delete a single synonyms set, identified by the given objectID and wait for operation to complete
750
+ #
751
+ # @param opts [Hash] contains extra parameters to send with your query
752
+ #
753
+ # @return [IndexingResponse]
754
+ #
755
+ def delete_synonym!(object_id, opts = {})
756
+ response = delete_synonym(object_id, opts)
757
+ response.wait(opts)
758
+ end
759
+
760
+ # # # # # # # # # # # # # # # # # # # # #
761
+ # BROWSING
762
+ # # # # # # # # # # # # # # # # # # # # #
763
+
764
+ # Browse all index content
765
+ #
766
+ # @param opts [Hash] contains extra parameters to send with your query
767
+ #
768
+ # @return [Enumerator, ObjectIterator]
769
+ #
770
+ def browse_objects(opts = {}, &block)
771
+ if block_given?
772
+ ObjectIterator.new(@transporter, @index_name, opts).each(&block)
773
+ else
774
+ ObjectIterator.new(@transporter, @index_name, opts)
775
+ end
776
+ end
777
+
778
+ # Browse all rules
779
+ #
780
+ # @param opts [Hash] contains extra parameters to send with your query
781
+ #
782
+ # @return [Enumerator, RuleIterator]
783
+ #
784
+ def browse_rules(opts = {}, &block)
785
+ if block_given?
786
+ RuleIterator.new(@transporter, @index_name, opts).each(&block)
787
+ else
788
+ RuleIterator.new(@transporter, @index_name, opts)
789
+ end
790
+ end
791
+
792
+ # Browse all synonyms
793
+ #
794
+ # @param opts [Hash] contains extra parameters to send with your query
795
+ #
796
+ # @return [Enumerator, SynonymIterator]
797
+ #
798
+ def browse_synonyms(opts = {}, &block)
799
+ if block_given?
800
+ SynonymIterator.new(@transporter, @index_name, opts).each(&block)
801
+ else
802
+ SynonymIterator.new(@transporter, @index_name, opts)
803
+ end
804
+ end
805
+
806
+ # # # # # # # # # # # # # # # # # # # # #
807
+ # REPLACING
808
+ # # # # # # # # # # # # # # # # # # # # #
809
+
810
+ # Replace all objects in the index
811
+ #
812
+ # @param objects [Array] Array of objects
813
+ # @param opts [Hash] contains extra parameters to send with your query
814
+ #
815
+ # @return [Enumerator, SynonymIterator]
816
+ #
817
+ def replace_all_objects(objects, opts = {})
818
+ safe = false
819
+ request_options = symbolize_hash(opts)
820
+ if request_options[:safe]
821
+ safe = true
822
+ request_options.delete(:safe)
823
+ end
824
+
825
+ tmp_index_name = @index_name + '_tmp_' + rand(10000000).to_s
826
+ copy_to_response = copy_to(tmp_index_name, request_options.merge({ scope: %w(settings synonyms rules) }))
827
+
828
+ if safe
829
+ copy_to_response.wait
830
+ end
831
+
832
+ # TODO: consider create a new client with state of retry is shared
833
+ tmp_client = Algolia::Search::Client.new(@config)
834
+ tmp_index = tmp_client.init_index(tmp_index_name)
835
+
836
+ save_objects_response = tmp_index.save_objects(objects, request_options)
837
+
838
+ if safe
839
+ save_objects_response.wait
840
+ end
841
+
842
+ move_to_response = tmp_index.move_to(@index_name)
843
+ if safe
844
+ move_to_response.wait
845
+ end
846
+ end
847
+
848
+ # Replace all objects in the index and wait for the operation to complete
849
+ #
850
+ # @param objects [Array] Array of objects
851
+ # @param opts [Hash] contains extra parameters to send with your query
852
+ #
853
+ # @return [Enumerator, SynonymIterator]
854
+ #
855
+ def replace_all_objects!(objects, opts = {})
856
+ replace_all_objects(objects, opts.merge(safe: true))
857
+ end
858
+
859
+ # Replace all rules in the index
860
+ #
861
+ # @param rules [Array] Array of rules
862
+ # @param opts [Hash] contains extra parameters to send with your query
863
+ #
864
+ # @return [Array, IndexingResponse]
865
+ #
866
+ def replace_all_rules(rules, opts = {})
867
+ request_options = symbolize_hash(opts)
868
+ request_options[:clearExistingRules] = true
869
+
870
+ save_rules(rules, request_options)
871
+ end
872
+
873
+ # Replace all rules in the index and wait for the operation to complete
874
+ #
875
+ # @param rules [Array] Array of rules
876
+ # @param opts [Hash] contains extra parameters to send with your query
877
+ #
878
+ # @return [Array, IndexingResponse]
879
+ #
880
+ def replace_all_rules!(rules, opts = {})
881
+ request_options = symbolize_hash(opts)
882
+ request_options[:clearExistingRules] = true
883
+
884
+ save_rules!(rules, request_options)
885
+ end
886
+
887
+ # Replace all synonyms in the index
888
+ #
889
+ # @param synonyms [Array] Array of synonyms
890
+ # @param opts [Hash] contains extra parameters to send with your query
891
+ #
892
+ # @return [Array, IndexingResponse]
893
+ #
894
+ def replace_all_synonyms(synonyms, opts = {})
895
+ request_options = symbolize_hash(opts)
896
+ request_options[:replaceExistingSynonyms] = true
897
+
898
+ save_synonyms(synonyms, request_options)
899
+ end
900
+
901
+ # Replace all synonyms in the index and wait for the operation to complete
902
+ #
903
+ # @param synonyms [Array] Array of synonyms
904
+ # @param opts [Hash] contains extra parameters to send with your query
905
+ #
906
+ # @return [Array, IndexingResponse]
907
+ #
908
+ def replace_all_synonyms!(synonyms, opts = {})
909
+ request_options = symbolize_hash(opts)
910
+ request_options[:replaceExistingSynonyms] = true
911
+
912
+ save_synonyms!(synonyms, request_options)
913
+ end
914
+
915
+ # # # # # # # # # # # # # # # # # # # # #
916
+ # SEARCHING
917
+ # # # # # # # # # # # # # # # # # # # # #
918
+
919
+ # Perform a search on the index
920
+ #
921
+ # @param query the full text query
922
+ # @param opts [Hash] contains extra parameters to send with your query
923
+ #
924
+ # @return [Hash]
925
+ #
926
+ def search(query, opts = {})
927
+ @transporter.read(:POST, path_encode('/1/indexes/%s/query', @index_name), { 'query': query.to_s }, opts)
928
+ end
929
+
930
+ # Search for values of a given facet, optionally restricting the returned values to those contained
931
+ # in objects matching other search criteria
932
+ #
933
+ # @param facet_name [String]
934
+ # @param facet_query [String]
935
+ # @param opts [Hash] contains extra parameters to send with your query
936
+ #
937
+ # @return [Hash]
938
+ #
939
+ def search_for_facet_values(facet_name, facet_query, opts = {})
940
+ @transporter.read(:POST, path_encode('/1/indexes/%s/facets/%s/query', @index_name, facet_name),
941
+ { 'facetQuery': facet_query }, opts)
942
+ end
943
+
944
+ # Search or browse all synonyms, optionally filtering them by type
945
+ #
946
+ # @param query [String] Search for specific synonyms matching this string
947
+ # @param opts [Hash] contains extra parameters to send with your query
948
+ #
949
+ # @return [Hash]
950
+ #
951
+ def search_synonyms(query, opts = {})
952
+ @transporter.read(:POST, path_encode('/1/indexes/%s/synonyms/search', @index_name), { query: query.to_s }, opts)
953
+ end
954
+
955
+ # Search or browse all rules, optionally filtering them by type
956
+ #
957
+ # @param query [String] Search for specific rules matching this string
958
+ # @param opts [Hash] contains extra parameters to send with your query
959
+ #
960
+ # @return [Hash]
961
+ #
962
+ def search_rules(query, opts = {})
963
+ @transporter.read(:POST, path_encode('/1/indexes/%s/rules/search', @index_name), { query: query.to_s }, opts)
964
+ end
965
+
966
+ # # # # # # # # # # # # # # # # # # # # #
967
+ # SETTINGS
968
+ # # # # # # # # # # # # # # # # # # # # #
969
+
970
+ # Retrieve index settings
971
+ #
972
+ # @param opts [Hash] contains extra parameters to send with your query
973
+ #
974
+ # @return [Hash]
975
+ #
976
+ def get_settings(opts = {})
977
+ response = @transporter.read(:GET, path_encode('/1/indexes/%s/settings', @index_name) + handle_params({ getVersion: 2 }), {}, opts)
978
+
979
+ deserialize_settings(response)
980
+ end
981
+
982
+ # Update some index settings. Only specified settings are overridden
983
+ #
984
+ # @param settings [Hash] the settings to update
985
+ # @param opts [Hash] contains extra parameters to send with your query
986
+ #
987
+ # @return [IndexingResponse]
988
+ #
989
+ def set_settings(settings, opts = {})
990
+ response = @transporter.write(:PUT, path_encode('/1/indexes/%s/settings', @index_name), settings, opts)
991
+
992
+ IndexingResponse.new(self, response)
993
+ end
994
+
995
+ # Update some index settings and wait for operation to complete.
996
+ # Only specified settings are overridden
997
+ #
998
+ # @param settings [Hash] the settings to update
999
+ # @param opts [Hash] contains extra parameters to send with your query
1000
+ #
1001
+ # @return [IndexingResponse]
1002
+ #
1003
+ def set_settings!(settings, opts = {})
1004
+ response = set_settings(settings, opts)
1005
+ response.wait(opts)
1006
+ end
1007
+
1008
+ # # # # # # # # # # # # # # # # # # # # #
1009
+ # EXISTS
1010
+ # # # # # # # # # # # # # # # # # # # # #
1011
+
1012
+ # Checks if the current index exists
1013
+ #
1014
+ # @return [Boolean]
1015
+ #
1016
+ def exists
1017
+ begin
1018
+ get_settings
1019
+ rescue AlgoliaHttpError => e
1020
+ if e.code == 404
1021
+ return false
1022
+ end
1023
+
1024
+ raise e
1025
+ end
1026
+ true
1027
+ end
1028
+
1029
+ #
1030
+ # Aliases the exists method
1031
+ #
1032
+ alias_method :exists?, :exists
1033
+
1034
+ # # # # # # # # # # # # # # # # # # # # #
1035
+ # PRIVATE
1036
+ # # # # # # # # # # # # # # # # # # # # #
1037
+
1038
+ private
1039
+
1040
+ # Check the passed object to determine if it's an array
1041
+ #
1042
+ # @param object [Object]
1043
+ #
1044
+ def check_array(object)
1045
+ raise AlgoliaError, 'argument must be an array of objects' unless object.is_a?(Array)
1046
+ end
1047
+
1048
+ # Check the passed object
1049
+ #
1050
+ # @param object [Object]
1051
+ # @param in_array [Boolean] whether the object is an array or not
1052
+ #
1053
+ def check_object(object, in_array = false)
1054
+ case object
1055
+ when Array
1056
+ raise AlgoliaError, in_array ? 'argument must be an array of objects' : 'argument must not be an array'
1057
+ when String, Integer, Float, TrueClass, FalseClass, NilClass
1058
+ raise AlgoliaError, "argument must be an #{'array of' if in_array} object, got: #{object.inspect}"
1059
+ end
1060
+ end
1061
+
1062
+ # Check if passed object has a objectID
1063
+ #
1064
+ # @param object [Object]
1065
+ # @param object_id [String]
1066
+ #
1067
+ def get_object_id(object, object_id = nil)
1068
+ check_object(object)
1069
+ object_id ||= object[:objectID] || object['objectID']
1070
+ raise AlgoliaError, "Missing 'objectID'" if object_id.nil?
1071
+ object_id
1072
+ end
1073
+
1074
+ # Build a batch request
1075
+ #
1076
+ # @param action [String] action to perform on the engine
1077
+ # @param objects [Array] objects on which build the action
1078
+ # @param with_object_id [Boolean] if set to true, check if each object has an objectID set
1079
+ #
1080
+ def chunk(action, objects, with_object_id = false)
1081
+ objects.map do |object|
1082
+ check_object(object, true)
1083
+ request = { action: action, body: object }
1084
+ request[:objectID] = get_object_id(object).to_s if with_object_id
1085
+ request
1086
+ end
1087
+ end
1088
+
1089
+ def raw_batch(requests, opts)
1090
+ @transporter.write(:POST, path_encode('/1/indexes/%s/batch', @index_name), { requests: requests }, opts)
1091
+ end
1092
+ end
1093
+ end
1094
+ end