algolia 2.0.0.pre.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
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