algolia 3.39.1 → 3.40.0

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +1 -1
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile.lock +1 -1
  5. data/lib/algolia/api/ingestion_client.rb +71 -0
  6. data/lib/algolia/api/search_client.rb +342 -22
  7. data/lib/algolia/chunked_helper_options.rb +15 -0
  8. data/lib/algolia/configuration.rb +3 -1
  9. data/lib/algolia/models/abtesting/ab_test.rb +5 -0
  10. data/lib/algolia/models/query-suggestions/configuration.rb +1 -0
  11. data/lib/algolia/models/query-suggestions/configuration_response.rb +1 -0
  12. data/lib/algolia/models/query-suggestions/configuration_with_index.rb +1 -0
  13. data/lib/algolia/models/query-suggestions/source_index.rb +4 -0
  14. data/lib/algolia/models/recommend/fallback_params.rb +2 -1
  15. data/lib/algolia/models/recommend/recommend_search_params.rb +2 -1
  16. data/lib/algolia/models/search/browse_params_object.rb +2 -1
  17. data/lib/algolia/models/search/consequence_params.rb +2 -1
  18. data/lib/algolia/models/search/fetched_index.rb +25 -4
  19. data/lib/algolia/models/search/fetched_index_ab_test.rb +253 -0
  20. data/lib/algolia/models/search/fetched_index_ab_test_target.rb +210 -0
  21. data/lib/algolia/models/search/fetched_index_ab_test_variant.rb +242 -0
  22. data/lib/algolia/models/search/index_settings.rb +2 -1
  23. data/lib/algolia/models/search/search_for_facets.rb +2 -1
  24. data/lib/algolia/models/search/search_for_hits.rb +2 -1
  25. data/lib/algolia/models/search/search_params_object.rb +2 -1
  26. data/lib/algolia/models/search/search_response_partial.rb +587 -0
  27. data/lib/algolia/models/search/search_result.rb +2 -1
  28. data/lib/algolia/models/search/settings_response.rb +2 -1
  29. data/lib/algolia/version.rb +1 -1
  30. data/lib/algolia.rb +1 -2
  31. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7efa0c7cc16401848c061d498f8490b972e2bb87c751fae945acc26104b4363a
4
- data.tar.gz: 4a98837bfd4bc4ba14098628e9f1642def743ec961abc118d82f88dcf48c87cf
3
+ metadata.gz: 7030ad3b848a8e074b47b86a06c9b2494a1141097ce80c9e3e0a50c81e96aacb
4
+ data.tar.gz: 336d669b4166d8edf241d3b63c31bc51155f718282a9c91819b21625ce022091
5
5
  SHA512:
6
- metadata.gz: b573eba34f313adafb0b087cc666b75c62d91434b848e77bb9c971232e5d37e1615a84dbac5337a813b487b6fca57f6f566a2c914df39fdf8d89a955ee31d65c
7
- data.tar.gz: d7eac776ce519b89e7b352a6ee6618e95f91efc25859e070fd4361e7fa61456849e1ac6e57eac2fa662318a3f58eda79990bf50c2aafaeed7d5eebbf670a0621
6
+ metadata.gz: 0b4be15c6ca713800092aad40bacd12e508b048f082c08e76c8f14c7d70bea63788b52b1feddb33100dfaf2c0eba0ade284cbf46691f1a341eb15a8c06fd2d4c
7
+ data.tar.gz: a3ae3ff7bcbc1d78a9e0e4ae937d9a06b3ddad1520529ba722d95d6f2e43d4bb18a43c1d3c397c47bc5af9d892bdbe9815ed9349291ae060537f51ef5fabb5b2
@@ -21,7 +21,7 @@ jobs:
21
21
  - name: Install Ruby
22
22
  uses: ruby/setup-ruby@v1
23
23
  with:
24
- ruby-version: 4.0.3
24
+ ruby-version: 4.0.4
25
25
  bundler-cache: true
26
26
 
27
27
  - uses: rubygems/release-gem@e9a6361a0b14562539327c2a02373edc56dd3169
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [3.40.0](https://github.com/algolia/algoliasearch-client-ruby/compare/3.39.1...3.40.0)
2
+
3
+ BREAKING CHANGES: this minor version includes breaking changes. See below for more details.
4
+
5
+ - [84ebc480a8](https://github.com/algolia/api-clients-automation/commit/84ebc480a8) chore(deps): dependencies 2026-05-15 ([#6400](https://github.com/algolia/api-clients-automation/pull/6400)) by [@Fluf22](https://github.com/Fluf22/)
6
+ - [90da6bf0ae](https://github.com/algolia/api-clients-automation/commit/90da6bf0ae) chore(deps): dependencies 2026-05-18 ([#6429](https://github.com/algolia/api-clients-automation/pull/6429)) by [@Fluf22](https://github.com/Fluf22/)
7
+ - [77cfd0447b](https://github.com/algolia/api-clients-automation/commit/77cfd0447b) feat(ruby): add TransformationOptions for ingestion transporter configuration ([#6313](https://github.com/algolia/api-clients-automation/pull/6313)) by [@MarioAlexandruDan](https://github.com/MarioAlexandruDan/)
8
+ - [eddd51e349](https://github.com/algolia/api-clients-automation/commit/eddd51e349) fix(specs): document abTest field on listIndices response ([#6443](https://github.com/algolia/api-clients-automation/pull/6443)) by [@eric-zaharia](https://github.com/eric-zaharia/)
9
+ - [1d3f2c149b](https://github.com/algolia/api-clients-automation/commit/1d3f2c149b) fix(specs): clean up abTest schema on listIndices response ([#6448](https://github.com/algolia/api-clients-automation/pull/6448)) by [@Fluf22](https://github.com/Fluf22/)
10
+ - [35bf16c848](https://github.com/algolia/api-clients-automation/commit/35bf16c848) fix(specs): BREAKING CHANGE – add searchResponsePartial fallback to searchResult oneOf ([#6350](https://github.com/algolia/api-clients-automation/pull/6350)) by [@Fluf22](https://github.com/Fluf22/)
11
+ - For those of you who want really clean responses without hits nor facets, we added a third fallback type to the search response. It should not impact the rest of the users.
12
+ - [e4f6b366a1](https://github.com/algolia/api-clients-automation/commit/e4f6b366a1) feat(ruby): BREAKING CHANGE – expose maximum number of retries ([#6461](https://github.com/algolia/api-clients-automation/pull/6461)) by [@eric-zaharia](https://github.com/eric-zaharia/)
13
+ - We updated the default number of retries to account for customers with huge indices, and made it configurable if you prefer to fail early. The impact is that the retry time will be longer in case of issue, but it shouldn't impact most customers: those who don't have issues.
14
+
1
15
  ## [3.39.1](https://github.com/algolia/algoliasearch-client-ruby/compare/3.39.0...3.39.1)
2
16
 
3
17
  - [363cc2d91b](https://github.com/algolia/api-clients-automation/commit/363cc2d91b) fix(specs): Ingestion API - update destination payload ([#6320](https://github.com/algolia/api-clients-automation/pull/6320)) by [@sbellone](https://github.com/sbellone/)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- algolia (3.39.1)
4
+ algolia (3.40.0)
5
5
  base64 (>= 0.2.0, < 1)
6
6
  faraday (>= 1.0.1, < 3.0)
7
7
  faraday-net_http_persistent (>= 0.15, < 3)
@@ -3588,5 +3588,76 @@ module Algolia
3588
3588
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Ingestion::WatchResponse")
3589
3589
  end
3590
3590
 
3591
+ # Helper: Pushes objects to the given index in chunks, optionally waiting for each batch to be processed.
3592
+ #
3593
+ # @param index_name [String] the `index_name` where the operation will be performed. (required)
3594
+ # @param objects [Array] the array of objects to push to the given Algolia `index_name`. (required)
3595
+ # @param action [String] the push action to perform on the objects. (optional, default: addObject)
3596
+ # @param wait_for_tasks [Boolean] whether to wait until every task has been processed. (optional, default: false)
3597
+ # @param batch_size [Integer] the size of each chunk of objects sent in a single push call. (optional, default: 1000)
3598
+ # @param reference_index_name [String] the reference index name used for replace-all operations. (optional)
3599
+ # @param request_options [Hash] the request options to send along with the query. (optional)
3600
+ #
3601
+ # @return [Array<Ingestion::WatchResponse>]
3602
+ def chunked_push(
3603
+ index_name,
3604
+ objects,
3605
+ action = Ingestion::Action::ADD_OBJECT,
3606
+ wait_for_tasks = false,
3607
+ batch_size = 1000,
3608
+ reference_index_name = nil,
3609
+ request_options = {},
3610
+ chunked_options = nil
3611
+ )
3612
+ responses = []
3613
+ offset = 0
3614
+ wait_batch_size = batch_size / 10
3615
+ wait_batch_size = batch_size if wait_batch_size < 1
3616
+ total_batches = (objects.length.to_f / batch_size).ceil
3617
+ opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
3618
+
3619
+ objects.each_slice(batch_size).with_index do |chunk, batch_index|
3620
+ response = push(
3621
+ index_name,
3622
+ Ingestion::PushTaskPayload.new(action: action, records: chunk),
3623
+ false,
3624
+ reference_index_name,
3625
+ request_options
3626
+ )
3627
+ responses << response
3628
+
3629
+ if wait_for_tasks && (responses.length % wait_batch_size == 0 || batch_index == total_batches - 1)
3630
+ responses[offset, wait_batch_size].each do |watch_response|
3631
+ if watch_response.event_id.nil?
3632
+ raise ArgumentError, "received unexpected response from the push endpoint, eventID must not be nil"
3633
+ end
3634
+
3635
+ retries = 0
3636
+ loop do
3637
+ begin
3638
+ event = get_event(watch_response.run_id, watch_response.event_id, request_options)
3639
+ break if event
3640
+ rescue AlgoliaHttpError => e
3641
+ raise e unless e.code == 404
3642
+ end
3643
+
3644
+ retries += 1
3645
+ if retries >= opts.max_retries
3646
+ raise(
3647
+ ApiError,
3648
+ "Stopped waiting for the task after #{opts.max_retries} retries. This does not mean the operation failed; it may still complete. If you need to keep polling, retry with a higher max_retries."
3649
+ )
3650
+ end
3651
+
3652
+ sleep([retries * 1.5, 5].min)
3653
+ end
3654
+ end
3655
+
3656
+ offset += wait_batch_size
3657
+ end
3658
+ end
3659
+
3660
+ responses
3661
+ end
3591
3662
  end
3592
3663
  end
@@ -6,6 +6,49 @@ require "openssl"
6
6
  require "base64"
7
7
 
8
8
  module Algolia
9
+ # Configuration options for the ingestion transporter used by *_with_transformation helpers.
10
+ # When passed to SearchClient.with_transformation or set via set_transformation_options,
11
+ # the ingestion transporter is eagerly created using Ingestion API defaults (25s timeouts,
12
+ # no compression). Only fields explicitly set here override those defaults.
13
+ # See https://www.algolia.com/doc/libraries/ruby/v3/methods/ingestion
14
+ class TransformationOptions
15
+ attr_accessor(
16
+ :region,
17
+ :connect_timeout,
18
+ :read_timeout,
19
+ :write_timeout,
20
+ :hosts,
21
+ :compression_type,
22
+ :header_params
23
+ )
24
+
25
+ def initialize(region, opts = {})
26
+ if region.nil? || region.to_s.strip.empty?
27
+ raise(
28
+ ArgumentError,
29
+ "`region` is required in `TransformationOptions`. See https://www.algolia.com/doc/libraries/ruby/v3/methods/ingestion"
30
+ )
31
+ end
32
+
33
+ valid_keys = %i[connect_timeout read_timeout write_timeout hosts compression_type header_params]
34
+ unknown = opts.keys - valid_keys
35
+ unless unknown.empty?
36
+ raise(
37
+ ArgumentError,
38
+ "Unknown TransformationOptions keys: #{unknown.join(", ")}. Valid keys are: #{valid_keys.join(", ")}"
39
+ )
40
+ end
41
+
42
+ @region = region
43
+ @connect_timeout = opts[:connect_timeout]
44
+ @read_timeout = opts[:read_timeout]
45
+ @write_timeout = opts[:write_timeout]
46
+ @hosts = opts[:hosts]
47
+ @compression_type = opts[:compression_type]
48
+ @header_params = opts[:header_params]
49
+ end
50
+ end
51
+
9
52
  class SearchClient
10
53
  attr_accessor :api_client
11
54
 
@@ -27,6 +70,10 @@ module Algolia
27
70
  end
28
71
 
29
72
  @api_client = Algolia::ApiClient.new(config)
73
+ @ingestion_transporter = nil
74
+ if config.transformation_options
75
+ @ingestion_transporter = _build_ingestion_transporter(config.transformation_options)
76
+ end
30
77
  end
31
78
 
32
79
  def self.create(app_id, api_key, opts = {})
@@ -49,6 +96,31 @@ module Algolia
49
96
  new(config)
50
97
  end
51
98
 
99
+ # Creates a SearchClient configured with a TransformationOptions for use with
100
+ # *_with_transformation helpers. The ingestion transporter is initialised eagerly using
101
+ # Ingestion API defaults (25s timeouts); set override fields on TransformationOptions to
102
+ # change specific defaults.
103
+ # See https://www.algolia.com/doc/libraries/ruby/v3/methods/ingestion
104
+ #
105
+ # @param app_id [String] the Algolia application ID. (required)
106
+ # @param api_key [String] the Algolia API key. (required)
107
+ # @param transformation_options [TransformationOptions] the transformation options including region and optional ingestion transporter overrides. (required)
108
+ # @param opts [Hash] additional configuration options passed to the search client. (optional)
109
+ # @return [SearchClient]
110
+ def self.with_transformation(app_id, api_key, transformation_options, opts = {})
111
+ opts = opts.dup
112
+ hosts = opts.delete(:hosts)
113
+ if hosts
114
+ config = Algolia::Configuration.new(app_id, api_key, hosts, "Search", opts)
115
+ client = new(config)
116
+ else
117
+ client = create(app_id, api_key, opts)
118
+ end
119
+
120
+ client.set_transformation_options(transformation_options)
121
+ client
122
+ end
123
+
52
124
  # Helper method to switch the API key used to authenticate the requests.
53
125
  #
54
126
  # @param api_key [String] the new API key to use.
@@ -3340,18 +3412,231 @@ module Algolia
3340
3412
  @api_client.deserialize(response.body, request_options[:debug_return_type] || "Search::UpdateApiKeyResponse")
3341
3413
  end
3342
3414
 
3415
+ # The parent search config MUST NOT leak into the ingestion transporter.
3416
+ def _build_ingestion_transporter(transformation_options)
3417
+ hosts = if transformation_options.hosts
3418
+ transformation_options.hosts
3419
+ else
3420
+ [
3421
+ Transport::StatefulHost.new(
3422
+ "data.#{transformation_options.region}.algolia.com",
3423
+ accept: CallType::READ | CallType::WRITE
3424
+ )
3425
+ ]
3426
+ end
3427
+
3428
+ opts = {}
3429
+ unless transformation_options.connect_timeout.nil?
3430
+ opts[:connect_timeout] = transformation_options.connect_timeout
3431
+ end
3432
+
3433
+ opts[:read_timeout] = transformation_options.read_timeout unless transformation_options.read_timeout.nil?
3434
+ opts[:write_timeout] = transformation_options.write_timeout unless transformation_options.write_timeout.nil?
3435
+ unless transformation_options.compression_type.nil?
3436
+ opts[:compression_type] = transformation_options.compression_type
3437
+ end
3438
+
3439
+ config = Algolia::Configuration.new(
3440
+ @api_client.config.app_id,
3441
+ @api_client.config.api_key,
3442
+ hosts,
3443
+ "Ingestion",
3444
+ opts
3445
+ )
3446
+
3447
+ if transformation_options.header_params
3448
+ config.header_params = config.header_params.merge(transformation_options.header_params)
3449
+ end
3450
+
3451
+ Algolia::IngestionClient.create_with_config(config)
3452
+ end
3453
+
3454
+ # Helper: Sets (or replaces) the ingestion transporter used by *_with_transformation helpers.
3455
+ #
3456
+ # @param transformation_options [TransformationOptions] the transformation options including region and optional ingestion transporter overrides. (required)
3457
+ def set_transformation_options(transformation_options)
3458
+ raise ArgumentError, "`transformation_options` must not be nil" if transformation_options.nil?
3459
+ @ingestion_transporter = _build_ingestion_transporter(transformation_options)
3460
+ end
3461
+
3462
+ def assert_ingestion_transporter!
3463
+ if @ingestion_transporter.nil?
3464
+ raise(
3465
+ ArgumentError,
3466
+ "`transformation_options` must be set in the client config before calling this method. It defaults to the Ingestion API defaults. See https://www.algolia.com/doc/libraries/ruby/v3/methods/ingestion/"
3467
+ )
3468
+ end
3469
+ end
3470
+
3471
+ # Helper: Similar to the `save_objects` method but requires a Push connector to be created first,
3472
+ # in order to transform records before indexing them to Algolia.
3473
+ # `set_transformation_options` must have been called, or the client created via `SearchClient.with_transformation`.
3474
+ #
3475
+ # @param index_name [String] the `index_name` where the operation will be performed. (required)
3476
+ # @param objects [Array] the array of objects to store in the given Algolia `index_name`. (required)
3477
+ # @param wait_for_tasks [Boolean] whether to wait until every task has been processed. (optional, default: false)
3478
+ # @param batch_size [Integer] the size of each chunk of objects sent in a single push call. (optional, default: 1000)
3479
+ # @param request_options [Hash] the request options to send along with the query. (optional)
3480
+ #
3481
+ # @return [Array<Ingestion::WatchResponse>]
3482
+ def save_objects_with_transformation(
3483
+ index_name,
3484
+ objects,
3485
+ wait_for_tasks = false,
3486
+ batch_size = 1000,
3487
+ request_options = {},
3488
+ chunked_options = nil
3489
+ )
3490
+ assert_ingestion_transporter!
3491
+
3492
+ @ingestion_transporter.chunked_push(
3493
+ index_name,
3494
+ objects,
3495
+ Ingestion::Action::ADD_OBJECT,
3496
+ wait_for_tasks,
3497
+ batch_size,
3498
+ nil,
3499
+ request_options,
3500
+ chunked_options
3501
+ )
3502
+ end
3503
+
3504
+ # Helper: Similar to the `partial_update_objects` method but requires a Push connector to be created first,
3505
+ # in order to transform records before indexing them to Algolia.
3506
+ # `set_transformation_options` must have been called, or the client created via `SearchClient.with_transformation`.
3507
+ #
3508
+ # @param index_name [String] the `index_name` where the operation will be performed. (required)
3509
+ # @param objects [Array] the array of objects to update in the given Algolia `index_name`. (required)
3510
+ # @param create_if_not_exists [Boolean] whether to create objects that do not exist. (optional, default: false)
3511
+ # @param wait_for_tasks [Boolean] whether to wait until every task has been processed. (optional, default: false)
3512
+ # @param batch_size [Integer] the size of each chunk of objects sent in a single push call. (optional, default: 1000)
3513
+ # @param request_options [Hash] the request options to send along with the query. (optional)
3514
+ #
3515
+ # @return [Array<Ingestion::WatchResponse>]
3516
+ def partial_update_objects_with_transformation(
3517
+ index_name,
3518
+ objects,
3519
+ create_if_not_exists = false,
3520
+ wait_for_tasks = false,
3521
+ batch_size = 1000,
3522
+ request_options = {},
3523
+ chunked_options = nil
3524
+ )
3525
+ assert_ingestion_transporter!
3526
+
3527
+ action = create_if_not_exists ? Ingestion::Action::PARTIAL_UPDATE_OBJECT : Ingestion::Action::PARTIAL_UPDATE_OBJECT_NO_CREATE
3528
+
3529
+ @ingestion_transporter.chunked_push(
3530
+ index_name,
3531
+ objects,
3532
+ action,
3533
+ wait_for_tasks,
3534
+ batch_size,
3535
+ nil,
3536
+ request_options,
3537
+ chunked_options
3538
+ )
3539
+ end
3540
+
3541
+ # Helper: Similar to the `replace_all_objects` method but requires a Push connector to be created first,
3542
+ # in order to transform records before indexing them to Algolia.
3543
+ # `set_transformation_options` must have been called, or the client created via `SearchClient.with_transformation`.
3544
+ #
3545
+ # @param index_name [String] the `index_name` to replace objects in. (required)
3546
+ # @param objects [Array] the array of objects to store in the given Algolia `index_name`. (required)
3547
+ # @param batch_size [Integer] the size of each chunk of objects sent in a single push call. (optional, default: 1000)
3548
+ # @param scopes [Array] the scopes to keep from the index. (optional, default: settings, rules, synonyms)
3549
+ # @param request_options [Hash] the request options to send along with the query. (optional)
3550
+ #
3551
+ # @return [Search::ReplaceAllObjectsWithTransformationResponse]
3552
+ def replace_all_objects_with_transformation(
3553
+ index_name,
3554
+ objects,
3555
+ batch_size = 1000,
3556
+ scopes = [Search::ScopeType::SETTINGS, Search::ScopeType::RULES, Search::ScopeType::SYNONYMS],
3557
+ request_options = {},
3558
+ chunked_options = nil
3559
+ )
3560
+ assert_ingestion_transporter!
3561
+
3562
+ opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
3563
+ tmp_index_name = index_name + "_tmp_" + rand(10_000_000).to_s
3564
+
3565
+ begin
3566
+ copy_operation_response = operation_index(
3567
+ index_name,
3568
+ Search::OperationIndexParams.new(
3569
+ operation: Search::OperationType::COPY,
3570
+ destination: tmp_index_name,
3571
+ scope: scopes
3572
+ ),
3573
+ request_options
3574
+ )
3575
+
3576
+ watch_responses = @ingestion_transporter.chunked_push(
3577
+ tmp_index_name,
3578
+ objects,
3579
+ Ingestion::Action::ADD_OBJECT,
3580
+ true,
3581
+ batch_size,
3582
+ index_name,
3583
+ request_options,
3584
+ opts
3585
+ )
3586
+
3587
+ wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3588
+
3589
+ copy_operation_response = operation_index(
3590
+ index_name,
3591
+ Search::OperationIndexParams.new(
3592
+ operation: Search::OperationType::COPY,
3593
+ destination: tmp_index_name,
3594
+ scope: scopes
3595
+ ),
3596
+ request_options
3597
+ )
3598
+
3599
+ wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3600
+
3601
+ move_operation_response = operation_index(
3602
+ tmp_index_name,
3603
+ Search::OperationIndexParams.new(
3604
+ operation: Search::OperationType::MOVE,
3605
+ destination: index_name
3606
+ ),
3607
+ request_options
3608
+ )
3609
+
3610
+ wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
3611
+
3612
+ search_watch_responses = watch_responses.map do |wr|
3613
+ Search::WatchResponse.build_from_hash(wr.to_hash)
3614
+ end
3615
+
3616
+ Search::ReplaceAllObjectsWithTransformationResponse.new(
3617
+ copy_operation_response: copy_operation_response,
3618
+ watch_responses: search_watch_responses,
3619
+ move_operation_response: move_operation_response
3620
+ )
3621
+ rescue Exception => e
3622
+ delete_index(tmp_index_name)
3623
+
3624
+ raise e
3625
+ end
3626
+ end
3627
+
3343
3628
  # Helper: Wait for a task to be published (completed) for a given `index_name` and `task_id`.
3344
3629
  #
3345
3630
  # @param index_name [String] the `index_name` where the operation was performed. (required)
3346
3631
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3347
- # @param max_retries [Integer] the maximum number of retries. (optional, default to 50)
3632
+ # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3348
3633
  # @param timeout [Proc] the function to decide how long to wait between retries. (optional)
3349
3634
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3350
3635
  # @return [Http::Response] the last get_task response
3351
3636
  def wait_for_task(
3352
3637
  index_name,
3353
3638
  task_id,
3354
- max_retries = 50,
3639
+ max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3355
3640
  timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3356
3641
  request_options = {}
3357
3642
  )
@@ -3366,19 +3651,22 @@ module Algolia
3366
3651
  sleep(timeout.call(retries) / 1000.0)
3367
3652
  end
3368
3653
 
3369
- raise ApiError, "The maximum number of retries exceeded. (#{max_retries})"
3654
+ raise(
3655
+ ApiError,
3656
+ "Stopped waiting for the task after #{max_retries} retries. This does not mean the operation failed; it may still complete. If you need to keep polling, retry with a higher max_retries."
3657
+ )
3370
3658
  end
3371
3659
 
3372
3660
  # Helper: Wait for an application-level task to be published (completed) for a given `task_id`.
3373
3661
  #
3374
3662
  # @param task_id [Integer] the `task_id` returned in the method response. (required)
3375
- # @param max_retries [Integer] the maximum number of retries. (optional, default to 50)
3663
+ # @param max_retries [Integer] the maximum number of retries. (optional, default to Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES)
3376
3664
  # @param timeout [Proc] the function to decide how long to wait between retries. (optional)
3377
3665
  # @param request_options [Hash] the requestOptions to send along with the query, they will be forwarded to the `get_task` method.
3378
3666
  # @return [Http::Response] the last get_task response
3379
3667
  def wait_for_app_task(
3380
3668
  task_id,
3381
- max_retries = 50,
3669
+ max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3382
3670
  timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3383
3671
  request_options = {}
3384
3672
  )
@@ -3393,7 +3681,10 @@ module Algolia
3393
3681
  sleep(timeout.call(retries) / 1000.0)
3394
3682
  end
3395
3683
 
3396
- raise ApiError, "The maximum number of retries exceeded. (#{max_retries})"
3684
+ raise(
3685
+ ApiError,
3686
+ "Stopped waiting for the task after #{max_retries} retries. This does not mean the operation failed; it may still complete. If you need to keep polling, retry with a higher max_retries."
3687
+ )
3397
3688
  end
3398
3689
 
3399
3690
  # Helper: Wait for an API key to be added, updated or deleted based on a given `operation`.
@@ -3409,7 +3700,7 @@ module Algolia
3409
3700
  key,
3410
3701
  operation,
3411
3702
  api_key = Search::ApiKey.new,
3412
- max_retries = 50,
3703
+ max_retries = Algolia::ChunkedHelperOptions::DEFAULT_MAX_RETRIES,
3413
3704
  timeout = -> (retry_count) { [retry_count * 200, 5000].min },
3414
3705
  request_options = {}
3415
3706
  )
@@ -3432,7 +3723,10 @@ module Algolia
3432
3723
  sleep(timeout.call(retries) / 1000.0)
3433
3724
  end
3434
3725
 
3435
- raise ApiError, "The maximum number of retries exceeded. (#{max_retries})"
3726
+ raise(
3727
+ ApiError,
3728
+ "Stopped waiting for the task after #{max_retries} retries. This does not mean the operation failed; it may still complete. If you need to keep polling, retry with a higher max_retries."
3729
+ )
3436
3730
  end
3437
3731
 
3438
3732
  while retries < max_retries
@@ -3451,7 +3745,10 @@ module Algolia
3451
3745
  sleep(timeout.call(retries) / 1000.0)
3452
3746
  end
3453
3747
 
3454
- raise ApiError, "The maximum number of retries exceeded. (#{max_retries})"
3748
+ raise(
3749
+ ApiError,
3750
+ "Stopped waiting for the task after #{max_retries} retries. This does not mean the operation failed; it may still complete. If you need to keep polling, retry with a higher max_retries."
3751
+ )
3455
3752
  end
3456
3753
 
3457
3754
  # Helper: Iterate on the `browse` method of the client to allow aggregating objects of an index.
@@ -3629,14 +3926,22 @@ module Algolia
3629
3926
  #
3630
3927
  # @return [BatchResponse]
3631
3928
  #
3632
- def save_objects(index_name, objects, wait_for_tasks = false, batch_size = 1000, request_options = {})
3929
+ def save_objects(
3930
+ index_name,
3931
+ objects,
3932
+ wait_for_tasks = false,
3933
+ batch_size = 1000,
3934
+ request_options = {},
3935
+ chunked_options = nil
3936
+ )
3633
3937
  chunked_batch(
3634
3938
  index_name,
3635
3939
  objects,
3636
3940
  Search::Action::ADD_OBJECT,
3637
3941
  wait_for_tasks,
3638
3942
  batch_size,
3639
- request_options
3943
+ request_options,
3944
+ chunked_options
3640
3945
  )
3641
3946
  end
3642
3947
 
@@ -3650,14 +3955,22 @@ module Algolia
3650
3955
  #
3651
3956
  # @return [BatchResponse]
3652
3957
  #
3653
- def delete_objects(index_name, object_ids, wait_for_tasks = false, batch_size = 1000, request_options = {})
3958
+ def delete_objects(
3959
+ index_name,
3960
+ object_ids,
3961
+ wait_for_tasks = false,
3962
+ batch_size = 1000,
3963
+ request_options = {},
3964
+ chunked_options = nil
3965
+ )
3654
3966
  chunked_batch(
3655
3967
  index_name,
3656
3968
  object_ids.map { |id| {"objectID" => id} },
3657
3969
  Search::Action::DELETE_OBJECT,
3658
3970
  wait_for_tasks,
3659
3971
  batch_size,
3660
- request_options
3972
+ request_options,
3973
+ chunked_options
3661
3974
  )
3662
3975
  end
3663
3976
 
@@ -3678,7 +3991,8 @@ module Algolia
3678
3991
  create_if_not_exists,
3679
3992
  wait_for_tasks = false,
3680
3993
  batch_size = 1000,
3681
- request_options = {}
3994
+ request_options = {},
3995
+ chunked_options = nil
3682
3996
  )
3683
3997
  chunked_batch(
3684
3998
  index_name,
@@ -3686,7 +4000,8 @@ module Algolia
3686
4000
  create_if_not_exists ? Search::Action::PARTIAL_UPDATE_OBJECT : Search::Action::PARTIAL_UPDATE_OBJECT_NO_CREATE,
3687
4001
  wait_for_tasks,
3688
4002
  batch_size,
3689
- request_options
4003
+ request_options,
4004
+ chunked_options
3690
4005
  )
3691
4006
  end
3692
4007
 
@@ -3707,8 +4022,10 @@ module Algolia
3707
4022
  action = Action::ADD_OBJECT,
3708
4023
  wait_for_tasks = false,
3709
4024
  batch_size = 1000,
3710
- request_options = {}
4025
+ request_options = {},
4026
+ chunked_options = nil
3711
4027
  )
4028
+ opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
3712
4029
  responses = []
3713
4030
  objects.each_slice(batch_size) do |chunk|
3714
4031
  requests = chunk.map do |object|
@@ -3720,7 +4037,7 @@ module Algolia
3720
4037
 
3721
4038
  if wait_for_tasks
3722
4039
  responses.each do |response|
3723
- wait_for_task(index_name, response.task_id)
4040
+ wait_for_task(index_name, response.task_id, opts.max_retries)
3724
4041
  end
3725
4042
  end
3726
4043
 
@@ -3741,8 +4058,10 @@ module Algolia
3741
4058
  objects,
3742
4059
  batch_size = 1000,
3743
4060
  scopes = [Search::ScopeType::SETTINGS, Search::ScopeType::RULES, Search::ScopeType::SYNONYMS],
3744
- request_options = {}
4061
+ request_options = {},
4062
+ chunked_options = nil
3745
4063
  )
4064
+ opts = Algolia::ChunkedHelperOptions.resolve(chunked_options)
3746
4065
  tmp_index_name = index_name + "_tmp_" + rand(10_000_000).to_s
3747
4066
 
3748
4067
  begin
@@ -3762,10 +4081,11 @@ module Algolia
3762
4081
  Search::Action::ADD_OBJECT,
3763
4082
  true,
3764
4083
  batch_size,
3765
- request_options
4084
+ request_options,
4085
+ opts
3766
4086
  )
3767
4087
 
3768
- wait_for_task(tmp_index_name, copy_operation_response.task_id)
4088
+ wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3769
4089
 
3770
4090
  copy_operation_response = operation_index(
3771
4091
  index_name,
@@ -3777,7 +4097,7 @@ module Algolia
3777
4097
  request_options
3778
4098
  )
3779
4099
 
3780
- wait_for_task(tmp_index_name, copy_operation_response.task_id)
4100
+ wait_for_task(tmp_index_name, copy_operation_response.task_id, opts.max_retries)
3781
4101
 
3782
4102
  move_operation_response = operation_index(
3783
4103
  tmp_index_name,
@@ -3788,7 +4108,7 @@ module Algolia
3788
4108
  request_options
3789
4109
  )
3790
4110
 
3791
- wait_for_task(tmp_index_name, move_operation_response.task_id)
4111
+ wait_for_task(tmp_index_name, move_operation_response.task_id, opts.max_retries)
3792
4112
 
3793
4113
  Search::ReplaceAllObjectsResponse.new(
3794
4114
  copy_operation_response: copy_operation_response,
@@ -0,0 +1,15 @@
1
+ module Algolia
2
+ # Optional configuration for chunked helpers that batch records and poll for task completion.
3
+ class ChunkedHelperOptions
4
+ DEFAULT_MAX_RETRIES = 100
5
+ attr_reader :max_retries
6
+
7
+ def initialize(max_retries: DEFAULT_MAX_RETRIES)
8
+ @max_retries = max_retries
9
+ end
10
+
11
+ def self.resolve(options)
12
+ options || new
13
+ end
14
+ end
15
+ end